VirtualBox

source: vbox/trunk/src/VBox/Main/VirtualBoxImpl.cpp@ 30377

Last change on this file since 30377 was 30377, checked in by vboxsync, 15 years ago

Main: do not hold any other lock while calling VirtualBox::saveSettings (mostly comments, only real change is in DHCPServer); also, VirtualBox lock is not needed in SessionMachine::endSavingState

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 152.7 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 30377 2010-06-22 14:01:01Z vboxsync $ */
2
3/** @file
4 * Implementation of IVirtualBox in VBoxSVC.
5 */
6
7/*
8 * Copyright (C) 2006-2010 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.215389.xyz. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include <iprt/asm.h>
20#include <iprt/buildconfig.h>
21#include <iprt/cpp/utils.h>
22#include <iprt/dir.h>
23#include <iprt/env.h>
24#include <iprt/file.h>
25#include <iprt/path.h>
26#include <iprt/process.h>
27#include <iprt/string.h>
28#include <iprt/stream.h>
29#include <iprt/thread.h>
30#include <iprt/uuid.h>
31#include <iprt/cpp/xml.h>
32
33#include <VBox/com/com.h>
34#include <VBox/com/array.h>
35#include "VBox/com/EventQueue.h"
36
37#include <VBox/err.h>
38#include <VBox/param.h>
39#include <VBox/VBoxHDD.h>
40#include <VBox/settings.h>
41#include <VBox/version.h>
42
43#include <package-generated.h>
44
45#include <algorithm>
46#include <set>
47#include <vector>
48#include <memory> // for auto_ptr
49
50#include <typeinfo>
51
52#include "VirtualBoxImpl.h"
53
54#include "Global.h"
55#include "MachineImpl.h"
56#include "MediumImpl.h"
57#include "SharedFolderImpl.h"
58#include "ProgressImpl.h"
59#include "ProgressProxyImpl.h"
60#include "HostImpl.h"
61#include "USBControllerImpl.h"
62#include "SystemPropertiesImpl.h"
63#include "GuestOSTypeImpl.h"
64#include "DHCPServerRunner.h"
65#include "DHCPServerImpl.h"
66#ifdef VBOX_WITH_RESOURCE_USAGE_API
67#include "PerformanceImpl.h"
68#endif /* VBOX_WITH_RESOURCE_USAGE_API */
69#include "EventImpl.h"
70
71#include "AutoCaller.h"
72#include "Logging.h"
73#include "objectslist.h"
74
75#ifdef RT_OS_WINDOWS
76# include "win/svchlp.h"
77# include "win/VBoxComEvents.h"
78#endif
79
80////////////////////////////////////////////////////////////////////////////////
81//
82// Definitions
83//
84////////////////////////////////////////////////////////////////////////////////
85
86#define VBOX_GLOBAL_SETTINGS_FILE "VirtualBox.xml"
87
88////////////////////////////////////////////////////////////////////////////////
89//
90// Global variables
91//
92////////////////////////////////////////////////////////////////////////////////
93
94// static
95Bstr VirtualBox::sVersion;
96
97// static
98ULONG VirtualBox::sRevision;
99
100// static
101Bstr VirtualBox::sPackageType;
102
103////////////////////////////////////////////////////////////////////////////////
104//
105// VirtualBoxCallbackRegistration
106//
107////////////////////////////////////////////////////////////////////////////////
108
109/**
110 * Registered IVirtualBoxCallback, used by VirtualBox::CallbackList and
111 * VirtualBox::Data::llCallbacks.
112 *
113 * In addition to keeping the interface pointer this also keeps track of the
114 * methods that asked to not be called again. The latter is for reducing
115 * unnecessary IPC.
116 */
117class VirtualBoxCallbackRegistration
118{
119public:
120 ComPtr<IVirtualBoxCallback> ptrICb;
121 /** Bitmap of disabled callback methods, that is methods that has return
122 * VBOX_E_DONT_CALL_AGAIN. */
123 uint32_t bmDisabled;
124 /** Callback bit indexes (for bmDisabled). */
125 typedef enum
126 {
127 kOnMachineStateChange = 0,
128 kOnMachineDataChange,
129 kOnExtraDataCanChange,
130 kOnExtraDataChange,
131 kOnMediumRegistered,
132 kOnMachineRegistered,
133 kOnSessionStateChange,
134 kOnSnapshotTaken,
135 kOnSnapshotDeleted,
136 kOnSnapshotChange,
137 kOnGuestPropertyChange
138 } CallbackBit;
139
140 VirtualBoxCallbackRegistration(IVirtualBoxCallback *pIVirtualBoxCallback)
141 : ptrICb(pIVirtualBoxCallback), bmDisabled(0)
142 {
143 /* nothing */
144 }
145
146 ~VirtualBoxCallbackRegistration()
147 {
148 /* nothing */
149 }
150
151 /** Equal operator for std::find. */
152 bool operator==(const VirtualBoxCallbackRegistration &rThat) const
153 {
154 return this->ptrICb == rThat.ptrICb;
155 }
156
157 /**
158 * Checks if the callback is wanted, i.e. if the method hasn't yet returned
159 * VBOX_E_DONT_CALL_AGAIN.
160 * @returns @c true if it is wanted, @c false if not.
161 * @param enmBit The callback, be sure to get this one right!
162 */
163 inline bool isWanted(CallbackBit enmBit) const
164 {
165 return !ASMBitTest(&bmDisabled, enmBit);
166 }
167
168 /**
169 * Called in response to VBOX_E_DONT_CALL_AGAIN.
170 * @param enmBit The callback, be sure to get this one right!
171 */
172 inline void setDontCallAgain(CallbackBit enmBit)
173 {
174 ASMAtomicBitSet(&bmDisabled, enmBit);
175 }
176};
177
178////////////////////////////////////////////////////////////////////////////////
179//
180// CallbackEvent class
181//
182////////////////////////////////////////////////////////////////////////////////
183
184/**
185 * Abstract callback event class to asynchronously call VirtualBox callbacks
186 * on a dedicated event thread. Subclasses reimplement #handleCallback()
187 * to call appropriate IVirtualBoxCallback methods depending on the event
188 * to be dispatched.
189 *
190 * @note The VirtualBox instance passed to the constructor is strongly
191 * referenced, so that the VirtualBox singleton won't be released until the
192 * event gets handled by the event thread.
193 */
194class VirtualBox::CallbackEvent : public Event
195{
196public:
197
198 CallbackEvent(VirtualBox *aVirtualBox, VirtualBoxCallbackRegistration::CallbackBit aWhat)
199 : mVirtualBox(aVirtualBox), mWhat(aWhat)
200 {
201 Assert(aVirtualBox);
202 }
203
204 void *handler();
205
206 virtual HRESULT handleCallback(const ComPtr<IVirtualBoxCallback> &aCallback) = 0;
207#ifdef RT_OS_WINDOWS
208 virtual HRESULT prepareEventDesc(ComEventDesc& aEvDesc) = 0;
209#endif
210
211private:
212
213 /**
214 * Note that this is a weak ref -- the CallbackEvent handler thread
215 * is bound to the lifetime of the VirtualBox instance, so it's safe.
216 */
217 VirtualBox *mVirtualBox;
218
219protected:
220 /** The callback being called, used for handling VBOX_E_DONT_CALL_AGAIN. */
221 VirtualBoxCallbackRegistration::CallbackBit mWhat;
222};
223
224////////////////////////////////////////////////////////////////////////////////
225//
226// VirtualBox private member data definition
227//
228////////////////////////////////////////////////////////////////////////////////
229
230#if defined(RT_OS_WINDOWS)
231 #define UPDATEREQARG NULL
232 #define UPDATEREQTYPE HANDLE
233#elif defined(RT_OS_OS2)
234 #define UPDATEREQARG NIL_RTSEMEVENT
235 #define UPDATEREQTYPE RTSEMEVENT
236#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
237 #define UPDATEREQARG
238 #define UPDATEREQTYPE RTSEMEVENT
239#else
240# error "Port me!"
241#endif
242
243typedef ObjectsList<Machine> MachinesOList;
244typedef ObjectsList<Medium> MediaOList;
245typedef ObjectsList<GuestOSType> GuestOSTypesOList;
246typedef ObjectsList<SharedFolder> SharedFoldersOList;
247typedef ObjectsList<DHCPServer> DHCPServersOList;
248
249typedef std::map<Guid, ComPtr<IProgress> > ProgressMap;
250typedef std::map<Guid, ComObjPtr<Medium> > HardDiskMap;
251
252/**
253 * Main VirtualBox data structure.
254 * @note |const| members are persistent during lifetime so can be accessed
255 * without locking.
256 */
257struct VirtualBox::Data
258{
259 Data()
260 : pMainConfigFile(NULL),
261 lockMachines(LOCKCLASS_LISTOFMACHINES),
262 ollMachines(lockMachines),
263 lockGuestOSTypes(LOCKCLASS_LISTOFOTHEROBJECTS),
264 ollGuestOSTypes(lockGuestOSTypes),
265 lockMedia(LOCKCLASS_LISTOFMEDIA),
266 ollHardDisks(lockMedia),
267 ollDVDImages(lockMedia),
268 ollFloppyImages(lockMedia),
269 lockSharedFolders(LOCKCLASS_LISTOFOTHEROBJECTS),
270 ollSharedFolders(lockSharedFolders),
271 lockDHCPServers(LOCKCLASS_LISTOFOTHEROBJECTS),
272 ollDHCPServers(lockDHCPServers),
273 mtxProgressOperations(LOCKCLASS_PROGRESSLIST),
274 updateReq(UPDATEREQARG),
275 threadClientWatcher(NIL_RTTHREAD),
276 threadAsyncEvent(NIL_RTTHREAD),
277 pAsyncEventQ(NULL)
278 {}
279
280 ~Data()
281 {
282 if (pMainConfigFile)
283 {
284 delete pMainConfigFile;
285 pMainConfigFile = NULL;
286 }
287 };
288
289 // const data members not requiring locking
290 const Utf8Str strHomeDir;
291
292 // VirtualBox main settings file
293 const Utf8Str strSettingsFilePath;
294 settings::MainConfigFile *pMainConfigFile;
295
296 // const objects not requiring locking
297 const ComObjPtr<Host> pHost;
298 const ComObjPtr<SystemProperties> pSystemProperties;
299#ifdef VBOX_WITH_RESOURCE_USAGE_API
300 const ComObjPtr<PerformanceCollector> pPerformanceCollector;
301#endif /* VBOX_WITH_RESOURCE_USAGE_API */
302
303 // Each of the following lists use a particular lock handle that protects the
304 // list as a whole. As opposed to version 3.1 and earlier, these lists no
305 // longer need the main VirtualBox object lock, but only the respective list
306 // lock. In each case, the locking order is defined that the list must be
307 // requested before object locks of members of the lists (see the order definitions
308 // in AutoLock.h; e.g. LOCKCLASS_LISTOFMACHINES before LOCKCLASS_MACHINEOBJECT).
309 RWLockHandle lockMachines;
310 MachinesOList ollMachines;
311
312 RWLockHandle lockGuestOSTypes;
313 GuestOSTypesOList ollGuestOSTypes;
314
315 // All the media lists are protected by the following locking handle:
316 RWLockHandle lockMedia;
317 MediaOList ollHardDisks, // base images only!
318 ollDVDImages,
319 ollFloppyImages;
320 // the hard disks map is an additional map sorted by UUID for quick lookup
321 // and contains ALL hard disks (base and differencing); it is protected by
322 // the same lock as the other media lists above
323 HardDiskMap mapHardDisks;
324
325 // list of pending machine renames (also protected by media tree lock;
326 // see VirtualBox::rememberMachineNameChangeForMedia())
327 struct PendingMachineRename
328 {
329 Utf8Str strConfigDirOld;
330 Utf8Str strConfigDirNew;
331 };
332 typedef std::list<PendingMachineRename> PendingMachineRenamesList;
333 PendingMachineRenamesList llPendingMachineRenames;
334
335 RWLockHandle lockSharedFolders;
336 SharedFoldersOList ollSharedFolders;
337
338 RWLockHandle lockDHCPServers;
339 DHCPServersOList ollDHCPServers;
340
341 RWLockHandle mtxProgressOperations;
342 ProgressMap mapProgressOperations;
343 CallbackList llCallbacks;
344
345 // the following are data for the client watcher thread
346 const UPDATEREQTYPE updateReq;
347 const RTTHREAD threadClientWatcher;
348 typedef std::list<RTPROCESS> ProcessList;
349 ProcessList llProcesses;
350
351 // the following are data for the async event thread
352 const RTTHREAD threadAsyncEvent;
353 EventQueue * const pAsyncEventQ;
354
355#ifdef RT_OS_WINDOWS
356 ComEventsHelper mComEvHelper;
357#endif
358 const ComObjPtr<EventSource> pEventSource;
359};
360
361// constructor / destructor
362/////////////////////////////////////////////////////////////////////////////
363
364VirtualBox::VirtualBox()
365{}
366
367VirtualBox::~VirtualBox()
368{}
369
370HRESULT VirtualBox::FinalConstruct()
371{
372 LogFlowThisFunc(("\n"));
373
374 return init();
375}
376
377void VirtualBox::FinalRelease()
378{
379 LogFlowThisFunc(("\n"));
380
381 uninit();
382}
383
384// public initializer/uninitializer for internal purposes only
385/////////////////////////////////////////////////////////////////////////////
386
387/**
388 * Initializes the VirtualBox object.
389 *
390 * @return COM result code
391 */
392HRESULT VirtualBox::init()
393{
394 /* Enclose the state transition NotReady->InInit->Ready */
395 AutoInitSpan autoInitSpan(this);
396 AssertReturn(autoInitSpan.isOk(), E_FAIL);
397
398 /* Locking this object for writing during init sounds a bit paradoxical,
399 * but in the current locking mess this avoids that some code gets a
400 * read lock and later calls code which wants the same write lock. */
401 AutoWriteLock lock(this COMMA_LOCKVAL_SRC_POS);
402
403 // allocate our instance data
404 m = new Data;
405
406 LogFlow(("===========================================================\n"));
407 LogFlowThisFuncEnter();
408
409 if (sVersion.isEmpty())
410 sVersion = VBOX_VERSION_STRING;
411 sRevision = RTBldCfgRevision();
412 if (sPackageType.isEmpty())
413 sPackageType = VBOX_PACKAGE_STRING;
414 LogFlowThisFunc(("Version: %ls, Package: %ls\n", sVersion.raw(), sPackageType.raw()));
415
416 /* Get the VirtualBox home directory. */
417 {
418 char szHomeDir[RTPATH_MAX];
419 int vrc = com::GetVBoxUserHomeDirectory(szHomeDir, sizeof(szHomeDir));
420 if (RT_FAILURE(vrc))
421 return setError(E_FAIL,
422 tr("Could not create the VirtualBox home directory '%s' (%Rrc)"),
423 szHomeDir, vrc);
424
425 unconst(m->strHomeDir) = szHomeDir;
426 }
427
428 /* compose the VirtualBox.xml file name */
429 unconst(m->strSettingsFilePath) = Utf8StrFmt("%s%c%s",
430 m->strHomeDir.raw(),
431 RTPATH_DELIMITER,
432 VBOX_GLOBAL_SETTINGS_FILE);
433 HRESULT rc = S_OK;
434 bool fCreate = false;
435 try
436 {
437 // load and parse VirtualBox.xml; this will throw on XML or logic errors
438 try
439 {
440 m->pMainConfigFile = new settings::MainConfigFile(&m->strSettingsFilePath);
441 }
442 catch (xml::EIPRTFailure &e)
443 {
444 // this is thrown by the XML backend if the RTOpen() call fails;
445 // only if the main settings file does not exist, create it,
446 // if there's something more serious, then do fail!
447 if (e.rc() == VERR_FILE_NOT_FOUND)
448 fCreate = true;
449 else
450 throw;
451 }
452
453 if (fCreate)
454 m->pMainConfigFile = new settings::MainConfigFile(NULL);
455
456#ifdef VBOX_WITH_RESOURCE_USAGE_API
457 /* create the performance collector object BEFORE host */
458 unconst(m->pPerformanceCollector).createObject();
459 rc = m->pPerformanceCollector->init();
460 ComAssertComRCThrowRC(rc);
461#endif /* VBOX_WITH_RESOURCE_USAGE_API */
462
463 /* create the host object early, machines will need it */
464 unconst(m->pHost).createObject();
465 rc = m->pHost->init(this);
466 ComAssertComRCThrowRC(rc);
467
468 rc = m->pHost->loadSettings(m->pMainConfigFile->host);
469 if (FAILED(rc)) throw rc;
470
471 /* create the system properties object, someone may need it too */
472 unconst(m->pSystemProperties).createObject();
473 rc = m->pSystemProperties->init(this);
474 ComAssertComRCThrowRC(rc);
475
476 rc = m->pSystemProperties->loadSettings(m->pMainConfigFile->systemProperties);
477 if (FAILED(rc)) throw rc;
478
479 /* guest OS type objects, needed by machines */
480 for (size_t i = 0; i < RT_ELEMENTS(Global::sOSTypes); ++ i)
481 {
482 ComObjPtr <GuestOSType> guestOSTypeObj;
483 rc = guestOSTypeObj.createObject();
484 if (SUCCEEDED(rc))
485 {
486 rc = guestOSTypeObj->init(Global::sOSTypes[i].familyId,
487 Global::sOSTypes[i].familyDescription,
488 Global::sOSTypes[i].id,
489 Global::sOSTypes[i].description,
490 Global::sOSTypes[i].osType,
491 Global::sOSTypes[i].osHint,
492 Global::sOSTypes[i].recommendedRAM,
493 Global::sOSTypes[i].recommendedVRAM,
494 Global::sOSTypes[i].recommendedHDD,
495 Global::sOSTypes[i].networkAdapterType,
496 Global::sOSTypes[i].numSerialEnabled,
497 Global::sOSTypes[i].dvdStorageControllerType,
498 Global::sOSTypes[i].dvdStorageBusType,
499 Global::sOSTypes[i].hdStorageControllerType,
500 Global::sOSTypes[i].hdStorageBusType);
501 if (SUCCEEDED(rc))
502 m->ollGuestOSTypes.addChild(guestOSTypeObj);
503 }
504 ComAssertComRCThrowRC(rc);
505 }
506
507 /* all registered media, needed by machines */
508 if (FAILED(rc = initMedia()))
509 throw rc;
510
511 /* machines */
512 if (FAILED(rc = initMachines()))
513 throw rc;
514
515
516#ifdef DEBUG
517 LogFlowThisFunc(("Dumping media backreferences\n"));
518 dumpAllBackRefs();
519#endif
520
521 /* net services */
522 for (settings::DHCPServersList::const_iterator it = m->pMainConfigFile->llDhcpServers.begin();
523 it != m->pMainConfigFile->llDhcpServers.end();
524 ++it)
525 {
526 const settings::DHCPServer &data = *it;
527
528 ComObjPtr<DHCPServer> pDhcpServer;
529 if (SUCCEEDED(rc = pDhcpServer.createObject()))
530 rc = pDhcpServer->init(this, data);
531 if (FAILED(rc)) throw rc;
532
533 rc = registerDHCPServer(pDhcpServer, false /* aSaveRegistry */);
534 if (FAILED(rc)) throw rc;
535 }
536
537 /* events */
538#if 0
539 // disabled for now
540 if (SUCCEEDED(rc = unconst(m->pEventSource).createObject()))
541 rc = m->pEventSource->init(this);
542 if (FAILED(rc)) throw rc;
543#endif
544
545 }
546 catch (HRESULT err)
547 {
548 /* we assume that error info is set by the thrower */
549 rc = err;
550 }
551 catch (...)
552 {
553 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
554 }
555
556 if (SUCCEEDED(rc))
557 {
558 /* start the client watcher thread */
559#if defined(RT_OS_WINDOWS)
560 unconst(m->updateReq) = ::CreateEvent(NULL, FALSE, FALSE, NULL);
561#elif defined(RT_OS_OS2)
562 RTSemEventCreate(&unconst(m->updateReq));
563#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
564 RTSemEventCreate(&unconst(m->updateReq));
565#else
566# error "Port me!"
567#endif
568 int vrc = RTThreadCreate(&unconst(m->threadClientWatcher),
569 ClientWatcher,
570 (void *)this,
571 0,
572 RTTHREADTYPE_MAIN_WORKER,
573 RTTHREADFLAGS_WAITABLE,
574 "Watcher");
575 ComAssertRC(vrc);
576 if (RT_FAILURE(vrc))
577 rc = E_FAIL;
578 }
579
580 if (SUCCEEDED(rc))
581 {
582 try
583 {
584 /* start the async event handler thread */
585 int vrc = RTThreadCreate(&unconst(m->threadAsyncEvent),
586 AsyncEventHandler,
587 &unconst(m->pAsyncEventQ),
588 0,
589 RTTHREADTYPE_MAIN_WORKER,
590 RTTHREADFLAGS_WAITABLE,
591 "EventHandler");
592 ComAssertRCThrow(vrc, E_FAIL);
593
594 /* wait until the thread sets m->pAsyncEventQ */
595 RTThreadUserWait(m->threadAsyncEvent, RT_INDEFINITE_WAIT);
596 ComAssertThrow(m->pAsyncEventQ, E_FAIL);
597 }
598 catch (HRESULT aRC)
599 {
600 rc = aRC;
601 }
602 }
603
604#ifdef RT_OS_WINDOWS
605 if (SUCCEEDED(rc))
606 rc = m->mComEvHelper.init(IID_IVirtualBoxCallback);
607#endif
608 /* Confirm a successful initialization when it's the case */
609 if (SUCCEEDED(rc))
610 autoInitSpan.setSucceeded();
611
612 LogFlowThisFunc(("rc=%08X\n", rc));
613 LogFlowThisFuncLeave();
614 LogFlow(("===========================================================\n"));
615 return rc;
616}
617
618HRESULT VirtualBox::initMachines()
619{
620 for (settings::MachinesRegistry::const_iterator it = m->pMainConfigFile->llMachines.begin();
621 it != m->pMainConfigFile->llMachines.end();
622 ++it)
623 {
624 HRESULT rc = S_OK;
625 const settings::MachineRegistryEntry &xmlMachine = *it;
626 Guid uuid = xmlMachine.uuid;
627
628 ComObjPtr<Machine> pMachine;
629 if (SUCCEEDED(rc = pMachine.createObject()))
630 {
631 rc = pMachine->init(this,
632 xmlMachine.strSettingsFile,
633 &uuid);
634 if (SUCCEEDED(rc))
635 rc = registerMachine(pMachine);
636 if (FAILED(rc))
637 return rc;
638 }
639 }
640
641 return S_OK;
642}
643
644HRESULT VirtualBox::initMedia()
645{
646 AutoWriteLock treeLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
647
648 HRESULT rc = S_OK;
649 settings::MediaList::const_iterator it;
650 for (it = m->pMainConfigFile->llHardDisks.begin();
651 it != m->pMainConfigFile->llHardDisks.end();
652 ++it)
653 {
654 const settings::Medium &xmlHD = *it;
655
656 ComObjPtr<Medium> pHardDisk;
657 if (SUCCEEDED(rc = pHardDisk.createObject()))
658 rc = pHardDisk->init(this,
659 NULL, // parent
660 DeviceType_HardDisk,
661 xmlHD); // XML data; this recurses to processes the children
662 if (FAILED(rc)) return rc;
663
664 rc = registerHardDisk(pHardDisk, NULL /*pfNeedsSaveSettings*/ );
665 if (FAILED(rc)) return rc;
666 }
667
668 for (it = m->pMainConfigFile->llDvdImages.begin();
669 it != m->pMainConfigFile->llDvdImages.end();
670 ++it)
671 {
672 const settings::Medium &xmlDvd = *it;
673
674 ComObjPtr<Medium> pImage;
675 if (SUCCEEDED(pImage.createObject()))
676 rc = pImage->init(this, NULL, DeviceType_DVD, xmlDvd);
677 if (FAILED(rc)) return rc;
678
679 rc = registerImage(pImage, DeviceType_DVD, NULL /*pfNeedsSaveSettings*/ );
680 if (FAILED(rc)) return rc;
681 }
682
683 for (it = m->pMainConfigFile->llFloppyImages.begin();
684 it != m->pMainConfigFile->llFloppyImages.end();
685 ++it)
686 {
687 const settings::Medium &xmlFloppy = *it;
688
689 ComObjPtr<Medium> pImage;
690 if (SUCCEEDED(pImage.createObject()))
691 rc = pImage->init(this, NULL, DeviceType_Floppy, xmlFloppy);
692 if (FAILED(rc)) return rc;
693
694 rc = registerImage(pImage, DeviceType_Floppy, NULL /*pfNeedsSaveSettings*/ );
695 if (FAILED(rc)) return rc;
696 }
697
698 return S_OK;
699}
700
701void VirtualBox::uninit()
702{
703 /* Enclose the state transition Ready->InUninit->NotReady */
704 AutoUninitSpan autoUninitSpan(this);
705 if (autoUninitSpan.uninitDone())
706 return;
707
708 LogFlow(("===========================================================\n"));
709 LogFlowThisFuncEnter();
710 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
711
712 /* tell all our child objects we've been uninitialized */
713
714 LogFlowThisFunc(("Uninitializing machines (%d)...\n", m->ollMachines.size()));
715 if (m->pHost)
716 {
717 /* It is necessary to hold the VirtualBox and Host locks here because
718 we may have to uninitialize SessionMachines. */
719 AutoMultiWriteLock2 multilock(this, m->pHost COMMA_LOCKVAL_SRC_POS);
720 m->ollMachines.uninitAll();
721 }
722 else
723 m->ollMachines.uninitAll();
724 m->ollFloppyImages.uninitAll();
725 m->ollDVDImages.uninitAll();
726 m->ollHardDisks.uninitAll();
727 m->ollDHCPServers.uninitAll();
728
729 m->mapProgressOperations.clear();
730
731 m->ollGuestOSTypes.uninitAll();
732
733 /* Note that we release singleton children after we've all other children.
734 * In some cases this is important because these other children may use
735 * some resources of the singletons which would prevent them from
736 * uninitializing (as for example, mSystemProperties which owns
737 * MediumFormat objects which Medium objects refer to) */
738 if (m->pSystemProperties)
739 {
740 m->pSystemProperties->uninit();
741 unconst(m->pSystemProperties).setNull();
742 }
743
744 if (m->pHost)
745 {
746 m->pHost->uninit();
747 unconst(m->pHost).setNull();
748 }
749
750 LogFlowThisFunc(("Releasing event source...\n"));
751 if (m->pEventSource)
752 {
753 m->pEventSource->uninit();
754 unconst(m->pEventSource).setNull();
755 }
756
757#ifdef VBOX_WITH_RESOURCE_USAGE_API
758 if (m->pPerformanceCollector)
759 {
760 m->pPerformanceCollector->uninit();
761 unconst(m->pPerformanceCollector).setNull();
762 }
763#endif /* VBOX_WITH_RESOURCE_USAGE_API */
764
765 LogFlowThisFunc(("Releasing callbacks...\n"));
766 if (m->llCallbacks.size())
767 {
768 /* release all callbacks */
769 LogWarningFunc(("%d unregistered callbacks!\n",
770 m->llCallbacks.size()));
771 m->llCallbacks.clear();
772 }
773
774 LogFlowThisFunc(("Terminating the async event handler...\n"));
775 if (m->threadAsyncEvent != NIL_RTTHREAD)
776 {
777 /* signal to exit the event loop */
778 if (m->pAsyncEventQ->postEvent(NULL))
779 {
780 /*
781 * Wait for thread termination (only if we've successfully posted
782 * a NULL event!)
783 */
784 int vrc = RTThreadWait(m->threadAsyncEvent, 60000, NULL);
785 if (RT_FAILURE(vrc))
786 LogWarningFunc(("RTThreadWait(%RTthrd) -> %Rrc\n",
787 m->threadAsyncEvent, vrc));
788 }
789 else
790 {
791 AssertMsgFailed(("postEvent(NULL) failed\n"));
792 RTThreadWait(m->threadAsyncEvent, 0, NULL);
793 }
794
795 unconst(m->threadAsyncEvent) = NIL_RTTHREAD;
796 unconst(m->pAsyncEventQ) = NULL;
797 }
798
799 LogFlowThisFunc(("Terminating the client watcher...\n"));
800 if (m->threadClientWatcher != NIL_RTTHREAD)
801 {
802 /* signal the client watcher thread */
803 updateClientWatcher();
804 /* wait for the termination */
805 RTThreadWait(m->threadClientWatcher, RT_INDEFINITE_WAIT, NULL);
806 unconst(m->threadClientWatcher) = NIL_RTTHREAD;
807 }
808 m->llProcesses.clear();
809#if defined(RT_OS_WINDOWS)
810 if (m->updateReq != NULL)
811 {
812 ::CloseHandle(m->updateReq);
813 unconst(m->updateReq) = NULL;
814 }
815#elif defined(RT_OS_OS2)
816 if (m->updateReq != NIL_RTSEMEVENT)
817 {
818 RTSemEventDestroy(m->updateReq);
819 unconst(m->updateReq) = NIL_RTSEMEVENT;
820 }
821#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
822 if (m->updateReq != NIL_RTSEMEVENT)
823 {
824 RTSemEventDestroy(m->updateReq);
825 unconst(m->updateReq) = NIL_RTSEMEVENT;
826 }
827#else
828# error "Port me!"
829#endif
830
831 // clean up our instance data
832 delete m;
833
834 /* Unload hard disk plugin backends. */
835 VDShutdown();
836
837 LogFlowThisFuncLeave();
838 LogFlow(("===========================================================\n"));
839}
840
841// IVirtualBox properties
842/////////////////////////////////////////////////////////////////////////////
843
844STDMETHODIMP VirtualBox::COMGETTER(Version)(BSTR *aVersion)
845{
846 CheckComArgNotNull(aVersion);
847
848 AutoCaller autoCaller(this);
849 if (FAILED(autoCaller.rc())) return autoCaller.rc();
850
851 sVersion.cloneTo(aVersion);
852 return S_OK;
853}
854
855STDMETHODIMP VirtualBox::COMGETTER(Revision)(ULONG *aRevision)
856{
857 CheckComArgNotNull(aRevision);
858
859 AutoCaller autoCaller(this);
860 if (FAILED(autoCaller.rc())) return autoCaller.rc();
861
862 *aRevision = sRevision;
863 return S_OK;
864}
865
866STDMETHODIMP VirtualBox::COMGETTER(PackageType)(BSTR *aPackageType)
867{
868 CheckComArgNotNull(aPackageType);
869
870 AutoCaller autoCaller(this);
871 if (FAILED(autoCaller.rc())) return autoCaller.rc();
872
873 sPackageType.cloneTo(aPackageType);
874 return S_OK;
875}
876
877STDMETHODIMP VirtualBox::COMGETTER(HomeFolder)(BSTR *aHomeFolder)
878{
879 CheckComArgNotNull(aHomeFolder);
880
881 AutoCaller autoCaller(this);
882 if (FAILED(autoCaller.rc())) return autoCaller.rc();
883
884 /* mHomeDir is const and doesn't need a lock */
885 m->strHomeDir.cloneTo(aHomeFolder);
886 return S_OK;
887}
888
889STDMETHODIMP VirtualBox::COMGETTER(SettingsFilePath)(BSTR *aSettingsFilePath)
890{
891 CheckComArgNotNull(aSettingsFilePath);
892
893 AutoCaller autoCaller(this);
894 if (FAILED(autoCaller.rc())) return autoCaller.rc();
895
896 /* mCfgFile.mName is const and doesn't need a lock */
897 m->strSettingsFilePath.cloneTo(aSettingsFilePath);
898 return S_OK;
899}
900
901STDMETHODIMP VirtualBox::COMGETTER(Host)(IHost **aHost)
902{
903 CheckComArgOutSafeArrayPointerValid(aHost);
904
905 AutoCaller autoCaller(this);
906 if (FAILED(autoCaller.rc())) return autoCaller.rc();
907
908 /* mHost is const, no need to lock */
909 m->pHost.queryInterfaceTo(aHost);
910 return S_OK;
911}
912
913STDMETHODIMP
914VirtualBox::COMGETTER(SystemProperties)(ISystemProperties **aSystemProperties)
915{
916 CheckComArgOutSafeArrayPointerValid(aSystemProperties);
917
918 AutoCaller autoCaller(this);
919 if (FAILED(autoCaller.rc())) return autoCaller.rc();
920
921 /* mSystemProperties is const, no need to lock */
922 m->pSystemProperties.queryInterfaceTo(aSystemProperties);
923 return S_OK;
924}
925
926STDMETHODIMP
927VirtualBox::COMGETTER(Machines)(ComSafeArrayOut(IMachine *, aMachines))
928{
929 if (ComSafeArrayOutIsNull(aMachines))
930 return E_POINTER;
931
932 AutoCaller autoCaller(this);
933 if (FAILED(autoCaller.rc())) return autoCaller.rc();
934
935 AutoReadLock al(m->ollMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
936 SafeIfaceArray<IMachine> machines(m->ollMachines.getList());
937 machines.detachTo(ComSafeArrayOutArg(aMachines));
938
939 return S_OK;
940}
941
942STDMETHODIMP VirtualBox::COMGETTER(HardDisks)(ComSafeArrayOut(IMedium *, aHardDisks))
943{
944 if (ComSafeArrayOutIsNull(aHardDisks))
945 return E_POINTER;
946
947 AutoCaller autoCaller(this);
948 if (FAILED(autoCaller.rc())) return autoCaller.rc();
949
950 AutoReadLock al(m->ollHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
951 SafeIfaceArray<IMedium> hardDisks(m->ollHardDisks.getList());
952 hardDisks.detachTo(ComSafeArrayOutArg(aHardDisks));
953
954 return S_OK;
955}
956
957STDMETHODIMP VirtualBox::COMGETTER(DVDImages)(ComSafeArrayOut(IMedium *, aDVDImages))
958{
959 if (ComSafeArrayOutIsNull(aDVDImages))
960 return E_POINTER;
961
962 AutoCaller autoCaller(this);
963 if (FAILED(autoCaller.rc())) return autoCaller.rc();
964
965 AutoReadLock al(m->ollDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
966 SafeIfaceArray<IMedium> images(m->ollDVDImages.getList());
967 images.detachTo(ComSafeArrayOutArg(aDVDImages));
968
969 return S_OK;
970}
971
972STDMETHODIMP VirtualBox::COMGETTER(FloppyImages)(ComSafeArrayOut(IMedium *, aFloppyImages))
973{
974 if (ComSafeArrayOutIsNull(aFloppyImages))
975 return E_POINTER;
976
977 AutoCaller autoCaller(this);
978 if (FAILED(autoCaller.rc())) return autoCaller.rc();
979
980 AutoReadLock al(m->ollFloppyImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
981 SafeIfaceArray<IMedium> images(m->ollFloppyImages.getList());
982 images.detachTo(ComSafeArrayOutArg(aFloppyImages));
983
984 return S_OK;
985}
986
987STDMETHODIMP VirtualBox::COMGETTER(ProgressOperations)(ComSafeArrayOut(IProgress *, aOperations))
988{
989 CheckComArgOutSafeArrayPointerValid(aOperations);
990
991 AutoCaller autoCaller(this);
992 if (FAILED(autoCaller.rc())) return autoCaller.rc();
993
994 /* protect mProgressOperations */
995 AutoReadLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
996 SafeIfaceArray<IProgress> progress(m->mapProgressOperations);
997 progress.detachTo(ComSafeArrayOutArg(aOperations));
998
999 return S_OK;
1000}
1001
1002STDMETHODIMP VirtualBox::COMGETTER(GuestOSTypes)(ComSafeArrayOut(IGuestOSType *, aGuestOSTypes))
1003{
1004 CheckComArgOutSafeArrayPointerValid(aGuestOSTypes);
1005
1006 AutoCaller autoCaller(this);
1007 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1008
1009 AutoReadLock al(m->ollGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1010 SafeIfaceArray<IGuestOSType> ostypes(m->ollGuestOSTypes.getList());
1011 ostypes.detachTo(ComSafeArrayOutArg(aGuestOSTypes));
1012
1013 return S_OK;
1014}
1015
1016STDMETHODIMP VirtualBox::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
1017{
1018#ifndef RT_OS_WINDOWS
1019 NOREF(aSharedFoldersSize);
1020#endif /* RT_OS_WINDOWS */
1021
1022 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
1023
1024 AutoCaller autoCaller(this);
1025 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1026
1027 return setError(E_NOTIMPL, "Not yet implemented");
1028}
1029
1030STDMETHODIMP
1031VirtualBox::COMGETTER(PerformanceCollector)(IPerformanceCollector **aPerformanceCollector)
1032{
1033#ifdef VBOX_WITH_RESOURCE_USAGE_API
1034 CheckComArgOutSafeArrayPointerValid(aPerformanceCollector);
1035
1036 AutoCaller autoCaller(this);
1037 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1038
1039 /* mPerformanceCollector is const, no need to lock */
1040 m->pPerformanceCollector.queryInterfaceTo(aPerformanceCollector);
1041
1042 return S_OK;
1043#else /* !VBOX_WITH_RESOURCE_USAGE_API */
1044 ReturnComNotImplemented();
1045#endif /* !VBOX_WITH_RESOURCE_USAGE_API */
1046}
1047
1048STDMETHODIMP
1049VirtualBox::COMGETTER(DHCPServers)(ComSafeArrayOut(IDHCPServer *, aDHCPServers))
1050{
1051 if (ComSafeArrayOutIsNull(aDHCPServers))
1052 return E_POINTER;
1053
1054 AutoCaller autoCaller(this);
1055 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1056
1057 AutoReadLock al(m->ollDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1058 SafeIfaceArray<IDHCPServer> svrs(m->ollDHCPServers.getList());
1059 svrs.detachTo(ComSafeArrayOutArg(aDHCPServers));
1060
1061 return S_OK;
1062}
1063
1064STDMETHODIMP
1065VirtualBox::COMGETTER(EventSource)(IEventSource ** aEventSource)
1066{
1067 CheckComArgOutPointerValid(aEventSource);
1068
1069 AutoCaller autoCaller(this);
1070 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1071
1072 /* event source is const, no need to lock */
1073 m->pEventSource.queryInterfaceTo(aEventSource);
1074
1075 return S_OK;
1076}
1077
1078STDMETHODIMP
1079VirtualBox::CheckFirmwarePresent(FirmwareType_T aFirmwareType,
1080 IN_BSTR aVersion,
1081 BSTR *aUrl,
1082 BSTR *aFile,
1083 BOOL *aResult)
1084{
1085 CheckComArgNotNull(aResult);
1086
1087 AutoCaller autoCaller(this);
1088 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1089
1090 const char * url = NULL;
1091
1092 NOREF(aVersion);
1093
1094 static const struct {
1095 FirmwareType_T type;
1096 const char* fileName;
1097 const char* url;
1098 } firmwareDesc[] = {
1099 {
1100 /* compiled-in firmware */
1101 FirmwareType_BIOS, NULL, NULL
1102 },
1103 {
1104 FirmwareType_EFI32, "VBoxEFI32.fd", "http://virtualbox.org/firmware/VBoxEFI32.fd"
1105 },
1106 {
1107 FirmwareType_EFI64, "VBoxEFI64.fd", "http://virtualbox.org/firmware/VBoxEFI64.fd"
1108 },
1109 {
1110 FirmwareType_EFIDUAL, "VBoxEFIDual.fd", "http://virtualbox.org/firmware/VBoxEFIDual.fd"
1111 }
1112 };
1113
1114 for (size_t i = 0; i < sizeof(firmwareDesc) / sizeof(firmwareDesc[0]); i++)
1115 {
1116 if (aFirmwareType != firmwareDesc[i].type)
1117 continue;
1118
1119 /* compiled-in firmware */
1120 if (firmwareDesc[i].fileName == NULL)
1121 {
1122 *aResult = TRUE;
1123 break;
1124 }
1125
1126 Utf8Str shortName, fullName;
1127 int rc;
1128
1129 shortName = Utf8StrFmt("Firmware%c%s",
1130 RTPATH_DELIMITER,
1131 firmwareDesc[i].fileName);
1132 rc = calculateFullPath(shortName, fullName); AssertRCReturn(rc, rc);
1133 if (RTFileExists(fullName.raw()))
1134 {
1135 *aResult = TRUE;
1136 if (aFile)
1137 Utf8Str(fullName).cloneTo(aFile);
1138 break;
1139 }
1140
1141 char pszVBoxPath[RTPATH_MAX];
1142 rc = RTPathExecDir(pszVBoxPath, RTPATH_MAX); AssertRCReturn(rc, rc);
1143 fullName = Utf8StrFmt("%s%c%s",
1144 pszVBoxPath,
1145 RTPATH_DELIMITER,
1146 firmwareDesc[i].fileName);
1147 if (RTFileExists(fullName.raw()))
1148 {
1149 *aResult = TRUE;
1150 if (aFile)
1151 Utf8Str(fullName).cloneTo(aFile);
1152 break;
1153 }
1154
1155
1156 url = firmwareDesc[i].url;
1157 /** @todo: account for version in the URL */
1158 if (aUrl != NULL)
1159 Utf8Str(firmwareDesc[i].url).cloneTo(aUrl);
1160 *aResult = FALSE;
1161
1162 /* Assume single record per firmware type */
1163 break;
1164 }
1165
1166 return S_OK;
1167}
1168// IVirtualBox methods
1169/////////////////////////////////////////////////////////////////////////////
1170
1171/** @note Locks mSystemProperties object for reading. */
1172STDMETHODIMP VirtualBox::CreateMachine(IN_BSTR aName,
1173 IN_BSTR aOsTypeId,
1174 IN_BSTR aBaseFolder,
1175 IN_BSTR aId,
1176 BOOL aOverride,
1177 IMachine **aMachine)
1178{
1179 LogFlowThisFuncEnter();
1180 LogFlowThisFunc(("aName=\"%ls\",aOsTypeId =\"%ls\",aBaseFolder=\"%ls\"\n", aName, aOsTypeId, aBaseFolder));
1181
1182 CheckComArgStrNotEmptyOrNull(aName);
1183 /** @todo tighten checks on aId? */
1184 CheckComArgOutPointerValid(aMachine);
1185
1186 AutoCaller autoCaller(this);
1187 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1188
1189 /* Compose the settings file name using the following scheme:
1190 *
1191 * <base_folder>/<machine_name>/<machine_name>.xml
1192 *
1193 * If a non-null and non-empty base folder is specified, the default
1194 * machine folder will be used as a base folder.
1195 */
1196 Utf8Str strSettingsFile = aBaseFolder;
1197 if (strSettingsFile.isEmpty())
1198 /* we use the non-full folder value below to keep the path relative */
1199 strSettingsFile = getDefaultMachineFolder();
1200
1201 strSettingsFile = Utf8StrFmt("%s%c%ls%c%ls.xml",
1202 strSettingsFile.raw(),
1203 RTPATH_DELIMITER,
1204 aName,
1205 RTPATH_DELIMITER,
1206 aName);
1207
1208 HRESULT rc = E_FAIL;
1209
1210 /* create a new object */
1211 ComObjPtr<Machine> machine;
1212 rc = machine.createObject();
1213 if (FAILED(rc)) return rc;
1214
1215 /* Create UUID if an empty one was specified. */
1216 Guid id(aId);
1217 if (id.isEmpty())
1218 id.create();
1219
1220 GuestOSType *osType = NULL;
1221 rc = findGuestOSType(Bstr(aOsTypeId), osType);
1222 if (FAILED(rc)) return rc;
1223
1224 /* initialize the machine object */
1225 rc = machine->init(this,
1226 strSettingsFile,
1227 Utf8Str(aName),
1228 id,
1229 osType,
1230 aOverride,
1231 TRUE /* aNameSync */);
1232 if (SUCCEEDED(rc))
1233 {
1234 /* set the return value */
1235 rc = machine.queryInterfaceTo(aMachine);
1236 AssertComRC(rc);
1237 }
1238
1239 LogFlowThisFuncLeave();
1240
1241 return rc;
1242}
1243
1244STDMETHODIMP VirtualBox::CreateLegacyMachine(IN_BSTR aName,
1245 IN_BSTR aOsTypeId,
1246 IN_BSTR aSettingsFile,
1247 IN_BSTR aId,
1248 IMachine **aMachine)
1249{
1250 CheckComArgStrNotEmptyOrNull(aName);
1251 CheckComArgStrNotEmptyOrNull(aSettingsFile);
1252 /** @todo tighten checks on aId? */
1253 CheckComArgOutPointerValid(aMachine);
1254
1255 AutoCaller autoCaller(this);
1256 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1257
1258 HRESULT rc = E_FAIL;
1259
1260 Utf8Str settingsFile = aSettingsFile;
1261 /* append the default extension if none */
1262 if (!RTPathHaveExt(settingsFile.c_str()))
1263 settingsFile = Utf8StrFmt("%s.xml", settingsFile.raw());
1264
1265 /* create a new object */
1266 ComObjPtr<Machine> machine;
1267 rc = machine.createObject();
1268 if (FAILED(rc)) return rc;
1269
1270 /* Create UUID if an empty one was specified. */
1271 Guid id(aId);
1272 if (id.isEmpty())
1273 id.create();
1274
1275 GuestOSType *osType = NULL;
1276 rc = findGuestOSType(Bstr(aOsTypeId), osType);
1277 if (FAILED(rc)) return rc;
1278
1279 /* initialize the machine object */
1280 rc = machine->init(this,
1281 settingsFile,
1282 Utf8Str(aName),
1283 id,
1284 osType,
1285 FALSE /* aOverride */,
1286 FALSE /* aNameSync */);
1287 if (SUCCEEDED(rc))
1288 {
1289 /* set the return value */
1290 rc = machine.queryInterfaceTo(aMachine);
1291 AssertComRC(rc);
1292 }
1293
1294 return rc;
1295}
1296
1297STDMETHODIMP VirtualBox::OpenMachine(IN_BSTR aSettingsFile,
1298 IMachine **aMachine)
1299{
1300 CheckComArgStrNotEmptyOrNull(aSettingsFile);
1301 CheckComArgOutSafeArrayPointerValid(aMachine);
1302
1303 AutoCaller autoCaller(this);
1304 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1305
1306 HRESULT rc = E_FAIL;
1307
1308 /* create a new object */
1309 ComObjPtr<Machine> machine;
1310 rc = machine.createObject();
1311 if (SUCCEEDED(rc))
1312 {
1313 /* initialize the machine object */
1314 rc = machine->init(this,
1315 aSettingsFile,
1316 NULL); /* const Guid *aId */
1317 if (SUCCEEDED(rc))
1318 {
1319 /* set the return value */
1320 rc = machine.queryInterfaceTo(aMachine);
1321 ComAssertComRC(rc);
1322 }
1323 }
1324
1325 return rc;
1326}
1327
1328/** @note Locks objects! */
1329STDMETHODIMP VirtualBox::RegisterMachine(IMachine *aMachine)
1330{
1331 CheckComArgNotNull(aMachine);
1332
1333 AutoCaller autoCaller(this);
1334 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1335
1336 HRESULT rc;
1337
1338 Bstr name;
1339 rc = aMachine->COMGETTER(Name)(name.asOutParam());
1340 if (FAILED(rc)) return rc;
1341
1342 /* We can safely cast child to Machine * here because only Machine
1343 * implementations of IMachine can be among our children. */
1344 Machine *pMachine = static_cast<Machine*>(aMachine);
1345
1346 AutoCaller machCaller(pMachine);
1347 ComAssertComRCRetRC(machCaller.rc());
1348
1349 rc = registerMachine(pMachine);
1350 /* fire an event */
1351 if (SUCCEEDED(rc))
1352 onMachineRegistered(pMachine->getId(), TRUE);
1353
1354 return rc;
1355}
1356
1357/** @note Locks objects! */
1358STDMETHODIMP VirtualBox::GetMachine(IN_BSTR aId, IMachine **aMachine)
1359{
1360 CheckComArgOutSafeArrayPointerValid(aMachine);
1361
1362 AutoCaller autoCaller(this);
1363 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1364
1365 ComObjPtr<Machine> machine;
1366 HRESULT rc = findMachine(Guid(aId), true /* setError */, &machine);
1367
1368 /* the below will set *aMachine to NULL if machine is null */
1369 machine.queryInterfaceTo(aMachine);
1370
1371 return rc;
1372}
1373
1374/** @note Locks this object for reading, then some machine objects for reading. */
1375STDMETHODIMP VirtualBox::FindMachine(IN_BSTR aName, IMachine **aMachine)
1376{
1377 LogFlowThisFuncEnter();
1378 LogFlowThisFunc(("aName=\"%ls\", aMachine={%p}\n", aName, aMachine));
1379
1380 CheckComArgStrNotEmptyOrNull(aName);
1381 CheckComArgOutSafeArrayPointerValid(aMachine);
1382
1383 AutoCaller autoCaller(this);
1384 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1385
1386 /* start with not found */
1387 ComObjPtr<Machine> pMachineFound;
1388
1389 AutoReadLock al(m->ollMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1390 for (MachinesOList::iterator it = m->ollMachines.begin();
1391 it != m->ollMachines.end();
1392 ++it)
1393 {
1394 ComObjPtr<Machine> &pMachine2 = *it;
1395 AutoCaller machCaller(pMachine2);
1396 /* skip inaccessible machines */
1397 if (FAILED(machCaller.rc()))
1398 continue;
1399
1400 AutoReadLock machLock(pMachine2 COMMA_LOCKVAL_SRC_POS);
1401 if (pMachine2->getName() == aName)
1402 {
1403 pMachineFound = pMachine2;
1404 break;
1405 }
1406 }
1407
1408 /* this will set (*machine) to NULL if machineObj is null */
1409 pMachineFound.queryInterfaceTo(aMachine);
1410
1411 HRESULT rc = pMachineFound
1412 ? S_OK
1413 : setError(VBOX_E_OBJECT_NOT_FOUND,
1414 tr("Could not find a registered machine named '%ls'"), aName);
1415
1416 LogFlowThisFunc(("aName=\"%ls\", aMachine=%p, rc=%08X\n", aName, *aMachine, rc));
1417 LogFlowThisFuncLeave();
1418
1419 return rc;
1420}
1421
1422/** @note Locks objects! */
1423STDMETHODIMP VirtualBox::UnregisterMachine(IN_BSTR aId,
1424 IMachine **aMachine)
1425{
1426 Guid id(aId);
1427 if (id.isEmpty())
1428 return E_INVALIDARG;
1429
1430 AutoCaller autoCaller(this);
1431 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1432
1433 // find machine from the given ID
1434 ComObjPtr<Machine> pMachine;
1435 HRESULT rc = findMachine(id, true /* setError */, &pMachine);
1436 if (FAILED(rc)) return rc;
1437
1438 // trySetRegistered needs VirtualBox object write lock
1439 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1440 rc = pMachine->trySetRegistered(FALSE);
1441 if (FAILED(rc)) return rc;
1442
1443 /* remove from the collection of registered machines */
1444 m->ollMachines.removeChild(pMachine);
1445
1446 /* save the global registry */
1447 rc = saveSettings();
1448
1449 /* return the unregistered machine to the caller */
1450 pMachine.queryInterfaceTo(aMachine);
1451
1452 /* fire an event */
1453 onMachineRegistered(id, FALSE);
1454
1455 return rc;
1456}
1457
1458STDMETHODIMP VirtualBox::CreateHardDisk(IN_BSTR aFormat,
1459 IN_BSTR aLocation,
1460 IMedium **aHardDisk)
1461{
1462 CheckComArgOutPointerValid(aHardDisk);
1463
1464 AutoCaller autoCaller(this);
1465 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1466
1467 /* we don't access non-const data members so no need to lock */
1468
1469 Bstr format = aFormat;
1470 if (format.isEmpty())
1471 format = getDefaultHardDiskFormat();
1472
1473 HRESULT rc = E_FAIL;
1474
1475 bool fNeedsSaveSettings = false;
1476
1477 ComObjPtr<Medium> hardDisk;
1478 hardDisk.createObject();
1479 rc = hardDisk->init(this, format.raw(), aLocation, &fNeedsSaveSettings);
1480
1481 if (SUCCEEDED(rc))
1482 hardDisk.queryInterfaceTo(aHardDisk);
1483
1484 if (fNeedsSaveSettings)
1485 {
1486 AutoWriteLock vboxlock(this COMMA_LOCKVAL_SRC_POS);
1487 saveSettings();
1488 }
1489
1490 return rc;
1491}
1492
1493STDMETHODIMP VirtualBox::OpenHardDisk(IN_BSTR aLocation,
1494 AccessMode_T accessMode,
1495 BOOL aSetImageId,
1496 IN_BSTR aImageId,
1497 BOOL aSetParentId,
1498 IN_BSTR aParentId,
1499 IMedium **aHardDisk)
1500{
1501 CheckComArgStrNotEmptyOrNull(aLocation);
1502 CheckComArgOutSafeArrayPointerValid(aHardDisk);
1503
1504 AutoCaller autoCaller(this);
1505 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1506
1507 /* we don't access non-const data members so no need to lock */
1508
1509 HRESULT rc = E_FAIL;
1510
1511 ComObjPtr<Medium> hardDisk;
1512 hardDisk.createObject();
1513 Guid imageId, parentId;
1514 if (aSetImageId)
1515 {
1516 imageId = Guid(aImageId);
1517 if (imageId.isEmpty())
1518 return setError(E_INVALIDARG, tr("Argument %s is empty"), "aImageId");
1519 }
1520 if (aSetParentId)
1521 parentId = Guid(aParentId);
1522 rc = hardDisk->init(this,
1523 aLocation,
1524 (accessMode == AccessMode_ReadWrite) ? Medium::OpenReadWrite : Medium::OpenReadOnly,
1525 DeviceType_HardDisk,
1526 aSetImageId,
1527 imageId,
1528 aSetParentId,
1529 parentId);
1530
1531 if (SUCCEEDED(rc))
1532 {
1533 bool fNeedsSaveSettings = false;
1534 {
1535 AutoWriteLock treeLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1536 rc = registerHardDisk(hardDisk, &fNeedsSaveSettings);
1537 }
1538
1539 /* Note that it's important to call uninit() on failure to register
1540 * because the differencing hard disk would have been already associated
1541 * with the parent and this association needs to be broken. */
1542
1543 if (SUCCEEDED(rc))
1544 hardDisk.queryInterfaceTo(aHardDisk);
1545 else
1546 hardDisk->uninit();
1547
1548 if (fNeedsSaveSettings)
1549 {
1550 AutoWriteLock vboxlock(this COMMA_LOCKVAL_SRC_POS);
1551 saveSettings();
1552 }
1553 }
1554
1555 return rc;
1556}
1557
1558STDMETHODIMP VirtualBox::GetHardDisk(IN_BSTR aId,
1559 IMedium **aHardDisk)
1560{
1561 CheckComArgOutSafeArrayPointerValid(aHardDisk);
1562
1563 AutoCaller autoCaller(this);
1564 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1565
1566 Guid id(aId);
1567 ComObjPtr<Medium> hardDisk;
1568 HRESULT rc = findHardDisk(&id, NULL, true /* setError */, &hardDisk);
1569
1570 /* the below will set *aHardDisk to NULL if hardDisk is null */
1571 hardDisk.queryInterfaceTo(aHardDisk);
1572
1573 return rc;
1574}
1575
1576STDMETHODIMP VirtualBox::FindHardDisk(IN_BSTR aLocation,
1577 IMedium **aHardDisk)
1578{
1579 CheckComArgStrNotEmptyOrNull(aLocation);
1580 CheckComArgOutSafeArrayPointerValid(aHardDisk);
1581
1582 AutoCaller autoCaller(this);
1583 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1584
1585 ComObjPtr<Medium> hardDisk;
1586 HRESULT rc = findHardDisk(NULL, aLocation, true /* setError */, &hardDisk);
1587
1588 /* the below will set *aHardDisk to NULL if hardDisk is null */
1589 hardDisk.queryInterfaceTo(aHardDisk);
1590
1591 return rc;
1592}
1593
1594/** @note Doesn't lock anything. */
1595STDMETHODIMP VirtualBox::OpenDVDImage(IN_BSTR aLocation, IN_BSTR aId,
1596 IMedium **aDVDImage)
1597{
1598 CheckComArgStrNotEmptyOrNull(aLocation);
1599 CheckComArgOutSafeArrayPointerValid(aDVDImage);
1600
1601 AutoCaller autoCaller(this);
1602 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1603
1604 HRESULT rc = VBOX_E_FILE_ERROR;
1605
1606 Guid id(aId);
1607 /* generate an UUID if not specified */
1608 if (id.isEmpty())
1609 id.create();
1610
1611 ComObjPtr<Medium> image;
1612 image.createObject();
1613 rc = image->init(this, aLocation, Medium::OpenReadOnly, DeviceType_DVD, true, id, false, Guid());
1614 if (SUCCEEDED(rc))
1615 {
1616 AutoWriteLock treeLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1617 bool fNeedsSaveSettings = false;
1618 rc = registerImage(image, DeviceType_DVD, &fNeedsSaveSettings);
1619 treeLock.release();
1620
1621 if (SUCCEEDED(rc))
1622 image.queryInterfaceTo(aDVDImage);
1623
1624 if (fNeedsSaveSettings)
1625 {
1626 AutoWriteLock vboxlock(this COMMA_LOCKVAL_SRC_POS);
1627 saveSettings();
1628 }
1629 }
1630
1631 return rc;
1632}
1633
1634/** @note Locks objects! */
1635STDMETHODIMP VirtualBox::GetDVDImage(IN_BSTR aId, IMedium **aDVDImage)
1636{
1637 CheckComArgOutSafeArrayPointerValid(aDVDImage);
1638
1639 AutoCaller autoCaller(this);
1640 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1641
1642 Guid id(aId);
1643 ComObjPtr<Medium> image;
1644 HRESULT rc = findDVDImage(&id, NULL, true /* setError */, &image);
1645
1646 /* the below will set *aDVDImage to NULL if image is null */
1647 image.queryInterfaceTo(aDVDImage);
1648
1649 return rc;
1650}
1651
1652/** @note Locks objects! */
1653STDMETHODIMP VirtualBox::FindDVDImage(IN_BSTR aLocation, IMedium **aDVDImage)
1654{
1655 CheckComArgStrNotEmptyOrNull(aLocation);
1656 CheckComArgOutSafeArrayPointerValid(aDVDImage);
1657
1658 AutoCaller autoCaller(this);
1659 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1660
1661 ComObjPtr<Medium> image;
1662 HRESULT rc = findDVDImage(NULL, aLocation, true /* setError */, &image);
1663
1664 /* the below will set *aDVDImage to NULL if dvd is null */
1665 image.queryInterfaceTo(aDVDImage);
1666
1667 return rc;
1668}
1669
1670/** @note Doesn't lock anything. */
1671STDMETHODIMP VirtualBox::OpenFloppyImage(IN_BSTR aLocation, IN_BSTR aId,
1672 IMedium **aFloppyImage)
1673{
1674 CheckComArgStrNotEmptyOrNull(aLocation);
1675 CheckComArgOutSafeArrayPointerValid(aFloppyImage);
1676
1677 AutoCaller autoCaller(this);
1678 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1679
1680 HRESULT rc = VBOX_E_FILE_ERROR;
1681
1682 Guid id(aId);
1683 /* generate an UUID if not specified */
1684 if (id.isEmpty())
1685 id.create();
1686
1687 ComObjPtr<Medium> image;
1688 image.createObject();
1689 rc = image->init(this, aLocation, Medium::OpenReadWrite, DeviceType_Floppy, true, id, false, Guid());
1690 if (SUCCEEDED(rc))
1691 {
1692 AutoWriteLock treeLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1693 bool fNeedsSaveSettings = false;
1694 rc = registerImage(image, DeviceType_Floppy, &fNeedsSaveSettings);
1695 treeLock.release();
1696
1697 if (SUCCEEDED(rc))
1698 image.queryInterfaceTo(aFloppyImage);
1699
1700 if (fNeedsSaveSettings)
1701 {
1702 AutoWriteLock vboxlock(this COMMA_LOCKVAL_SRC_POS);
1703 saveSettings();
1704 }
1705 }
1706
1707 return rc;
1708}
1709
1710/** @note Locks objects! */
1711STDMETHODIMP VirtualBox::GetFloppyImage(IN_BSTR aId, IMedium **aFloppyImage)
1712
1713{
1714 CheckComArgOutSafeArrayPointerValid(aFloppyImage);
1715
1716 AutoCaller autoCaller(this);
1717 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1718
1719 Guid id(aId);
1720 ComObjPtr<Medium> image;
1721 HRESULT rc = findFloppyImage(&id, NULL, true /* setError */, &image);
1722
1723 /* the below will set *aFloppyImage to NULL if image is null */
1724 image.queryInterfaceTo(aFloppyImage);
1725
1726 return rc;
1727}
1728
1729/** @note Locks objects! */
1730STDMETHODIMP VirtualBox::FindFloppyImage(IN_BSTR aLocation,
1731 IMedium **aFloppyImage)
1732{
1733 CheckComArgStrNotEmptyOrNull(aLocation);
1734 CheckComArgOutSafeArrayPointerValid(aFloppyImage);
1735
1736 AutoCaller autoCaller(this);
1737 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1738
1739 ComObjPtr<Medium> image;
1740 HRESULT rc = findFloppyImage(NULL, aLocation, true /* setError */, &image);
1741
1742 /* the below will set *aFloppyImage to NULL if img is null */
1743 image.queryInterfaceTo(aFloppyImage);
1744
1745 return rc;
1746}
1747
1748/** @note Locks this object for reading. */
1749STDMETHODIMP VirtualBox::GetGuestOSType(IN_BSTR aId, IGuestOSType **aType)
1750{
1751 /* Old ID to new ID conversion table. See r39691 for a source */
1752 static const wchar_t *kOldNewIDs[] =
1753 {
1754 L"unknown", L"Other",
1755 L"win31", L"Windows31",
1756 L"win95", L"Windows95",
1757 L"win98", L"Windows98",
1758 L"winme", L"WindowsMe",
1759 L"winnt4", L"WindowsNT4",
1760 L"win2k", L"Windows2000",
1761 L"winxp", L"WindowsXP",
1762 L"win2k3", L"Windows2003",
1763 L"winvista", L"WindowsVista",
1764 L"win2k8", L"Windows2008",
1765 L"ecs", L"OS2eCS",
1766 L"fedoracore", L"Fedora",
1767 /* the rest is covered by the case-insensitive comparison */
1768 };
1769
1770 CheckComArgNotNull(aType);
1771
1772 AutoCaller autoCaller(this);
1773 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1774
1775 /* first, look for a substitution */
1776 Bstr id = aId;
1777 for (size_t i = 0; i < RT_ELEMENTS(kOldNewIDs) / 2; i += 2)
1778 {
1779 if (id == kOldNewIDs[i])
1780 {
1781 id = kOldNewIDs[i + 1];
1782 break;
1783 }
1784 }
1785
1786 *aType = NULL;
1787
1788 AutoReadLock alock(m->ollGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1789 for (GuestOSTypesOList::iterator it = m->ollGuestOSTypes.begin();
1790 it != m->ollGuestOSTypes.end();
1791 ++it)
1792 {
1793 const Bstr &typeId = (*it)->id();
1794 AssertMsg(!typeId.isEmpty(), ("ID must not be NULL"));
1795 if (typeId.compare(id, Bstr::CaseInsensitive) == 0)
1796 {
1797 (*it).queryInterfaceTo(aType);
1798 break;
1799 }
1800 }
1801
1802 return (*aType) ? S_OK :
1803 setError(E_INVALIDARG,
1804 tr("'%ls' is not a valid Guest OS type"),
1805 aId);
1806}
1807
1808STDMETHODIMP VirtualBox::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL /* aWritable */)
1809{
1810 CheckComArgStrNotEmptyOrNull(aName);
1811 CheckComArgStrNotEmptyOrNull(aHostPath);
1812
1813 AutoCaller autoCaller(this);
1814 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1815
1816 return setError(E_NOTIMPL, "Not yet implemented");
1817}
1818
1819STDMETHODIMP VirtualBox::RemoveSharedFolder(IN_BSTR aName)
1820{
1821 CheckComArgStrNotEmptyOrNull(aName);
1822
1823 AutoCaller autoCaller(this);
1824 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1825
1826 return setError(E_NOTIMPL, "Not yet implemented");
1827}
1828
1829/**
1830 * @note Locks this object for reading.
1831 */
1832STDMETHODIMP VirtualBox::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
1833{
1834 using namespace settings;
1835
1836 if (ComSafeArrayOutIsNull(aKeys))
1837 return E_POINTER;
1838
1839 AutoCaller autoCaller(this);
1840 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1841
1842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1843
1844 com::SafeArray<BSTR> saKeys(m->pMainConfigFile->mapExtraDataItems.size());
1845 int i = 0;
1846 for (ExtraDataItemsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.begin();
1847 it != m->pMainConfigFile->mapExtraDataItems.end();
1848 ++it, ++i)
1849 {
1850 const Utf8Str &strName = it->first; // the key
1851 strName.cloneTo(&saKeys[i]);
1852 }
1853 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
1854
1855 return S_OK;
1856}
1857
1858/**
1859 * @note Locks this object for reading.
1860 */
1861STDMETHODIMP VirtualBox::GetExtraData(IN_BSTR aKey,
1862 BSTR *aValue)
1863{
1864 CheckComArgStrNotEmptyOrNull(aKey);
1865 CheckComArgNotNull(aValue);
1866
1867 AutoCaller autoCaller(this);
1868 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1869
1870 /* start with nothing found */
1871 Utf8Str strKey(aKey);
1872 Bstr bstrResult;
1873
1874 settings::ExtraDataItemsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(strKey);
1875 if (it != m->pMainConfigFile->mapExtraDataItems.end())
1876 // found:
1877 bstrResult = it->second; // source is a Utf8Str
1878
1879 /* return the result to caller (may be empty) */
1880 bstrResult.cloneTo(aValue);
1881
1882 return S_OK;
1883}
1884
1885/**
1886 * @note Locks this object for writing.
1887 */
1888STDMETHODIMP VirtualBox::SetExtraData(IN_BSTR aKey,
1889 IN_BSTR aValue)
1890{
1891 CheckComArgStrNotEmptyOrNull(aKey);
1892
1893 AutoCaller autoCaller(this);
1894 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1895
1896 Utf8Str strKey(aKey);
1897 Utf8Str strValue(aValue);
1898 Utf8Str strOldValue; // empty
1899
1900 // locking note: we only hold the read lock briefly to look up the old value,
1901 // then release it and call the onExtraCanChange callbacks. There is a small
1902 // chance of a race insofar as the callback might be called twice if two callers
1903 // change the same key at the same time, but that's a much better solution
1904 // than the deadlock we had here before. The actual changing of the extradata
1905 // is then performed under the write lock and race-free.
1906
1907 // look up the old value first; if nothing's changed then we need not do anything
1908 {
1909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
1910 settings::ExtraDataItemsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(strKey);
1911 if (it != m->pMainConfigFile->mapExtraDataItems.end())
1912 strOldValue = it->second;
1913 }
1914
1915 bool fChanged;
1916 if ((fChanged = (strOldValue != strValue)))
1917 {
1918 // ask for permission from all listeners outside the locks;
1919 // onExtraDataCanChange() only briefly requests the VirtualBox
1920 // lock to copy the list of callbacks to invoke
1921 Bstr error;
1922 Bstr bstrValue(aValue);
1923
1924 if (!onExtraDataCanChange(Guid::Empty, aKey, bstrValue, error))
1925 {
1926 const char *sep = error.isEmpty() ? "" : ": ";
1927 CBSTR err = error.raw();
1928 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
1929 sep, err));
1930 return setError(E_ACCESSDENIED,
1931 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
1932 aKey,
1933 bstrValue.raw(),
1934 sep,
1935 err);
1936 }
1937
1938 // data is changing and change not vetoed: then write it out under the lock
1939
1940 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1941
1942 if (strValue.isEmpty())
1943 m->pMainConfigFile->mapExtraDataItems.erase(strKey);
1944 else
1945 m->pMainConfigFile->mapExtraDataItems[strKey] = strValue;
1946 // creates a new key if needed
1947
1948 /* save settings on success */
1949 HRESULT rc = saveSettings();
1950 if (FAILED(rc)) return rc;
1951 }
1952
1953 // fire notification outside the lock
1954 if (fChanged)
1955 onExtraDataChange(Guid::Empty, aKey, aValue);
1956
1957 return S_OK;
1958}
1959
1960/**
1961 * @note Locks objects!
1962 */
1963STDMETHODIMP VirtualBox::OpenSession(ISession *aSession, IN_BSTR aMachineId)
1964{
1965 CheckComArgNotNull(aSession);
1966
1967 AutoCaller autoCaller(this);
1968 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1969
1970 Guid id(aMachineId);
1971 ComObjPtr<Machine> machine;
1972
1973 HRESULT rc = findMachine(id, true /* setError */, &machine);
1974 if (FAILED(rc)) return rc;
1975
1976 /* check the session state */
1977 SessionState_T state;
1978 rc = aSession->COMGETTER(State)(&state);
1979 if (FAILED(rc)) return rc;
1980
1981 if (state != SessionState_Closed)
1982 return setError(VBOX_E_INVALID_OBJECT_STATE,
1983 tr("The given session is already open or being opened"));
1984
1985 /* get the IInternalSessionControl interface */
1986 ComPtr<IInternalSessionControl> control = aSession;
1987 ComAssertMsgRet(!!control, ("No IInternalSessionControl interface"),
1988 E_INVALIDARG);
1989
1990 rc = machine->openSession(control);
1991
1992 if (SUCCEEDED(rc))
1993 {
1994 /*
1995 * tell the client watcher thread to update the set of
1996 * machines that have open sessions
1997 */
1998 updateClientWatcher();
1999
2000 /* fire an event */
2001 onSessionStateChange(id, SessionState_Open);
2002 }
2003
2004 return rc;
2005}
2006
2007/**
2008 * @note Locks objects!
2009 */
2010STDMETHODIMP VirtualBox::OpenRemoteSession(ISession *aSession,
2011 IN_BSTR aMachineId,
2012 IN_BSTR aType,
2013 IN_BSTR aEnvironment,
2014 IProgress **aProgress)
2015{
2016 LogRel(("remotesession=%s\n", Utf8Str(aMachineId).c_str()));
2017
2018 CheckComArgStrNotEmptyOrNull(aMachineId);
2019 CheckComArgNotNull(aSession);
2020 CheckComArgStrNotEmptyOrNull(aType);
2021 CheckComArgOutSafeArrayPointerValid(aProgress);
2022
2023 AutoCaller autoCaller(this);
2024 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2025
2026 Guid id(aMachineId);
2027 ComObjPtr<Machine> machine;
2028
2029 HRESULT rc = findMachine(id, true /* setError */, &machine);
2030 if (FAILED(rc)) return rc;
2031
2032 /* check the session state */
2033 SessionState_T state;
2034 rc = aSession->COMGETTER(State)(&state);
2035 if (FAILED(rc)) return rc;
2036
2037 if (state != SessionState_Closed)
2038 return setError(VBOX_E_INVALID_OBJECT_STATE,
2039 tr("The given session is already open or being opened"));
2040
2041 /* get the IInternalSessionControl interface */
2042 ComPtr<IInternalSessionControl> control = aSession;
2043 ComAssertMsgRet(!!control, ("No IInternalSessionControl interface"),
2044 E_INVALIDARG);
2045
2046 /* get the teleporter enable state for the progress object init. */
2047 BOOL fTeleporterEnabled;
2048 rc = machine->COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
2049 if (FAILED(rc))
2050 return rc;
2051
2052 /* create a progress object */
2053 ComObjPtr<ProgressProxy> progress;
2054 progress.createObject();
2055 rc = progress->init(this,
2056 static_cast<IMachine *>(machine),
2057 Bstr(tr("Spawning session")),
2058 TRUE /* aCancelable */,
2059 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
2060 Bstr(tr("Spawning session")),
2061 2 /* uFirstOperationWeight */,
2062 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
2063 if (SUCCEEDED(rc))
2064 {
2065 rc = machine->openRemoteSession(control, aType, aEnvironment, progress);
2066 if (SUCCEEDED(rc))
2067 {
2068 progress.queryInterfaceTo(aProgress);
2069
2070 /* signal the client watcher thread */
2071 updateClientWatcher();
2072
2073 /* fire an event */
2074 onSessionStateChange(id, SessionState_Spawning);
2075 }
2076 }
2077
2078 return rc;
2079}
2080
2081/**
2082 * @note Locks objects!
2083 */
2084STDMETHODIMP VirtualBox::OpenExistingSession(ISession *aSession,
2085 IN_BSTR aMachineId)
2086{
2087 CheckComArgNotNull(aSession);
2088
2089 AutoCaller autoCaller(this);
2090 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2091
2092 Guid id(aMachineId);
2093 ComObjPtr<Machine> machine;
2094
2095 HRESULT rc = findMachine(id, true /* setError */, &machine);
2096 if (FAILED(rc)) return rc;
2097
2098 /* check the session state */
2099 SessionState_T state;
2100 rc = aSession->COMGETTER(State)(&state);
2101 if (FAILED(rc)) return rc;
2102
2103 if (state != SessionState_Closed)
2104 return setError(VBOX_E_INVALID_OBJECT_STATE,
2105 tr("The given session is already open or being opened"));
2106
2107 /* get the IInternalSessionControl interface */
2108 ComPtr<IInternalSessionControl> control = aSession;
2109 ComAssertMsgRet(!!control, ("No IInternalSessionControl interface"),
2110 E_INVALIDARG);
2111
2112 rc = machine->openExistingSession(control);
2113
2114 return rc;
2115}
2116
2117/**
2118 * @note Locks this object for writing.
2119 */
2120STDMETHODIMP VirtualBox::RegisterCallback(IVirtualBoxCallback *aCallback)
2121{
2122 LogFlowThisFunc(("aCallback=%p\n", aCallback));
2123
2124 CheckComArgNotNull(aCallback);
2125
2126 AutoCaller autoCaller(this);
2127 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2128
2129 /* Query the interface we associate with IVirtualBoxCallback as the caller
2130 might've been compiled against a different SDK. */
2131 void *pvCallback;
2132 HRESULT hrc = aCallback->QueryInterface(COM_IIDOF(IVirtualBoxCallback), &pvCallback);
2133 if (FAILED(hrc))
2134 return setError(hrc, tr("Incompatible IVirtualBoxCallback interface - version mismatch?"));
2135 aCallback = (IVirtualBoxCallback *)pvCallback;
2136
2137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2138 m->llCallbacks.push_back(CallbackList::value_type(aCallback));
2139
2140 aCallback->Release();
2141 return S_OK;
2142}
2143
2144/**
2145 * @note Locks this object for writing.
2146 */
2147STDMETHODIMP VirtualBox::UnregisterCallback(IVirtualBoxCallback *aCallback)
2148{
2149 CheckComArgNotNull(aCallback);
2150
2151 AutoCaller autoCaller(this);
2152 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2153
2154 HRESULT rc = S_OK;
2155
2156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2157
2158 CallbackList::iterator it;
2159 it = std::find(m->llCallbacks.begin(),
2160 m->llCallbacks.end(),
2161 CallbackList::value_type(aCallback));
2162 if (it == m->llCallbacks.end())
2163 rc = E_INVALIDARG;
2164 else
2165 m->llCallbacks.erase(it);
2166
2167 LogFlowThisFunc(("aCallback=%p, rc=%08X\n", aCallback, rc));
2168 return rc;
2169}
2170
2171
2172STDMETHODIMP VirtualBox::WaitForPropertyChange(IN_BSTR /* aWhat */,
2173 ULONG /* aTimeout */,
2174 BSTR * /* aChanged */,
2175 BSTR * /* aValues */)
2176{
2177 ReturnComNotImplemented();
2178}
2179
2180// public methods only for internal purposes
2181/////////////////////////////////////////////////////////////////////////////
2182
2183#ifdef DEBUG
2184void VirtualBox::dumpAllBackRefs()
2185{
2186 {
2187 AutoReadLock al(m->ollHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2188 for (MediaList::const_iterator mt = m->ollHardDisks.begin();
2189 mt != m->ollHardDisks.end();
2190 ++mt)
2191 {
2192 ComObjPtr<Medium> pMedium = *mt;
2193 pMedium->dumpBackRefs();
2194 }
2195 }
2196 {
2197 AutoReadLock al(m->ollDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2198 for (MediaList::const_iterator mt = m->ollDVDImages.begin();
2199 mt != m->ollDVDImages.end();
2200 ++mt)
2201 {
2202 ComObjPtr<Medium> pMedium = *mt;
2203 pMedium->dumpBackRefs();
2204 }
2205 }
2206}
2207#endif
2208
2209/**
2210 * Posts an event to the event queue that is processed asynchronously
2211 * on a dedicated thread.
2212 *
2213 * Posting events to the dedicated event queue is useful to perform secondary
2214 * actions outside any object locks -- for example, to iterate over a list
2215 * of callbacks and inform them about some change caused by some object's
2216 * method call.
2217 *
2218 * @param event event to post
2219 * (must be allocated using |new|, will be deleted automatically
2220 * by the event thread after processing)
2221 *
2222 * @note Doesn't lock any object.
2223 */
2224HRESULT VirtualBox::postEvent(Event *event)
2225{
2226 AutoCaller autoCaller(this);
2227 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2228
2229 if (autoCaller.state() != Ready)
2230 {
2231 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the event is discarded!\n",
2232 autoCaller.state()));
2233 return S_OK;
2234 }
2235
2236 AssertReturn(event, E_FAIL);
2237 AssertReturn(m->pAsyncEventQ, E_FAIL);
2238
2239 if (m->pAsyncEventQ->postEvent(event))
2240 return S_OK;
2241
2242 return E_FAIL;
2243}
2244
2245/**
2246 * Adds a progress to the global collection of pending operations.
2247 * Usually gets called upon progress object initialization.
2248 *
2249 * @param aProgress Operation to add to the collection.
2250 *
2251 * @note Doesn't lock objects.
2252 */
2253HRESULT VirtualBox::addProgress(IProgress *aProgress)
2254{
2255 CheckComArgNotNull(aProgress);
2256
2257 AutoCaller autoCaller(this);
2258 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2259
2260 Bstr id;
2261 HRESULT rc = aProgress->COMGETTER(Id)(id.asOutParam());
2262 AssertComRCReturnRC(rc);
2263
2264 /* protect mProgressOperations */
2265 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
2266
2267 m->mapProgressOperations.insert(ProgressMap::value_type(Guid(id), aProgress));
2268 return S_OK;
2269}
2270
2271/**
2272 * Removes the progress from the global collection of pending operations.
2273 * Usually gets called upon progress completion.
2274 *
2275 * @param aId UUID of the progress operation to remove
2276 *
2277 * @note Doesn't lock objects.
2278 */
2279HRESULT VirtualBox::removeProgress(IN_GUID aId)
2280{
2281 AutoCaller autoCaller(this);
2282 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2283
2284 ComPtr<IProgress> progress;
2285
2286 /* protect mProgressOperations */
2287 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
2288
2289 size_t cnt = m->mapProgressOperations.erase(aId);
2290 Assert(cnt == 1);
2291 NOREF(cnt);
2292
2293 return S_OK;
2294}
2295
2296#ifdef RT_OS_WINDOWS
2297
2298struct StartSVCHelperClientData
2299{
2300 ComObjPtr<VirtualBox> that;
2301 ComObjPtr<Progress> progress;
2302 bool privileged;
2303 VirtualBox::SVCHelperClientFunc func;
2304 void *user;
2305};
2306
2307/**
2308 * Helper method that starts a worker thread that:
2309 * - creates a pipe communication channel using SVCHlpClient;
2310 * - starts an SVC Helper process that will inherit this channel;
2311 * - executes the supplied function by passing it the created SVCHlpClient
2312 * and opened instance to communicate to the Helper process and the given
2313 * Progress object.
2314 *
2315 * The user function is supposed to communicate to the helper process
2316 * using the \a aClient argument to do the requested job and optionally expose
2317 * the progress through the \a aProgress object. The user function should never
2318 * call notifyComplete() on it: this will be done automatically using the
2319 * result code returned by the function.
2320 *
2321 * Before the user function is started, the communication channel passed to
2322 * the \a aClient argument is fully set up, the function should start using
2323 * its write() and read() methods directly.
2324 *
2325 * The \a aVrc parameter of the user function may be used to return an error
2326 * code if it is related to communication errors (for example, returned by
2327 * the SVCHlpClient members when they fail). In this case, the correct error
2328 * message using this value will be reported to the caller. Note that the
2329 * value of \a aVrc is inspected only if the user function itself returns
2330 * success.
2331 *
2332 * If a failure happens anywhere before the user function would be normally
2333 * called, it will be called anyway in special "cleanup only" mode indicated
2334 * by \a aClient, \a aProgress and \aVrc arguments set to NULL. In this mode,
2335 * all the function is supposed to do is to cleanup its aUser argument if
2336 * necessary (it's assumed that the ownership of this argument is passed to
2337 * the user function once #startSVCHelperClient() returns a success, thus
2338 * making it responsible for the cleanup).
2339 *
2340 * After the user function returns, the thread will send the SVCHlpMsg::Null
2341 * message to indicate a process termination.
2342 *
2343 * @param aPrivileged |true| to start the SVC Helper process as a privileged
2344 * user that can perform administrative tasks
2345 * @param aFunc user function to run
2346 * @param aUser argument to the user function
2347 * @param aProgress progress object that will track operation completion
2348 *
2349 * @note aPrivileged is currently ignored (due to some unsolved problems in
2350 * Vista) and the process will be started as a normal (unprivileged)
2351 * process.
2352 *
2353 * @note Doesn't lock anything.
2354 */
2355HRESULT VirtualBox::startSVCHelperClient(bool aPrivileged,
2356 SVCHelperClientFunc aFunc,
2357 void *aUser, Progress *aProgress)
2358{
2359 AssertReturn(aFunc, E_POINTER);
2360 AssertReturn(aProgress, E_POINTER);
2361
2362 AutoCaller autoCaller(this);
2363 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2364
2365 /* create the SVCHelperClientThread() argument */
2366 std::auto_ptr <StartSVCHelperClientData>
2367 d(new StartSVCHelperClientData());
2368 AssertReturn(d.get(), E_OUTOFMEMORY);
2369
2370 d->that = this;
2371 d->progress = aProgress;
2372 d->privileged = aPrivileged;
2373 d->func = aFunc;
2374 d->user = aUser;
2375
2376 RTTHREAD tid = NIL_RTTHREAD;
2377 int vrc = RTThreadCreate(&tid, SVCHelperClientThread,
2378 static_cast <void *>(d.get()),
2379 0, RTTHREADTYPE_MAIN_WORKER,
2380 RTTHREADFLAGS_WAITABLE, "SVCHelper");
2381
2382 ComAssertMsgRCRet(vrc, ("Could not create SVCHelper thread (%Rrc)", vrc),
2383 E_FAIL);
2384
2385 /* d is now owned by SVCHelperClientThread(), so release it */
2386 d.release();
2387
2388 return S_OK;
2389}
2390
2391/**
2392 * Worker thread for startSVCHelperClient().
2393 */
2394/* static */
2395DECLCALLBACK(int)
2396VirtualBox::SVCHelperClientThread(RTTHREAD aThread, void *aUser)
2397{
2398 LogFlowFuncEnter();
2399
2400 std::auto_ptr <StartSVCHelperClientData>
2401 d(static_cast <StartSVCHelperClientData *>(aUser));
2402
2403 HRESULT rc = S_OK;
2404 bool userFuncCalled = false;
2405
2406 do
2407 {
2408 AssertBreakStmt(d.get(), rc = E_POINTER);
2409 AssertReturn(!d->progress.isNull(), E_POINTER);
2410
2411 /* protect VirtualBox from uninitialization */
2412 AutoCaller autoCaller(d->that);
2413 if (!autoCaller.isOk())
2414 {
2415 /* it's too late */
2416 rc = autoCaller.rc();
2417 break;
2418 }
2419
2420 int vrc = VINF_SUCCESS;
2421
2422 Guid id;
2423 id.create();
2424 SVCHlpClient client;
2425 vrc = client.create(Utf8StrFmt("VirtualBox\\SVCHelper\\{%RTuuid}",
2426 id.raw()).c_str());
2427 if (RT_FAILURE(vrc))
2428 {
2429 rc = setError(E_FAIL,
2430 tr("Could not create the communication channel (%Rrc)"), vrc);
2431 break;
2432 }
2433
2434 /* get the path to the executable */
2435 char exePathBuf[RTPATH_MAX];
2436 char *exePath = RTProcGetExecutableName(exePathBuf, RTPATH_MAX);
2437 ComAssertBreak(exePath, E_FAIL);
2438
2439 Utf8Str argsStr = Utf8StrFmt("/Helper %s", client.name().raw());
2440
2441 LogFlowFunc(("Starting '\"%s\" %s'...\n", exePath, argsStr.raw()));
2442
2443 RTPROCESS pid = NIL_RTPROCESS;
2444
2445 if (d->privileged)
2446 {
2447 /* Attempt to start a privileged process using the Run As dialog */
2448
2449 Bstr file = exePath;
2450 Bstr parameters = argsStr;
2451
2452 SHELLEXECUTEINFO shExecInfo;
2453
2454 shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
2455
2456 shExecInfo.fMask = NULL;
2457 shExecInfo.hwnd = NULL;
2458 shExecInfo.lpVerb = L"runas";
2459 shExecInfo.lpFile = file;
2460 shExecInfo.lpParameters = parameters;
2461 shExecInfo.lpDirectory = NULL;
2462 shExecInfo.nShow = SW_NORMAL;
2463 shExecInfo.hInstApp = NULL;
2464
2465 if (!ShellExecuteEx(&shExecInfo))
2466 {
2467 int vrc2 = RTErrConvertFromWin32(GetLastError());
2468 /* hide excessive details in case of a frequent error
2469 * (pressing the Cancel button to close the Run As dialog) */
2470 if (vrc2 == VERR_CANCELLED)
2471 rc = setError(E_FAIL,
2472 tr("Operation cancelled by the user"));
2473 else
2474 rc = setError(E_FAIL,
2475 tr("Could not launch a privileged process '%s' (%Rrc)"),
2476 exePath, vrc2);
2477 break;
2478 }
2479 }
2480 else
2481 {
2482 const char *args[] = { exePath, "/Helper", client.name().c_str(), 0 };
2483 vrc = RTProcCreate(exePath, args, RTENV_DEFAULT, 0, &pid);
2484 if (RT_FAILURE(vrc))
2485 {
2486 rc = setError(E_FAIL,
2487 tr("Could not launch a process '%s' (%Rrc)"), exePath, vrc);
2488 break;
2489 }
2490 }
2491
2492 /* wait for the client to connect */
2493 vrc = client.connect();
2494 if (RT_SUCCESS(vrc))
2495 {
2496 /* start the user supplied function */
2497 rc = d->func(&client, d->progress, d->user, &vrc);
2498 userFuncCalled = true;
2499 }
2500
2501 /* send the termination signal to the process anyway */
2502 {
2503 int vrc2 = client.write(SVCHlpMsg::Null);
2504 if (RT_SUCCESS(vrc))
2505 vrc = vrc2;
2506 }
2507
2508 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
2509 {
2510 rc = setError(E_FAIL,
2511 tr("Could not operate the communication channel (%Rrc)"), vrc);
2512 break;
2513 }
2514 }
2515 while (0);
2516
2517 if (FAILED(rc) && !userFuncCalled)
2518 {
2519 /* call the user function in the "cleanup only" mode
2520 * to let it free resources passed to in aUser */
2521 d->func(NULL, NULL, d->user, NULL);
2522 }
2523
2524 d->progress->notifyComplete(rc);
2525
2526 LogFlowFuncLeave();
2527 return 0;
2528}
2529
2530#endif /* RT_OS_WINDOWS */
2531
2532/**
2533 * Sends a signal to the client watcher thread to rescan the set of machines
2534 * that have open sessions.
2535 *
2536 * @note Doesn't lock anything.
2537 */
2538void VirtualBox::updateClientWatcher()
2539{
2540 AutoCaller autoCaller(this);
2541 AssertComRCReturnVoid(autoCaller.rc());
2542
2543 AssertReturnVoid(m->threadClientWatcher != NIL_RTTHREAD);
2544
2545 /* sent an update request */
2546#if defined(RT_OS_WINDOWS)
2547 ::SetEvent(m->updateReq);
2548#elif defined(RT_OS_OS2)
2549 RTSemEventSignal(m->updateReq);
2550#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
2551 RTSemEventSignal(m->updateReq);
2552#else
2553# error "Port me!"
2554#endif
2555}
2556
2557/**
2558 * Adds the given child process ID to the list of processes to be reaped.
2559 * This call should be followed by #updateClientWatcher() to take the effect.
2560 */
2561void VirtualBox::addProcessToReap(RTPROCESS pid)
2562{
2563 AutoCaller autoCaller(this);
2564 AssertComRCReturnVoid(autoCaller.rc());
2565
2566 /// @todo (dmik) Win32?
2567#ifndef RT_OS_WINDOWS
2568 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2569 m->llProcesses.push_back(pid);
2570#endif
2571}
2572
2573/**
2574 * Removes a dead callback.
2575 * @param aCallback The reference to the registered callback interface.
2576 */
2577void VirtualBox::removeDeadCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2578{
2579 /* find and delete */
2580 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2581 CallbackList::iterator it = std::find(m->llCallbacks.begin(),
2582 m->llCallbacks.end(),
2583 CallbackList::value_type(aCallback));
2584 if (it != m->llCallbacks.end())
2585 {
2586 LogRel(("Removing dead callback: %p\n", &*it));
2587 m->llCallbacks.erase(it);
2588 }
2589}
2590
2591/** Event for onMachineStateChange(), onMachineDataChange(), onMachineRegistered() */
2592struct MachineEvent : public VirtualBox::CallbackEvent
2593{
2594 MachineEvent(VirtualBox *aVB, const Guid &aId)
2595 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineDataChange), id(aId)
2596 {}
2597
2598 MachineEvent(VirtualBox *aVB, const Guid &aId, MachineState_T aState)
2599 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineStateChange), id(aId)
2600 , state(aState)
2601 {}
2602
2603 MachineEvent(VirtualBox *aVB, const Guid &aId, BOOL aRegistered)
2604 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineRegistered), id(aId)
2605 , registered(aRegistered)
2606 {}
2607
2608 HRESULT handleCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2609 {
2610 switch (mWhat)
2611 {
2612 case VirtualBoxCallbackRegistration::kOnMachineDataChange:
2613 LogFlow(("OnMachineDataChange: id={%RTuuid}\n", id.ptr()));
2614 return aCallback->OnMachineDataChange(id.toUtf16());
2615
2616 case VirtualBoxCallbackRegistration::kOnMachineStateChange:
2617 LogFlow(("OnMachineStateChange: id={%RTuuid}, state=%d\n",
2618 id.ptr(), state));
2619 return aCallback->OnMachineStateChange(id.toUtf16(), state);
2620
2621 case VirtualBoxCallbackRegistration::kOnMachineRegistered:
2622 LogFlow(("OnMachineRegistered: id={%RTuuid}, registered=%d\n",
2623 id.ptr(), registered));
2624 return aCallback->OnMachineRegistered(id.toUtf16(), registered);
2625
2626 default:
2627 AssertFailedReturn(S_OK);
2628 }
2629 }
2630#ifdef RT_OS_WINDOWS
2631 HRESULT prepareEventDesc(ComEventDesc& aEvDesc)
2632 {
2633 switch (mWhat)
2634 {
2635 case VirtualBoxCallbackRegistration::kOnMachineDataChange:
2636 aEvDesc.init("OnMachineDataChange", 1);
2637 aEvDesc.add(id.toUtf16());
2638 break;
2639
2640 case VirtualBoxCallbackRegistration::kOnMachineStateChange:
2641 aEvDesc.init("OnMachineStateChange", 2);
2642 aEvDesc.add(id.toUtf16()).add((int)state);
2643 break;
2644
2645 case VirtualBoxCallbackRegistration::kOnMachineRegistered:
2646 aEvDesc.init("OnMachineRegistered", 2);
2647 aEvDesc.add(id.toUtf16()).add((BOOL)registered);
2648 break;
2649
2650 default:
2651 AssertFailedReturn(S_OK);
2652 }
2653 return S_OK;
2654 }
2655#endif
2656
2657 Guid id;
2658 MachineState_T state;
2659 BOOL registered;
2660};
2661
2662/**
2663 * @note Doesn't lock any object.
2664 */
2665void VirtualBox::onMachineStateChange(const Guid &aId, MachineState_T aState)
2666{
2667 postEvent(new MachineEvent(this, aId, aState));
2668}
2669
2670/**
2671 * @note Doesn't lock any object.
2672 */
2673void VirtualBox::onMachineDataChange(const Guid &aId)
2674{
2675 postEvent(new MachineEvent(this, aId));
2676}
2677
2678/**
2679 * @note Locks this object for reading.
2680 */
2681BOOL VirtualBox::onExtraDataCanChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue,
2682 Bstr &aError)
2683{
2684 LogFlowThisFunc(("machine={%s} aKey={%ls} aValue={%ls}\n",
2685 aId.toString().raw(), aKey, aValue));
2686
2687 AutoCaller autoCaller(this);
2688 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2689
2690 CallbackList list;
2691 {
2692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2693 list = m->llCallbacks;
2694 }
2695
2696 BOOL allowChange = TRUE;
2697 CallbackList::iterator it = list.begin();
2698 Bstr id = aId.toUtf16();
2699 while ((it != list.end()) && allowChange)
2700 {
2701 if (it->isWanted(VirtualBoxCallbackRegistration::kOnExtraDataCanChange))
2702 {
2703 HRESULT rc = it->ptrICb->OnExtraDataCanChange(id, aKey, aValue,
2704 aError.asOutParam(),
2705 &allowChange);
2706 if (FAILED(rc))
2707 {
2708 if (rc == VBOX_E_DONT_CALL_AGAIN)
2709 {
2710 /* Have to update the original. */
2711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2712 CallbackList::iterator itOrg;
2713 itOrg = std::find(m->llCallbacks.begin(),
2714 m->llCallbacks.end(),
2715 CallbackList::value_type(it->ptrICb));
2716 if (itOrg != m->llCallbacks.end())
2717 itOrg->setDontCallAgain(VirtualBoxCallbackRegistration::kOnExtraDataCanChange);
2718 }
2719 else if (FAILED_DEAD_INTERFACE(rc))
2720 removeDeadCallback(it->ptrICb);
2721
2722 /* if a call to this method fails for some reason (for ex., because
2723 * the other side is dead), we ensure allowChange stays true
2724 * (MS COM RPC implementation seems to zero all output vars before
2725 * issuing an IPC call or after a failure, so it's essential
2726 * there) */
2727 allowChange = TRUE;
2728 }
2729 }
2730 ++it;
2731 }
2732
2733 LogFlowThisFunc(("allowChange=%RTbool\n", allowChange));
2734 return allowChange;
2735}
2736
2737/** Event for onExtraDataChange() */
2738struct ExtraDataEvent : public VirtualBox::CallbackEvent
2739{
2740 ExtraDataEvent(VirtualBox *aVB, const Guid &aMachineId,
2741 IN_BSTR aKey, IN_BSTR aVal)
2742 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnExtraDataChange)
2743 , machineId(aMachineId), key(aKey), val(aVal)
2744 {}
2745
2746 HRESULT handleCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2747 {
2748 LogFlow(("OnExtraDataChange: machineId={%RTuuid}, key='%ls', val='%ls'\n",
2749 machineId.ptr(), key.raw(), val.raw()));
2750 return aCallback->OnExtraDataChange(machineId.toUtf16(), key, val);
2751 }
2752#ifdef RT_OS_WINDOWS
2753 HRESULT prepareEventDesc(ComEventDesc& aEvDesc)
2754 {
2755 aEvDesc.init("OnExtraDataChange", 3);
2756 aEvDesc.add(machineId.toUtf16()).add(key).add(val);
2757 return S_OK;
2758 }
2759#endif
2760
2761 Guid machineId;
2762 Bstr key, val;
2763};
2764
2765/**
2766 * @note Doesn't lock any object.
2767 */
2768void VirtualBox::onExtraDataChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue)
2769{
2770 postEvent(new ExtraDataEvent(this, aId, aKey, aValue));
2771}
2772
2773/**
2774 * @note Doesn't lock any object.
2775 */
2776void VirtualBox::onMachineRegistered(const Guid &aId, BOOL aRegistered)
2777{
2778 postEvent(new MachineEvent(this, aId, aRegistered));
2779}
2780
2781/** Event for onSessionStateChange() */
2782struct SessionEvent : public VirtualBox::CallbackEvent
2783{
2784 SessionEvent(VirtualBox *aVB, const Guid &aMachineId, SessionState_T aState)
2785 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnSessionStateChange)
2786 , machineId(aMachineId), sessionState(aState)
2787 {}
2788
2789 HRESULT handleCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2790 {
2791 LogFlow(("OnSessionStateChange: machineId={%RTuuid}, sessionState=%d\n",
2792 machineId.ptr(), sessionState));
2793 return aCallback->OnSessionStateChange(machineId.toUtf16(), sessionState);
2794 }
2795
2796#ifdef RT_OS_WINDOWS
2797 HRESULT prepareEventDesc(ComEventDesc& aEvDesc)
2798 {
2799 aEvDesc.init("OnSessionStateChange", 2);
2800 aEvDesc.add(machineId.toUtf16()).add((int)sessionState);
2801 return S_OK;
2802 }
2803#endif
2804
2805 Guid machineId;
2806 SessionState_T sessionState;
2807};
2808
2809/**
2810 * @note Doesn't lock any object.
2811 */
2812void VirtualBox::onSessionStateChange(const Guid &aId, SessionState_T aState)
2813{
2814 postEvent(new SessionEvent(this, aId, aState));
2815}
2816
2817/** Event for onSnapshotTaken(), onSnapshotDeleted() and onSnapshotChange() */
2818struct SnapshotEvent : public VirtualBox::CallbackEvent
2819{
2820 SnapshotEvent(VirtualBox *aVB, const Guid &aMachineId, const Guid &aSnapshotId,
2821 VirtualBoxCallbackRegistration::CallbackBit aWhat)
2822 : CallbackEvent(aVB, aWhat)
2823 , machineId(aMachineId), snapshotId(aSnapshotId)
2824 {}
2825
2826 HRESULT handleCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2827 {
2828 Bstr mid = machineId.toUtf16();
2829 Bstr sid = snapshotId.toUtf16();
2830
2831 switch (mWhat)
2832 {
2833 case VirtualBoxCallbackRegistration::kOnSnapshotTaken:
2834 LogFlow(("OnSnapshotTaken: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2835 machineId.ptr(), snapshotId.ptr()));
2836 return aCallback->OnSnapshotTaken(mid, sid);
2837
2838 case VirtualBoxCallbackRegistration::kOnSnapshotDeleted:
2839 LogFlow(("OnSnapshotDeleted: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2840 machineId.ptr(), snapshotId.ptr()));
2841 return aCallback->OnSnapshotDeleted(mid, sid);
2842
2843 case VirtualBoxCallbackRegistration::kOnSnapshotChange:
2844 LogFlow(("OnSnapshotChange: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2845 machineId.ptr(), snapshotId.ptr()));
2846 return aCallback->OnSnapshotChange(mid, sid);
2847
2848 default:
2849 AssertFailedReturn(S_OK);
2850 }
2851 }
2852
2853#ifdef RT_OS_WINDOWS
2854 HRESULT prepareEventDesc(ComEventDesc& aEvDesc)
2855 {
2856 aEvDesc.init("OnSnapshotTaken", 2);
2857 aEvDesc.add(machineId.toUtf16()).add(snapshotId.toUtf16());
2858 return S_OK;
2859 }
2860#endif
2861
2862
2863 Guid machineId;
2864 Guid snapshotId;
2865};
2866
2867/**
2868 * @note Doesn't lock any object.
2869 */
2870void VirtualBox::onSnapshotTaken(const Guid &aMachineId, const Guid &aSnapshotId)
2871{
2872 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2873 VirtualBoxCallbackRegistration::kOnSnapshotTaken));
2874}
2875
2876/**
2877 * @note Doesn't lock any object.
2878 */
2879void VirtualBox::onSnapshotDeleted(const Guid &aMachineId, const Guid &aSnapshotId)
2880{
2881 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2882 VirtualBoxCallbackRegistration::kOnSnapshotDeleted));
2883}
2884
2885/**
2886 * @note Doesn't lock any object.
2887 */
2888void VirtualBox::onSnapshotChange(const Guid &aMachineId, const Guid &aSnapshotId)
2889{
2890 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2891 VirtualBoxCallbackRegistration::kOnSnapshotChange));
2892}
2893
2894/** Event for onGuestPropertyChange() */
2895struct GuestPropertyEvent : public VirtualBox::CallbackEvent
2896{
2897 GuestPropertyEvent(VirtualBox *aVBox, const Guid &aMachineId,
2898 IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
2899 : CallbackEvent(aVBox, VirtualBoxCallbackRegistration::kOnGuestPropertyChange),
2900 machineId(aMachineId),
2901 name(aName),
2902 value(aValue),
2903 flags(aFlags)
2904 {}
2905
2906 HRESULT handleCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2907 {
2908 LogFlow(("OnGuestPropertyChange: machineId={%RTuuid}, name='%ls', value='%ls', flags='%ls'\n",
2909 machineId.ptr(), name.raw(), value.raw(), flags.raw()));
2910 return aCallback->OnGuestPropertyChange(machineId.toUtf16(), name, value, flags);
2911 }
2912
2913#ifdef RT_OS_WINDOWS
2914 HRESULT prepareEventDesc(ComEventDesc& aEvDesc)
2915 {
2916 aEvDesc.init("OnGuestPropertyChange", 4);
2917 aEvDesc.add(machineId.toUtf16()).add(name).add(value).add(flags);
2918 return S_OK;
2919 }
2920#endif
2921
2922 Guid machineId;
2923 Bstr name, value, flags;
2924};
2925
2926/**
2927 * @note Doesn't lock any object.
2928 */
2929void VirtualBox::onGuestPropertyChange(const Guid &aMachineId, IN_BSTR aName,
2930 IN_BSTR aValue, IN_BSTR aFlags)
2931{
2932 postEvent(new GuestPropertyEvent(this, aMachineId, aName, aValue, aFlags));
2933}
2934
2935/**
2936 * @note Locks this object for reading.
2937 */
2938ComObjPtr<GuestOSType> VirtualBox::getUnknownOSType()
2939{
2940 ComObjPtr<GuestOSType> type;
2941 AutoCaller autoCaller(this);
2942 AssertComRCReturn(autoCaller.rc(), type);
2943
2944 /* unknown type must always be the first */
2945 ComAssertRet(m->ollGuestOSTypes.size() > 0, type);
2946
2947 return m->ollGuestOSTypes.front();
2948}
2949
2950/**
2951 * Returns the list of opened machines (machines having direct sessions opened
2952 * by client processes) and optionally the list of direct session controls.
2953 *
2954 * @param aMachines Where to put opened machines (will be empty if none).
2955 * @param aControls Where to put direct session controls (optional).
2956 *
2957 * @note The returned lists contain smart pointers. So, clear it as soon as
2958 * it becomes no more necessary to release instances.
2959 *
2960 * @note It can be possible that a session machine from the list has been
2961 * already uninitialized, so do a usual AutoCaller/AutoReadLock sequence
2962 * when accessing unprotected data directly.
2963 *
2964 * @note Locks objects for reading.
2965 */
2966void VirtualBox::getOpenedMachines(SessionMachinesList &aMachines,
2967 InternalControlList *aControls /*= NULL*/)
2968{
2969 AutoCaller autoCaller(this);
2970 AssertComRCReturnVoid(autoCaller.rc());
2971
2972 aMachines.clear();
2973 if (aControls)
2974 aControls->clear();
2975
2976 AutoReadLock alock(m->ollMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2977
2978 for (MachinesOList::iterator it = m->ollMachines.begin();
2979 it != m->ollMachines.end();
2980 ++it)
2981 {
2982 ComObjPtr<SessionMachine> sm;
2983 ComPtr<IInternalSessionControl> ctl;
2984 if ((*it)->isSessionOpen(sm, &ctl))
2985 {
2986 aMachines.push_back(sm);
2987 if (aControls)
2988 aControls->push_back(ctl);
2989 }
2990 }
2991}
2992
2993/**
2994 * Searches for a Machine object with the given ID in the collection
2995 * of registered machines.
2996 *
2997 * @param id
2998 * ID of the machine
2999 * @param doSetError
3000 * if TRUE, the appropriate error info is set in case when the machine
3001 * is not found
3002 * @param machine
3003 * where to store the found machine object (can be NULL)
3004 *
3005 * @return
3006 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
3007 *
3008 * @note Locks this object for reading.
3009 */
3010HRESULT VirtualBox::findMachine(const Guid &aId,
3011 bool aSetError,
3012 ComObjPtr<Machine> *aMachine /* = NULL */)
3013{
3014 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
3015
3016 AutoCaller autoCaller(this);
3017 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3018
3019 {
3020 AutoReadLock al(m->ollMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3021
3022 for (MachinesOList::iterator it = m->ollMachines.begin();
3023 it != m->ollMachines.end();
3024 ++it)
3025 {
3026 ComObjPtr<Machine> pMachine2 = *it;
3027 AutoCaller machCaller(pMachine2);
3028 /* skip inaccessible machines */
3029 if (FAILED(machCaller.rc()))
3030 continue;
3031
3032 if (pMachine2->getId() == aId)
3033 {
3034 rc = S_OK;
3035 if (aMachine)
3036 *aMachine = pMachine2;
3037 break;
3038 }
3039 }
3040 }
3041
3042 if (aSetError && FAILED(rc))
3043 rc = setError(rc,
3044 tr("Could not find a registered machine with UUID {%RTuuid}"),
3045 aId.raw());
3046
3047 return rc;
3048}
3049
3050/**
3051 * Searches for a Medium object with the given ID or location in the list of
3052 * registered hard disks. If both ID and location are specified, the first
3053 * object that matches either of them (not necessarily both) is returned.
3054 *
3055 * @param aId ID of the hard disk (unused when NULL).
3056 * @param aLocation Full location specification (unused NULL).
3057 * @param aSetError If @c true , the appropriate error info is set in case
3058 * when the hard disk is not found.
3059 * @param aHardDisk Where to store the found hard disk object (can be NULL).
3060 *
3061 * @return S_OK when found or E_INVALIDARG when not found.
3062 *
3063 * @note Locks the media tree for reading.
3064 */
3065HRESULT VirtualBox::findHardDisk(const Guid *aId,
3066 CBSTR aLocation,
3067 bool aSetError,
3068 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
3069{
3070 AssertReturn(aId || aLocation, E_INVALIDARG);
3071
3072 // we use the hard disks map, but it is protected by the
3073 // hard disk _list_ lock handle
3074 AutoReadLock alock(m->ollHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3075
3076 /* first, look up by UUID in the map if UUID is provided */
3077 if (aId)
3078 {
3079 HardDiskMap::const_iterator it = m->mapHardDisks.find(*aId);
3080 if (it != m->mapHardDisks.end())
3081 {
3082 if (aHardDisk)
3083 *aHardDisk = (*it).second;
3084 return S_OK;
3085 }
3086 }
3087
3088 /* then iterate and search by location */
3089 int result = -1;
3090 if (aLocation)
3091 {
3092 Utf8Str location = aLocation;
3093
3094 for (HardDiskMap::const_iterator it = m->mapHardDisks.begin();
3095 it != m->mapHardDisks.end();
3096 ++ it)
3097 {
3098 const ComObjPtr<Medium> &hd = (*it).second;
3099
3100 HRESULT rc = hd->compareLocationTo(location.c_str(), result);
3101 if (FAILED(rc)) return rc;
3102
3103 if (result == 0)
3104 {
3105 if (aHardDisk)
3106 *aHardDisk = hd;
3107 break;
3108 }
3109 }
3110 }
3111
3112 HRESULT rc = result == 0 ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
3113
3114 if (aSetError && result != 0)
3115 {
3116 if (aId)
3117 setError(rc,
3118 tr("Could not find a hard disk with UUID {%RTuuid} in the media registry ('%s')"),
3119 aId->raw(),
3120 m->strSettingsFilePath.raw());
3121 else
3122 setError(rc,
3123 tr("Could not find a hard disk with location '%ls' in the media registry ('%s')"),
3124 aLocation,
3125 m->strSettingsFilePath.raw());
3126 }
3127
3128 return rc;
3129}
3130
3131/**
3132 * Searches for a Medium object with the given ID or location in the list of
3133 * registered DVD images. If both ID and file path are specified, the first
3134 * object that matches either of them (not necessarily both) is returned.
3135 *
3136 * @param aId ID of the DVD image (unused when NULL).
3137 * @param aLocation Full path to the image file (unused when NULL).
3138 * @param aSetError If @c true, the appropriate error info is set in case when
3139 * the image is not found.
3140 * @param aImage Where to store the found image object (can be NULL).
3141 *
3142 * @return S_OK when found or E_INVALIDARG when not found.
3143 *
3144 * @note Locks the media tree for reading.
3145 */
3146HRESULT VirtualBox::findDVDImage(const Guid *aId,
3147 CBSTR aLocation,
3148 bool aSetError,
3149 ComObjPtr<Medium> *aImage /* = NULL */)
3150{
3151 AssertReturn(aId || aLocation, E_INVALIDARG);
3152
3153 Utf8Str location;
3154
3155 if (aLocation != NULL)
3156 {
3157 int vrc = calculateFullPath(Utf8Str(aLocation), location);
3158 if (RT_FAILURE(vrc))
3159 return setError(VBOX_E_FILE_ERROR,
3160 tr("Invalid image file location '%ls' (%Rrc)"),
3161 aLocation,
3162 vrc);
3163 }
3164
3165 AutoReadLock alock(m->ollDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3166
3167 bool found = false;
3168
3169 for (MediaList::const_iterator it = m->ollDVDImages.begin();
3170 it != m->ollDVDImages.end();
3171 ++ it)
3172 {
3173 /* no AutoCaller, registered image life time is bound to this */
3174 AutoReadLock imageLock(*it COMMA_LOCKVAL_SRC_POS);
3175
3176 found = (aId && (*it)->getId() == *aId) ||
3177 (aLocation != NULL &&
3178 RTPathCompare(location.c_str(),
3179 (*it)->getLocationFull().c_str()
3180 ) == 0);
3181 if (found)
3182 {
3183 if (aImage)
3184 *aImage = *it;
3185 break;
3186 }
3187 }
3188
3189 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
3190
3191 if (aSetError && !found)
3192 {
3193 if (aId)
3194 setError(rc,
3195 tr("Could not find a CD/DVD image with UUID {%RTuuid} in the media registry ('%s')"),
3196 aId->raw(),
3197 m->strSettingsFilePath.raw());
3198 else
3199 setError(rc,
3200 tr("Could not find a CD/DVD image with location '%ls' in the media registry ('%s')"),
3201 aLocation,
3202 m->strSettingsFilePath.raw());
3203 }
3204
3205 return rc;
3206}
3207
3208/**
3209 * Searches for a Medium object with the given ID or location in the
3210 * collection of registered DVD images. If both ID and file path are specified,
3211 * the first object that matches either of them (not necessarily both) is
3212 * returned.
3213 *
3214 * @param aId ID of the DVD image (unused when NULL).
3215 * @param aLocation Full path to the image file (unused when NULL).
3216 * @param aSetError If @c true, the appropriate error info is set in case when
3217 * the image is not found.
3218 * @param aImage Where to store the found image object (can be NULL).
3219 *
3220 * @return S_OK when found or E_INVALIDARG when not found.
3221 *
3222 * @note Locks the media tree for reading.
3223 */
3224HRESULT VirtualBox::findFloppyImage(const Guid *aId,
3225 CBSTR aLocation,
3226 bool aSetError,
3227 ComObjPtr<Medium> *aImage /* = NULL */)
3228{
3229 AssertReturn(aId || aLocation, E_INVALIDARG);
3230
3231 Utf8Str location;
3232
3233 if (aLocation != NULL)
3234 {
3235 int vrc = calculateFullPath(Utf8Str(aLocation), location);
3236 if (RT_FAILURE(vrc))
3237 return setError(VBOX_E_FILE_ERROR,
3238 tr("Invalid image file location '%ls' (%Rrc)"),
3239 aLocation, vrc);
3240 }
3241
3242 AutoReadLock alock(m->ollFloppyImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3243
3244 bool found = false;
3245
3246 for (MediaList::const_iterator it = m->ollFloppyImages.begin();
3247 it != m->ollFloppyImages.end();
3248 ++ it)
3249 {
3250 /* no AutoCaller, registered image life time is bound to this */
3251 AutoReadLock imageLock(*it COMMA_LOCKVAL_SRC_POS);
3252
3253 found = (aId && (*it)->getId() == *aId) ||
3254 (aLocation != NULL &&
3255 RTPathCompare(location.c_str(),
3256 (*it)->getLocationFull().c_str()
3257 ) == 0);
3258 if (found)
3259 {
3260 if (aImage)
3261 *aImage = *it;
3262 break;
3263 }
3264 }
3265
3266 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
3267
3268 if (aSetError && !found)
3269 {
3270 if (aId)
3271 setError(rc,
3272 tr("Could not find a floppy image with UUID {%RTuuid} in the media registry ('%s')"),
3273 aId->raw(),
3274 m->strSettingsFilePath.raw());
3275 else
3276 setError(rc,
3277 tr("Could not find a floppy image with location '%ls' in the media registry ('%s')"),
3278 aLocation,
3279 m->strSettingsFilePath.raw());
3280 }
3281
3282 return rc;
3283}
3284
3285HRESULT VirtualBox::findGuestOSType(const Bstr &bstrOSType,
3286 GuestOSType*& pGuestOSType)
3287{
3288 /* Look for a GuestOSType object */
3289 AssertMsg(m->ollGuestOSTypes.size() != 0,
3290 ("Guest OS types array must be filled"));
3291
3292 if (bstrOSType.isEmpty())
3293 {
3294 pGuestOSType = NULL;
3295 return S_OK;
3296 }
3297
3298 AutoReadLock alock(m->ollGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3299 for (GuestOSTypesOList::const_iterator it = m->ollGuestOSTypes.begin();
3300 it != m->ollGuestOSTypes.end();
3301 ++it)
3302 {
3303 if ((*it)->id() == bstrOSType)
3304 {
3305 pGuestOSType = *it;
3306 return S_OK;
3307 }
3308 }
3309
3310 return setError(VBOX_E_OBJECT_NOT_FOUND,
3311 tr("Guest OS type '%ls' is invalid"),
3312 bstrOSType.raw());
3313}
3314
3315const ComObjPtr<Host>& VirtualBox::host() const
3316{
3317 return m->pHost;
3318}
3319
3320const ComObjPtr<SystemProperties>& VirtualBox::systemProperties() const
3321{
3322 return m->pSystemProperties;
3323}
3324
3325#ifdef VBOX_WITH_RESOURCE_USAGE_API
3326const ComObjPtr<PerformanceCollector>& VirtualBox::performanceCollector() const
3327{
3328 return m->pPerformanceCollector;
3329}
3330#endif /* VBOX_WITH_RESOURCE_USAGE_API */
3331
3332/**
3333 * Returns the default machine folder from the system properties
3334 * with proper locking.
3335 * @return
3336 */
3337const Utf8Str& VirtualBox::getDefaultMachineFolder() const
3338{
3339 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
3340 return m->pSystemProperties->m_strDefaultMachineFolder;
3341}
3342
3343/**
3344 * Returns the default hard disk folder from the system properties
3345 * with proper locking.
3346 * @return
3347 */
3348const Utf8Str& VirtualBox::getDefaultHardDiskFolder() const
3349{
3350 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
3351 return m->pSystemProperties->m_strDefaultHardDiskFolder;
3352}
3353
3354/**
3355 * Returns the default hard disk format from the system properties
3356 * with proper locking.
3357 * @return
3358 */
3359const Utf8Str& VirtualBox::getDefaultHardDiskFormat() const
3360{
3361 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
3362 return m->pSystemProperties->m_strDefaultHardDiskFormat;
3363}
3364
3365const Utf8Str& VirtualBox::homeDir() const
3366{
3367 return m->strHomeDir;
3368}
3369
3370/**
3371 * Calculates the absolute path of the given path taking the VirtualBox home
3372 * directory as the current directory.
3373 *
3374 * @param aPath Path to calculate the absolute path for.
3375 * @param aResult Where to put the result (used only on success, can be the
3376 * same Utf8Str instance as passed in @a aPath).
3377 * @return IPRT result.
3378 *
3379 * @note Doesn't lock any object.
3380 */
3381int VirtualBox::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
3382{
3383 AutoCaller autoCaller(this);
3384 AssertComRCReturn(autoCaller.rc(), VERR_GENERAL_FAILURE);
3385
3386 /* no need to lock since mHomeDir is const */
3387
3388 char folder[RTPATH_MAX];
3389 int vrc = RTPathAbsEx(m->strHomeDir.c_str(), strPath.c_str(), folder, sizeof(folder));
3390 if (RT_SUCCESS(vrc))
3391 aResult = folder;
3392
3393 return vrc;
3394}
3395
3396/**
3397 * Tries to calculate the relative path of the given absolute path using the
3398 * directory of the VirtualBox settings file as the base directory.
3399 *
3400 * @param aPath Absolute path to calculate the relative path for.
3401 * @param aResult Where to put the result (used only when it's possible to
3402 * make a relative path from the given absolute path; otherwise
3403 * left untouched).
3404 *
3405 * @note Doesn't lock any object.
3406 */
3407void VirtualBox::calculateRelativePath(const Utf8Str &strPath, Utf8Str &aResult)
3408{
3409 AutoCaller autoCaller(this);
3410 AssertComRCReturnVoid(autoCaller.rc());
3411
3412 /* no need to lock since mHomeDir is const */
3413
3414 Utf8Str settingsDir = m->strHomeDir;
3415
3416 if (RTPathStartsWith(strPath.c_str(), settingsDir.c_str()))
3417 {
3418 /* when assigning, we create a separate Utf8Str instance because both
3419 * aPath and aResult can point to the same memory location when this
3420 * func is called (if we just do aResult = aPath, aResult will be freed
3421 * first, and since its the same as aPath, an attempt to copy garbage
3422 * will be made. */
3423 aResult = Utf8Str(strPath.c_str() + settingsDir.length() + 1);
3424 }
3425}
3426
3427// private methods
3428/////////////////////////////////////////////////////////////////////////////
3429
3430/**
3431 * Checks if there is a hard disk, DVD or floppy image with the given ID or
3432 * location already registered.
3433 *
3434 * On return, sets @a aConflict to the string describing the conflicting medium,
3435 * or sets it to @c Null if no conflicting media is found. Returns S_OK in
3436 * either case. A failure is unexpected.
3437 *
3438 * @param aId UUID to check.
3439 * @param aLocation Location to check.
3440 * @param aConflict Where to return parameters of the conflicting medium.
3441 *
3442 * @note Locks the media tree and media objects for reading.
3443 */
3444HRESULT VirtualBox::checkMediaForConflicts2(const Guid &aId,
3445 const Utf8Str &aLocation,
3446 Utf8Str &aConflict)
3447{
3448 aConflict.setNull();
3449
3450 AssertReturn(!aId.isEmpty() && !aLocation.isEmpty(), E_FAIL);
3451
3452 AutoReadLock alock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3453
3454 HRESULT rc = S_OK;
3455
3456 Bstr bstrLocation(aLocation);
3457
3458 {
3459 ComObjPtr<Medium> hardDisk;
3460 rc = findHardDisk(&aId, bstrLocation, false /* aSetError */, &hardDisk);
3461 if (SUCCEEDED(rc))
3462 {
3463 /* Note: no AutoCaller since bound to this */
3464 AutoReadLock mediaLock(hardDisk COMMA_LOCKVAL_SRC_POS);
3465 aConflict = Utf8StrFmt(tr("hard disk '%s' with UUID {%RTuuid}"),
3466 hardDisk->getLocationFull().raw(),
3467 hardDisk->getId().raw());
3468 return S_OK;
3469 }
3470 }
3471
3472 {
3473 ComObjPtr<Medium> image;
3474 rc = findDVDImage(&aId, bstrLocation, false /* aSetError */, &image);
3475 if (SUCCEEDED(rc))
3476 {
3477 /* Note: no AutoCaller since bound to this */
3478 AutoReadLock mediaLock(image COMMA_LOCKVAL_SRC_POS);
3479 aConflict = Utf8StrFmt(tr("CD/DVD image '%s' with UUID {%RTuuid}"),
3480 image->getLocationFull().raw(),
3481 image->getId().raw());
3482 return S_OK;
3483 }
3484 }
3485
3486 {
3487 ComObjPtr<Medium> image;
3488 rc = findFloppyImage(&aId, bstrLocation, false /* aSetError */, &image);
3489 if (SUCCEEDED(rc))
3490 {
3491 /* Note: no AutoCaller since bound to this */
3492 AutoReadLock mediaLock(image COMMA_LOCKVAL_SRC_POS);
3493 aConflict = Utf8StrFmt(tr("floppy image '%s' with UUID {%RTuuid}"),
3494 image->getLocationFull().raw(),
3495 image->getId().raw());
3496 return S_OK;
3497 }
3498 }
3499
3500 return S_OK;
3501}
3502
3503/**
3504 * Called from Machine::prepareSaveSettings() when it has detected
3505 * that a machine has been renamed. Such renames will require
3506 * updating the global media registry during the
3507 * VirtualBox::saveSettings() that follows later.
3508*
3509 * When a machine is renamed, there may well be media (in particular,
3510 * diff images for snapshots) in the global registry that will need
3511 * to have their paths updated. Before 3.2, Machine::saveSettings
3512 * used to call VirtualBox::saveSettings implicitly, which was both
3513 * unintuitive and caused locking order problems. Now, we remeber
3514 * such pending name changes with this method so that
3515 * VirtualBox::saveSettings() can process them properly.
3516 */
3517void VirtualBox::rememberMachineNameChangeForMedia(const Utf8Str &strOldConfigDir,
3518 const Utf8Str &strNewConfigDir)
3519{
3520 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3521
3522 Data::PendingMachineRename pmr;
3523 pmr.strConfigDirOld = strOldConfigDir;
3524 pmr.strConfigDirNew = strNewConfigDir;
3525 m->llPendingMachineRenames.push_back(pmr);
3526}
3527
3528/**
3529 * Helper function which actually writes out VirtualBox.xml, the main configuration file.
3530 * Gets called from the public VirtualBox::SaveSettings() as well as from various other
3531 * places internally when settings need saving.
3532 *
3533 * @note Caller must have locked the VirtualBox object for writing and must not hold any
3534 * other locks since this locks all kinds of member objects and trees temporarily,
3535 * which could cause conflicts.
3536 */
3537HRESULT VirtualBox::saveSettings()
3538{
3539 AutoCaller autoCaller(this);
3540 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3541
3542 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
3543 AssertReturn(!m->strSettingsFilePath.isEmpty(), E_FAIL);
3544
3545 HRESULT rc = S_OK;
3546
3547 try
3548 {
3549 // lock the lists while we're here
3550 AutoReadLock machinesLock(m->ollMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3551
3552 // machines
3553 settings::MachinesRegistry machinesTemp;
3554 {
3555 for (MachinesOList::iterator it = m->ollMachines.begin();
3556 it != m->ollMachines.end();
3557 ++it)
3558 {
3559 Machine *pMachine = *it;
3560 // save actual machine registry entry
3561 settings::MachineRegistryEntry mre;
3562 rc = pMachine->saveRegistryEntry(mre);
3563 machinesTemp.push_back(mre);
3564 }
3565 }
3566
3567 // lock all media for the following; use a write lock because we're
3568 // modifying the PendingMachineRenamesList, which is protected by this
3569 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3570
3571 // if a machine was renamed, then we'll need to refresh media paths
3572 if (m->llPendingMachineRenames.size())
3573 {
3574 // make a single list from the three media lists so we don't need three loops
3575 MediaList llAllMedia;
3576 // with hard disks, we must use the map, not the list, because the list only has base images
3577 for (HardDiskMap::iterator it = m->mapHardDisks.begin(); it != m->mapHardDisks.end(); ++it)
3578 llAllMedia.push_back(it->second);
3579 for (MediaList::iterator it = m->ollDVDImages.begin(); it != m->ollDVDImages.end(); ++it)
3580 llAllMedia.push_back(*it);
3581 for (MediaList::iterator it = m->ollFloppyImages.begin(); it != m->ollFloppyImages.end(); ++it)
3582 llAllMedia.push_back(*it);
3583
3584 for (MediaList::iterator it = llAllMedia.begin();
3585 it != llAllMedia.end();
3586 ++it)
3587 {
3588 Medium *pMedium = *it;
3589 for (Data::PendingMachineRenamesList::iterator it2 = m->llPendingMachineRenames.begin();
3590 it2 != m->llPendingMachineRenames.end();
3591 ++it2)
3592 {
3593 const Data::PendingMachineRename &pmr = *it2;
3594 const char *pcszOld = pmr.strConfigDirOld.c_str();
3595 const char *pcszNew = pmr.strConfigDirNew.c_str();
3596 pMedium->updatePath(pcszOld, pcszNew);
3597 }
3598 }
3599 // done, don't do it again until we have more machine renames
3600 m->llPendingMachineRenames.clear();
3601 }
3602
3603 // hard disks
3604 settings::MediaList hardDisksTemp;
3605 for (MediaList::const_iterator it = m->ollHardDisks.begin();
3606 it != m->ollHardDisks.end();
3607 ++it)
3608 {
3609 settings::Medium med;
3610 rc = (*it)->saveSettings(med); // this recurses into its children
3611 if (FAILED(rc)) throw rc;
3612 hardDisksTemp.push_back(med);
3613 }
3614
3615 /* CD/DVD images */
3616 settings::MediaList dvdsTemp;
3617 for (MediaList::const_iterator it = m->ollDVDImages.begin();
3618 it != m->ollDVDImages.end();
3619 ++it)
3620 {
3621 settings::Medium med;
3622 rc = (*it)->saveSettings(med);
3623 if (FAILED(rc)) throw rc;
3624 dvdsTemp.push_back(med);
3625 }
3626
3627 /* floppy images */
3628 settings::MediaList floppiesTemp;
3629 for (MediaList::const_iterator it = m->ollFloppyImages.begin();
3630 it != m->ollFloppyImages.end();
3631 ++it)
3632 {
3633 settings::Medium med;
3634 rc = (*it)->saveSettings(med);
3635 if (FAILED(rc)) throw rc;
3636 floppiesTemp.push_back(med);
3637 }
3638
3639 settings::DHCPServersList dhcpServersTemp;
3640 {
3641 AutoReadLock dhcpLock(m->ollDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3642 for (DHCPServersOList::const_iterator it = m->ollDHCPServers.begin();
3643 it != m->ollDHCPServers.end();
3644 ++it)
3645 {
3646 settings::DHCPServer d;
3647 rc = (*it)->saveSettings(d);
3648 if (FAILED(rc)) throw rc;
3649 dhcpServersTemp.push_back(d);
3650 }
3651 }
3652
3653 /* now copy the temp data to the config file under the VirtualBox lock */
3654 m->pMainConfigFile->llMachines = machinesTemp;
3655 m->pMainConfigFile->llHardDisks = hardDisksTemp;
3656 m->pMainConfigFile->llDvdImages = dvdsTemp;
3657 m->pMainConfigFile->llFloppyImages = floppiesTemp;
3658 m->pMainConfigFile->llDhcpServers = dhcpServersTemp;
3659
3660 // leave extra data alone, it's still in the config file
3661
3662 /* host data (USB filters) */
3663 rc = m->pHost->saveSettings(m->pMainConfigFile->host);
3664 if (FAILED(rc)) throw rc;
3665
3666 rc = m->pSystemProperties->saveSettings(m->pMainConfigFile->systemProperties);
3667 if (FAILED(rc)) throw rc;
3668
3669 // and write out the XML, still under the lock
3670 m->pMainConfigFile->write(m->strSettingsFilePath);
3671 }
3672 catch (HRESULT err)
3673 {
3674 /* we assume that error info is set by the thrower */
3675 rc = err;
3676 }
3677 catch (...)
3678 {
3679 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
3680 }
3681
3682 return rc;
3683}
3684
3685/**
3686 * Helper to register the machine.
3687 *
3688 * When called during VirtualBox startup, adds the given machine to the
3689 * collection of registered machines. Otherwise tries to mark the machine
3690 * as registered, and, if succeeded, adds it to the collection and
3691 * saves global settings.
3692 *
3693 * @note The caller must have added itself as a caller of the @a aMachine
3694 * object if calls this method not on VirtualBox startup.
3695 *
3696 * @param aMachine machine to register
3697 *
3698 * @note Locks objects!
3699 */
3700HRESULT VirtualBox::registerMachine(Machine *aMachine)
3701{
3702 ComAssertRet(aMachine, E_INVALIDARG);
3703
3704 AutoCaller autoCaller(this);
3705 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3706
3707 HRESULT rc = S_OK;
3708
3709 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3710
3711 {
3712 ComObjPtr<Machine> pMachine;
3713 rc = findMachine(aMachine->getId(), false /* aDoSetError */, &pMachine);
3714 if (SUCCEEDED(rc))
3715 {
3716 /* sanity */
3717 AutoLimitedCaller machCaller(pMachine);
3718 AssertComRC(machCaller.rc());
3719
3720 return setError(E_INVALIDARG,
3721 tr("Registered machine with UUID {%RTuuid} ('%ls') already exists"),
3722 aMachine->getId().raw(),
3723 pMachine->getSettingsFileFull().raw());
3724 }
3725
3726 ComAssertRet(rc == VBOX_E_OBJECT_NOT_FOUND, rc);
3727 rc = S_OK;
3728 }
3729
3730 if (autoCaller.state() != InInit)
3731 {
3732 rc = aMachine->trySetRegistered(TRUE);
3733 if (FAILED(rc)) return rc;
3734 }
3735
3736 /* add to the collection of registered machines */
3737 m->ollMachines.addChild(aMachine);
3738
3739 if (autoCaller.state() != InInit)
3740 rc = saveSettings();
3741
3742 return rc;
3743}
3744
3745/**
3746 * Remembers the given hard disk by storing it in the hard disk registry.
3747 *
3748 * @param aHardDisk Hard disk object to remember.
3749 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3750 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3751 *
3752 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3753 */
3754HRESULT VirtualBox::registerHardDisk(Medium *aHardDisk,
3755 bool *pfNeedsSaveSettings)
3756{
3757 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3758
3759 AutoCaller autoCaller(this);
3760 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3761
3762 AutoCaller hardDiskCaller(aHardDisk);
3763 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3764
3765 // caller must hold the media tree write lock
3766 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3767
3768 Guid id;
3769 Utf8Str strLocationFull;
3770 ComObjPtr<Medium> pParent;
3771 {
3772 AutoReadLock hardDiskLock(aHardDisk COMMA_LOCKVAL_SRC_POS);
3773 id = aHardDisk->getId();
3774 strLocationFull = aHardDisk->getLocationFull();
3775 pParent = aHardDisk->getParent();
3776 }
3777
3778 HRESULT rc;
3779
3780 Utf8Str strConflict;
3781 rc = checkMediaForConflicts2(id,
3782 strLocationFull,
3783 strConflict);
3784 if (FAILED(rc)) return rc;
3785
3786 if (strConflict.length())
3787 return setError(E_INVALIDARG,
3788 tr("Cannot register the hard disk '%s' with UUID {%RTuuid} because a %s already exists in the media registry ('%s')"),
3789 strLocationFull.raw(),
3790 id.raw(),
3791 strConflict.raw(),
3792 m->strSettingsFilePath.raw());
3793
3794 // store base (root) hard disks in the list
3795 if (pParent.isNull())
3796 m->ollHardDisks.getList().push_back(aHardDisk);
3797 // access the list directly because we already locked the list above
3798
3799 // store all hard disks (even differencing images) in the map
3800 m->mapHardDisks[id] = aHardDisk;
3801
3802 if (pfNeedsSaveSettings)
3803 *pfNeedsSaveSettings = true;
3804
3805 return rc;
3806}
3807
3808/**
3809 * Removes the given hard disk from the hard disk registry.
3810 *
3811 * @param aHardDisk Hard disk object to remove.
3812 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3813 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3814 *
3815 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3816 */
3817HRESULT VirtualBox::unregisterHardDisk(Medium *aHardDisk,
3818 bool *pfNeedsSaveSettings)
3819{
3820 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3821
3822 AutoCaller autoCaller(this);
3823 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3824
3825 AutoCaller hardDiskCaller(aHardDisk);
3826 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3827
3828 // caller must hold the media tree write lock
3829 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3830
3831 Guid id;
3832 ComObjPtr<Medium> pParent;
3833 {
3834 AutoReadLock hardDiskLock(aHardDisk COMMA_LOCKVAL_SRC_POS);
3835 id = aHardDisk->getId();
3836 pParent = aHardDisk->getParent();
3837 }
3838
3839 // remove base (root) hard disks from the list
3840 if (pParent.isNull())
3841 m->ollHardDisks.getList().remove(aHardDisk);
3842 // access the list directly because caller must have locked the list
3843
3844 // remove all hard disks (even differencing images) from map
3845 size_t cnt = m->mapHardDisks.erase(id);
3846 Assert(cnt == 1);
3847 NOREF(cnt);
3848
3849 if (pfNeedsSaveSettings)
3850 *pfNeedsSaveSettings = true;
3851
3852 return S_OK;
3853}
3854
3855/**
3856 * Remembers the given image by storing it in the CD/DVD or floppy image registry.
3857 *
3858 * @param argImage Image object to remember.
3859 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3860 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3861 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3862 *
3863 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3864 */
3865HRESULT VirtualBox::registerImage(Medium *argImage,
3866 DeviceType_T argType,
3867 bool *pfNeedsSaveSettings)
3868{
3869 AssertReturn(argImage != NULL, E_INVALIDARG);
3870
3871 AutoCaller autoCaller(this);
3872 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3873
3874 AutoCaller imageCaller(argImage);
3875 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3876
3877 // caller must hold the media tree write lock
3878 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3879
3880 Guid id;
3881 Utf8Str strLocationFull;
3882 ComObjPtr<Medium> pParent;
3883 {
3884 AutoReadLock al(argImage COMMA_LOCKVAL_SRC_POS);
3885 id = argImage->getId();
3886 strLocationFull = argImage->getLocationFull();
3887 pParent = argImage->getParent();
3888 }
3889
3890 // work on DVDs or floppies list?
3891 ObjectsList<Medium> &oll = (argType == DeviceType_DVD) ? m->ollDVDImages : m->ollFloppyImages;
3892
3893 HRESULT rc;
3894 // lock the images lists (list + map) while checking for conflicts
3895 AutoWriteLock al(oll.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3896
3897 Utf8Str strConflict;
3898 rc = checkMediaForConflicts2(id,
3899 strLocationFull,
3900 strConflict);
3901 if (FAILED(rc)) return rc;
3902
3903 if (strConflict.length())
3904 return setError(VBOX_E_INVALID_OBJECT_STATE,
3905 tr("Cannot register the image '%s' with UUID {%RTuuid} because a %s already exists in the media registry ('%s')"),
3906 strLocationFull.raw(),
3907 id.raw(),
3908 strConflict.raw(),
3909 m->strSettingsFilePath.raw());
3910
3911 // add to the collection
3912 oll.getList().push_back(argImage);
3913 // access the list directly because we already locked the list above
3914
3915 if (pfNeedsSaveSettings)
3916 *pfNeedsSaveSettings = true;
3917
3918 return rc;
3919}
3920
3921/**
3922 * Removes the given image from the CD/DVD or floppy image registry.
3923 *
3924 * @param argImage Image object to remove.
3925 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3926 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3927 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3928 *
3929 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3930 */
3931HRESULT VirtualBox::unregisterImage(Medium *argImage,
3932 DeviceType_T argType,
3933 bool *pfNeedsSaveSettings)
3934{
3935 AssertReturn(argImage != NULL, E_INVALIDARG);
3936
3937 AutoCaller autoCaller(this);
3938 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3939
3940 AutoCaller imageCaller(argImage);
3941 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3942
3943 // caller must hold the media tree write lock
3944 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3945
3946 Guid id;
3947 ComObjPtr<Medium> pParent;
3948 {
3949 AutoReadLock al(argImage COMMA_LOCKVAL_SRC_POS);
3950 id = argImage->getId();
3951 pParent = argImage->getParent();
3952 }
3953
3954 // work on DVDs or floppies list?
3955 ObjectsList<Medium> &oll = (argType == DeviceType_DVD) ? m->ollDVDImages : m->ollFloppyImages;
3956
3957 // access the list directly because the caller must have requested the lock
3958 oll.getList().remove(argImage);
3959
3960 HRESULT rc = S_OK;
3961
3962 if (pfNeedsSaveSettings)
3963 *pfNeedsSaveSettings = true;
3964
3965 return rc;
3966}
3967
3968/**
3969 * Creates the path to the specified file according to the path information
3970 * present in the file name.
3971 *
3972 * Note that the given file name must contain the full path otherwise the
3973 * extracted relative path will be created based on the current working
3974 * directory which is normally unknown.
3975 *
3976 * @param aFileName Full file name which path needs to be created.
3977 *
3978 * @return Extended error information on failure to create the path.
3979 */
3980/* static */
3981HRESULT VirtualBox::ensureFilePathExists(const Utf8Str &strFileName)
3982{
3983 Utf8Str strDir(strFileName);
3984 strDir.stripFilename();
3985 if (!RTDirExists(strDir.c_str()))
3986 {
3987 int vrc = RTDirCreateFullPath(strDir.c_str(), 0777);
3988 if (RT_FAILURE(vrc))
3989 return setError(E_FAIL,
3990 tr("Could not create the directory '%s' (%Rrc)"),
3991 strDir.c_str(),
3992 vrc);
3993 }
3994
3995 return S_OK;
3996}
3997
3998/**
3999 * Handles unexpected exceptions by turning them into COM errors in release
4000 * builds or by hitting a breakpoint in the release builds.
4001 *
4002 * Usage pattern:
4003 * @code
4004 try
4005 {
4006 // ...
4007 }
4008 catch (LaLalA)
4009 {
4010 // ...
4011 }
4012 catch (...)
4013 {
4014 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
4015 }
4016 * @endcode
4017 *
4018 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
4019 */
4020/* static */
4021HRESULT VirtualBox::handleUnexpectedExceptions(RT_SRC_POS_DECL)
4022{
4023 try
4024 {
4025 /* re-throw the current exception */
4026 throw;
4027 }
4028 catch (const xml::Error &err)
4029 {
4030 return setError(E_FAIL, tr("%s.\n%s[%d] (%s)"),
4031 err.what(),
4032 pszFile, iLine, pszFunction);
4033 }
4034 catch (const std::exception &err)
4035 {
4036 return setError(E_FAIL, tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
4037 err.what(), typeid(err).name(),
4038 pszFile, iLine, pszFunction);
4039 }
4040 catch (...)
4041 {
4042 return setError(E_FAIL, tr("Unknown exception\n%s[%d] (%s)"),
4043 pszFile, iLine, pszFunction);
4044 }
4045
4046 /* should not get here */
4047 AssertFailed();
4048 return E_FAIL;
4049}
4050
4051const Utf8Str& VirtualBox::settingsFilePath()
4052{
4053 return m->strSettingsFilePath;
4054}
4055
4056/**
4057 * Returns the lock handle which protects the media trees (hard disks,
4058 * DVDs, floppies). As opposed to version 3.1 and earlier, these lists
4059 * are no longer protected by the VirtualBox lock, but by this more
4060 * specialized lock. Mind the locking order: always request this lock
4061 * after the VirtualBox object lock but before the locks of the media
4062 * objects contained in these lists. See AutoLock.h.
4063 */
4064RWLockHandle& VirtualBox::getMediaTreeLockHandle()
4065{
4066 return m->lockMedia;
4067}
4068
4069/**
4070 * Thread function that watches the termination of all client processes
4071 * that have opened sessions using IVirtualBox::OpenSession()
4072 */
4073// static
4074DECLCALLBACK(int) VirtualBox::ClientWatcher(RTTHREAD /* thread */, void *pvUser)
4075{
4076 LogFlowFuncEnter();
4077
4078 VirtualBox *that = (VirtualBox*)pvUser;
4079 Assert(that);
4080
4081 typedef std::vector< ComObjPtr<Machine> > MachineVector;
4082 typedef std::vector< ComObjPtr<SessionMachine> > SessionMachineVector;
4083
4084 SessionMachineVector machines;
4085 MachineVector spawnedMachines;
4086
4087 size_t cnt = 0;
4088 size_t cntSpawned = 0;
4089
4090#if defined(RT_OS_WINDOWS)
4091
4092 HRESULT hrc = CoInitializeEx(NULL,
4093 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
4094 COINIT_SPEED_OVER_MEMORY);
4095 AssertComRC(hrc);
4096
4097 /// @todo (dmik) processes reaping!
4098
4099 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
4100 handles[0] = that->m->updateReq;
4101
4102 do
4103 {
4104 AutoCaller autoCaller(that);
4105 /* VirtualBox has been early uninitialized, terminate */
4106 if (!autoCaller.isOk())
4107 break;
4108
4109 do
4110 {
4111 /* release the caller to let uninit() ever proceed */
4112 autoCaller.release();
4113
4114 DWORD rc = ::WaitForMultipleObjects((DWORD)(1 + cnt + cntSpawned),
4115 handles,
4116 FALSE,
4117 INFINITE);
4118
4119 /* Restore the caller before using VirtualBox. If it fails, this
4120 * means VirtualBox is being uninitialized and we must terminate. */
4121 autoCaller.add();
4122 if (!autoCaller.isOk())
4123 break;
4124
4125 bool update = false;
4126
4127 if (rc == WAIT_OBJECT_0)
4128 {
4129 /* update event is signaled */
4130 update = true;
4131 }
4132 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
4133 {
4134 /* machine mutex is released */
4135 (machines[rc - WAIT_OBJECT_0 - 1])->checkForDeath();
4136 update = true;
4137 }
4138 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
4139 {
4140 /* machine mutex is abandoned due to client process termination */
4141 (machines[rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
4142 update = true;
4143 }
4144 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
4145 {
4146 /* spawned VM process has terminated (normally or abnormally) */
4147 (spawnedMachines[rc - WAIT_OBJECT_0 - cnt - 1])->
4148 checkForSpawnFailure();
4149 update = true;
4150 }
4151
4152 if (update)
4153 {
4154 /* close old process handles */
4155 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i)
4156 CloseHandle(handles[i]);
4157
4158 // lock the machines list for reading
4159 AutoReadLock thatLock(that->m->ollMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4160
4161 /* obtain a new set of opened machines */
4162 cnt = 0;
4163 machines.clear();
4164
4165 for (MachinesOList::iterator it = that->m->ollMachines.begin();
4166 it != that->m->ollMachines.end();
4167 ++it)
4168 {
4169 /// @todo handle situations with more than 64 objects
4170 AssertMsgBreak((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
4171 ("MAXIMUM_WAIT_OBJECTS reached"));
4172
4173 ComObjPtr<SessionMachine> sm;
4174 HANDLE ipcSem;
4175 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
4176 {
4177 machines.push_back(sm);
4178 handles[1 + cnt] = ipcSem;
4179 ++cnt;
4180 }
4181 }
4182
4183 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4184
4185 /* obtain a new set of spawned machines */
4186 cntSpawned = 0;
4187 spawnedMachines.clear();
4188
4189 for (MachinesOList::iterator it = that->m->ollMachines.begin();
4190 it != that->m->ollMachines.end();
4191 ++it)
4192 {
4193 /// @todo handle situations with more than 64 objects
4194 AssertMsgBreak((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
4195 ("MAXIMUM_WAIT_OBJECTS reached"));
4196
4197 RTPROCESS pid;
4198 if ((*it)->isSessionSpawning(&pid))
4199 {
4200 HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid);
4201 AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
4202 pid, GetLastError()));
4203 if (rc == 0)
4204 {
4205 spawnedMachines.push_back(*it);
4206 handles[1 + cnt + cntSpawned] = ph;
4207 ++cntSpawned;
4208 }
4209 }
4210 }
4211
4212 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4213
4214 // machines lock unwinds here
4215 }
4216 }
4217 while (true);
4218 }
4219 while (0);
4220
4221 /* close old process handles */
4222 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
4223 CloseHandle(handles[i]);
4224
4225 /* release sets of machines if any */
4226 machines.clear();
4227 spawnedMachines.clear();
4228
4229 ::CoUninitialize();
4230
4231#elif defined(RT_OS_OS2)
4232
4233 /// @todo (dmik) processes reaping!
4234
4235 /* according to PMREF, 64 is the maximum for the muxwait list */
4236 SEMRECORD handles[64];
4237
4238 HMUX muxSem = NULLHANDLE;
4239
4240 do
4241 {
4242 AutoCaller autoCaller(that);
4243 /* VirtualBox has been early uninitialized, terminate */
4244 if (!autoCaller.isOk())
4245 break;
4246
4247 do
4248 {
4249 /* release the caller to let uninit() ever proceed */
4250 autoCaller.release();
4251
4252 int vrc = RTSemEventWait(that->m->updateReq, 500);
4253
4254 /* Restore the caller before using VirtualBox. If it fails, this
4255 * means VirtualBox is being uninitialized and we must terminate. */
4256 autoCaller.add();
4257 if (!autoCaller.isOk())
4258 break;
4259
4260 bool update = false;
4261 bool updateSpawned = false;
4262
4263 if (RT_SUCCESS(vrc))
4264 {
4265 /* update event is signaled */
4266 update = true;
4267 updateSpawned = true;
4268 }
4269 else
4270 {
4271 AssertMsg(vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
4272 ("RTSemEventWait returned %Rrc\n", vrc));
4273
4274 /* are there any mutexes? */
4275 if (cnt > 0)
4276 {
4277 /* figure out what's going on with machines */
4278
4279 unsigned long semId = 0;
4280 APIRET arc = ::DosWaitMuxWaitSem(muxSem,
4281 SEM_IMMEDIATE_RETURN, &semId);
4282
4283 if (arc == NO_ERROR)
4284 {
4285 /* machine mutex is normally released */
4286 Assert(semId >= 0 && semId < cnt);
4287 if (semId >= 0 && semId < cnt)
4288 {
4289#ifdef DEBUG
4290 {
4291 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
4292 LogFlowFunc(("released mutex: machine='%ls'\n",
4293 machines[semId]->name().raw()));
4294 }
4295#endif
4296 machines[semId]->checkForDeath();
4297 }
4298 update = true;
4299 }
4300 else if (arc == ERROR_SEM_OWNER_DIED)
4301 {
4302 /* machine mutex is abandoned due to client process
4303 * termination; find which mutex is in the Owner Died
4304 * state */
4305 for (size_t i = 0; i < cnt; ++ i)
4306 {
4307 PID pid; TID tid;
4308 unsigned long reqCnt;
4309 arc = DosQueryMutexSem((HMTX)handles[i].hsemCur, &pid, &tid, &reqCnt);
4310 if (arc == ERROR_SEM_OWNER_DIED)
4311 {
4312 /* close the dead mutex as asked by PMREF */
4313 ::DosCloseMutexSem((HMTX)handles[i].hsemCur);
4314
4315 Assert(i >= 0 && i < cnt);
4316 if (i >= 0 && i < cnt)
4317 {
4318#ifdef DEBUG
4319 {
4320 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
4321 LogFlowFunc(("mutex owner dead: machine='%ls'\n",
4322 machines[i]->name().raw()));
4323 }
4324#endif
4325 machines[i]->checkForDeath();
4326 }
4327 }
4328 }
4329 update = true;
4330 }
4331 else
4332 AssertMsg(arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
4333 ("DosWaitMuxWaitSem returned %d\n", arc));
4334 }
4335
4336 /* are there any spawning sessions? */
4337 if (cntSpawned > 0)
4338 {
4339 for (size_t i = 0; i < cntSpawned; ++ i)
4340 updateSpawned |= (spawnedMachines[i])->
4341 checkForSpawnFailure();
4342 }
4343 }
4344
4345 if (update || updateSpawned)
4346 {
4347 AutoReadLock thatLock(that COMMA_LOCKVAL_SRC_POS);
4348
4349 if (update)
4350 {
4351 /* close the old muxsem */
4352 if (muxSem != NULLHANDLE)
4353 ::DosCloseMuxWaitSem(muxSem);
4354
4355 /* obtain a new set of opened machines */
4356 cnt = 0;
4357 machines.clear();
4358
4359 for (MachinesOList::iterator it = that->m->llMachines.begin();
4360 it != that->m->llMachines.end(); ++ it)
4361 {
4362 /// @todo handle situations with more than 64 objects
4363 AssertMsg(cnt <= 64 /* according to PMREF */,
4364 ("maximum of 64 mutex semaphores reached (%d)",
4365 cnt));
4366
4367 ComObjPtr<SessionMachine> sm;
4368 HMTX ipcSem;
4369 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
4370 {
4371 machines.push_back(sm);
4372 handles[cnt].hsemCur = (HSEM)ipcSem;
4373 handles[cnt].ulUser = cnt;
4374 ++ cnt;
4375 }
4376 }
4377
4378 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4379
4380 if (cnt > 0)
4381 {
4382 /* create a new muxsem */
4383 APIRET arc = ::DosCreateMuxWaitSem(NULL, &muxSem, cnt,
4384 handles,
4385 DCMW_WAIT_ANY);
4386 AssertMsg(arc == NO_ERROR,
4387 ("DosCreateMuxWaitSem returned %d\n", arc));
4388 NOREF(arc);
4389 }
4390 }
4391
4392 if (updateSpawned)
4393 {
4394 /* obtain a new set of spawned machines */
4395 spawnedMachines.clear();
4396
4397 for (MachinesOList::iterator it = that->m->llMachines.begin();
4398 it != that->m->llMachines.end(); ++ it)
4399 {
4400 if ((*it)->isSessionSpawning())
4401 spawnedMachines.push_back(*it);
4402 }
4403
4404 cntSpawned = spawnedMachines.size();
4405 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4406 }
4407 }
4408 }
4409 while (true);
4410 }
4411 while (0);
4412
4413 /* close the muxsem */
4414 if (muxSem != NULLHANDLE)
4415 ::DosCloseMuxWaitSem(muxSem);
4416
4417 /* release sets of machines if any */
4418 machines.clear();
4419 spawnedMachines.clear();
4420
4421#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
4422
4423 bool update = false;
4424 bool updateSpawned = false;
4425
4426 do
4427 {
4428 AutoCaller autoCaller(that);
4429 if (!autoCaller.isOk())
4430 break;
4431
4432 do
4433 {
4434 /* release the caller to let uninit() ever proceed */
4435 autoCaller.release();
4436
4437 int rc = RTSemEventWait(that->m->updateReq, 500);
4438
4439 /*
4440 * Restore the caller before using VirtualBox. If it fails, this
4441 * means VirtualBox is being uninitialized and we must terminate.
4442 */
4443 autoCaller.add();
4444 if (!autoCaller.isOk())
4445 break;
4446
4447 if (RT_SUCCESS(rc) || update || updateSpawned)
4448 {
4449 /* RT_SUCCESS(rc) means an update event is signaled */
4450
4451 // lock the machines list for reading
4452 AutoReadLock thatLock(that->m->ollMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4453
4454 if (RT_SUCCESS(rc) || update)
4455 {
4456 /* obtain a new set of opened machines */
4457 machines.clear();
4458
4459 for (MachinesOList::iterator it = that->m->ollMachines.begin();
4460 it != that->m->ollMachines.end();
4461 ++it)
4462 {
4463 ComObjPtr<SessionMachine> sm;
4464 if ((*it)->isSessionOpenOrClosing(sm))
4465 machines.push_back(sm);
4466 }
4467
4468 cnt = machines.size();
4469 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4470 }
4471
4472 if (RT_SUCCESS(rc) || updateSpawned)
4473 {
4474 /* obtain a new set of spawned machines */
4475 spawnedMachines.clear();
4476
4477 for (MachinesOList::iterator it = that->m->ollMachines.begin();
4478 it != that->m->ollMachines.end();
4479 ++it)
4480 {
4481 if ((*it)->isSessionSpawning())
4482 spawnedMachines.push_back(*it);
4483 }
4484
4485 cntSpawned = spawnedMachines.size();
4486 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4487 }
4488
4489 // machines lock unwinds here
4490 }
4491
4492 update = false;
4493 for (size_t i = 0; i < cnt; ++ i)
4494 update |= (machines[i])->checkForDeath();
4495
4496 updateSpawned = false;
4497 for (size_t i = 0; i < cntSpawned; ++ i)
4498 updateSpawned |= (spawnedMachines[i])->checkForSpawnFailure();
4499
4500 /* reap child processes */
4501 {
4502 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
4503 if (that->m->llProcesses.size())
4504 {
4505 LogFlowFunc(("UPDATE: child process count = %d\n",
4506 that->m->llProcesses.size()));
4507 VirtualBox::Data::ProcessList::iterator it = that->m->llProcesses.begin();
4508 while (it != that->m->llProcesses.end())
4509 {
4510 RTPROCESS pid = *it;
4511 RTPROCSTATUS status;
4512 int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status);
4513 if (vrc == VINF_SUCCESS)
4514 {
4515 LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n",
4516 pid, pid, status.iStatus,
4517 status.enmReason));
4518 it = that->m->llProcesses.erase(it);
4519 }
4520 else
4521 {
4522 LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
4523 pid, pid, vrc));
4524 if (vrc != VERR_PROCESS_RUNNING)
4525 {
4526 /* remove the process if it is not already running */
4527 it = that->m->llProcesses.erase(it);
4528 }
4529 else
4530 ++ it;
4531 }
4532 }
4533 }
4534 }
4535 }
4536 while (true);
4537 }
4538 while (0);
4539
4540 /* release sets of machines if any */
4541 machines.clear();
4542 spawnedMachines.clear();
4543
4544#else
4545# error "Port me!"
4546#endif
4547
4548 LogFlowFuncLeave();
4549 return 0;
4550}
4551
4552/**
4553 * Thread function that handles custom events posted using #postEvent().
4554 */
4555// static
4556DECLCALLBACK(int) VirtualBox::AsyncEventHandler(RTTHREAD thread, void *pvUser)
4557{
4558 LogFlowFuncEnter();
4559
4560 AssertReturn(pvUser, VERR_INVALID_POINTER);
4561
4562 // create an event queue for the current thread
4563 EventQueue *eventQ = new EventQueue();
4564 AssertReturn(eventQ, VERR_NO_MEMORY);
4565
4566 // return the queue to the one who created this thread
4567 *(static_cast <EventQueue **>(pvUser)) = eventQ;
4568 // signal that we're ready
4569 RTThreadUserSignal(thread);
4570
4571 BOOL ok = TRUE;
4572 Event *event = NULL;
4573
4574 while ((ok = eventQ->waitForEvent(&event)) && event)
4575 eventQ->handleEvent(event);
4576
4577 AssertReturn(ok, VERR_GENERAL_FAILURE);
4578
4579 delete eventQ;
4580
4581 LogFlowFuncLeave();
4582
4583 return 0;
4584}
4585
4586
4587////////////////////////////////////////////////////////////////////////////////
4588
4589/**
4590 * Takes the current list of registered callbacks of the managed VirtualBox
4591 * instance, and calls #handleCallback() for every callback item from the
4592 * list, passing the item as an argument.
4593 *
4594 * @note Locks the managed VirtualBox object for reading but leaves the lock
4595 * before iterating over callbacks and calling their methods.
4596 */
4597void *VirtualBox::CallbackEvent::handler()
4598{
4599 if (!mVirtualBox)
4600 return NULL;
4601
4602 AutoCaller autoCaller(mVirtualBox);
4603 if (!autoCaller.isOk())
4604 {
4605 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",
4606 autoCaller.state()));
4607 /* We don't need mVirtualBox any more, so release it */
4608 mVirtualBox = NULL;
4609 return NULL;
4610 }
4611
4612 CallbackList callbacks;
4613#ifdef RT_OS_WINDOWS
4614 EventListenersList listeners;
4615#endif
4616 {
4617 /* Make a copy to release the lock before iterating */
4618 AutoReadLock alock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
4619 callbacks = mVirtualBox->m->llCallbacks;
4620#ifdef RT_OS_WINDOWS
4621 IUnknown** pp;
4622 for (pp = mVirtualBox->m_vec.begin(); pp < mVirtualBox->m_vec.end(); pp++)
4623 {
4624 listeners.Add(*pp);
4625 }
4626#endif
4627 }
4628
4629
4630#ifdef RT_OS_WINDOWS
4631 // WIP
4632 {
4633 ComEventDesc evDesc;
4634
4635 int nConnections = listeners.GetSize();
4636 /* Only prepare args if someone really needs them */
4637 if (nConnections)
4638 prepareEventDesc(evDesc);
4639
4640 for (int i=0; i<nConnections; i++)
4641 {
4642 ComPtr<IUnknown> sp = listeners.GetAt(i);
4643 ComPtr<IVirtualBoxCallback> cbI;
4644 ComPtr<IDispatch> cbD;
4645
4646 cbI = sp;
4647 cbD = sp;
4648
4649 /**
4650 * Would be just handleCallback(cbI) in an ideal world, unfortunately our
4651 * consumers want to be invoked via IDispatch, thus going the hard way.
4652 */
4653 if (cbI != NULL && cbD != NULL)
4654 {
4655 CComVariant varResult;
4656 mVirtualBox->m->mComEvHelper.fire(cbD, evDesc, &varResult);
4657 // what we gonna do with the result?
4658 }
4659 }
4660 }
4661#endif
4662
4663 for (CallbackList::const_iterator it = callbacks.begin();
4664 it != callbacks.end();
4665 ++it)
4666 {
4667 if (it->isWanted(mWhat))
4668 {
4669 HRESULT hrc = handleCallback(it->ptrICb);
4670 if (hrc == VBOX_E_DONT_CALL_AGAIN)
4671 {
4672 /* Have to update the original. */
4673 AutoReadLock alock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
4674 CallbackList::iterator itOrg;
4675 itOrg = std::find(mVirtualBox->m->llCallbacks.begin(),
4676 mVirtualBox->m->llCallbacks.end(),
4677 CallbackList::value_type(it->ptrICb));
4678 if (itOrg != mVirtualBox->m->llCallbacks.end())
4679 itOrg->setDontCallAgain(mWhat);
4680 }
4681 else if (FAILED_DEAD_INTERFACE(hrc))
4682 mVirtualBox->removeDeadCallback(it->ptrICb);
4683 }
4684 }
4685
4686 mVirtualBox = NULL; /* Not needed any longer. Still make sense to do this? */
4687 return NULL;
4688}
4689
4690//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface(/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
4691//{
4692// return E_NOTIMPL;
4693//}
4694
4695STDMETHODIMP VirtualBox::CreateDHCPServer(IN_BSTR aName, IDHCPServer ** aServer)
4696{
4697 CheckComArgStrNotEmptyOrNull(aName);
4698 CheckComArgNotNull(aServer);
4699
4700 AutoCaller autoCaller(this);
4701 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4702
4703 ComObjPtr<DHCPServer> dhcpServer;
4704 dhcpServer.createObject();
4705 HRESULT rc = dhcpServer->init(this, aName);
4706 if (FAILED(rc)) return rc;
4707
4708 rc = registerDHCPServer(dhcpServer, true);
4709 if (FAILED(rc)) return rc;
4710
4711 dhcpServer.queryInterfaceTo(aServer);
4712
4713 return rc;
4714}
4715
4716STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName(IN_BSTR aName, IDHCPServer ** aServer)
4717{
4718 CheckComArgStrNotEmptyOrNull(aName);
4719 CheckComArgNotNull(aServer);
4720
4721 AutoCaller autoCaller(this);
4722 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4723
4724 HRESULT rc;
4725 Bstr bstr;
4726 ComPtr<DHCPServer> found;
4727
4728 AutoReadLock alock(m->ollDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4729
4730 for (DHCPServersOList::const_iterator it = m->ollDHCPServers.begin();
4731 it != m->ollDHCPServers.end();
4732 ++it)
4733 {
4734 rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());
4735 if (FAILED(rc)) throw rc;
4736
4737 if (bstr == aName)
4738 {
4739 found = *it;
4740 break;
4741 }
4742 }
4743
4744 if (!found)
4745 return E_INVALIDARG;
4746
4747 return found.queryInterfaceTo(aServer);
4748}
4749
4750STDMETHODIMP VirtualBox::RemoveDHCPServer(IDHCPServer * aServer)
4751{
4752 CheckComArgNotNull(aServer);
4753
4754 AutoCaller autoCaller(this);
4755 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4756
4757 HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);
4758
4759 return rc;
4760}
4761
4762/**
4763 * Remembers the given dhcp server by storing it in the hard disk registry.
4764 *
4765 * @param aDHCPServer Dhcp Server object to remember.
4766 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4767 *
4768 * When @a aSaveRegistry is @c true, this operation may fail because of the
4769 * failed #saveSettings() method it calls. In this case, the dhcp server object
4770 * will not be remembered. It is therefore the responsibility of the caller to
4771 * call this method as the last step of some action that requires registration
4772 * in order to make sure that only fully functional dhcp server objects get
4773 * registered.
4774 *
4775 * @note Locks this object for writing and @a aDHCPServer for reading.
4776 */
4777HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,
4778 bool aSaveRegistry /*= true*/)
4779{
4780 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4781
4782 AutoCaller autoCaller(this);
4783 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4784
4785 AutoCaller dhcpServerCaller(aDHCPServer);
4786 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4787
4788 Bstr name;
4789 HRESULT rc;
4790 rc = aDHCPServer->COMGETTER(NetworkName)(name.asOutParam());
4791 if (FAILED(rc)) return rc;
4792
4793 ComPtr<IDHCPServer> existing;
4794 rc = FindDHCPServerByNetworkName(name, existing.asOutParam());
4795 if (SUCCEEDED(rc))
4796 return E_INVALIDARG;
4797
4798 rc = S_OK;
4799
4800 m->ollDHCPServers.addChild(aDHCPServer);
4801
4802 if (aSaveRegistry)
4803 {
4804 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4805 rc = saveSettings();
4806 vboxLock.release();
4807
4808 if (FAILED(rc))
4809 unregisterDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4810 }
4811
4812 return rc;
4813}
4814
4815/**
4816 * Removes the given hard disk from the hard disk registry.
4817 *
4818 * @param aHardDisk Hard disk object to remove.
4819 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4820 *
4821 * When @a aSaveRegistry is @c true, this operation may fail because of the
4822 * failed #saveSettings() method it calls. In this case, the hard disk object
4823 * will NOT be removed from the registry when this method returns. It is
4824 * therefore the responsibility of the caller to call this method as the first
4825 * step of some action that requires unregistration, before calling uninit() on
4826 * @a aHardDisk.
4827 *
4828 * @note Locks this object for writing and @a aHardDisk for reading.
4829 */
4830HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,
4831 bool aSaveRegistry /*= true*/)
4832{
4833 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4834
4835 AutoCaller autoCaller(this);
4836 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4837
4838 AutoCaller dhcpServerCaller(aDHCPServer);
4839 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4840
4841 m->ollDHCPServers.removeChild(aDHCPServer);
4842
4843 HRESULT rc = S_OK;
4844
4845 if (aSaveRegistry)
4846 {
4847 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4848 rc = saveSettings();
4849 vboxLock.release();
4850
4851 if (FAILED(rc))
4852 registerDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4853 }
4854
4855 return rc;
4856}
4857
4858/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette