VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/VirtualBoxImpl.cpp@ 37540

Last change on this file since 37540 was 37525, checked in by vboxsync, 14 years ago

Main/VirtualBox+Medium: resurrect the feature of changing the medium UUID when opening the image, which allows to resolve duplicate UUIDs without using external tools. Also fixes Medium::setIDs, which wasn't correctly working.
Frontends/*: corresponding changes.

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

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