VirtualBox

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

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

VBox/com/defs.h,VirtualBoxImpl.cpp: Implemented FAILED_DEAD_INTERFACE() for COM.

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

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