VirtualBox

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

Last change on this file since 37423 was 37244, checked in by vboxsync, 14 years ago

Main/VirtualBox: new method for querying the API version

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 148.3 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 37244 2011-05-30 08:28:07Z 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 IMedium **aMedium)
1485{
1486 CheckComArgStrNotEmptyOrNull(aLocation);
1487 CheckComArgOutSafeArrayPointerValid(aMedium);
1488
1489 AutoCaller autoCaller(this);
1490 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1491
1492 ComObjPtr<Medium> pMedium;
1493
1494 /* we don't access non-const data members so no need to lock */
1495
1496 // check if the device type is correct, and see if a medium for the
1497 // given path has already initialized; if so, return that
1498 switch (deviceType)
1499 {
1500 case DeviceType_HardDisk:
1501 findHardDiskByLocation(aLocation,
1502 false, /* aSetError */
1503 &pMedium);
1504 break;
1505
1506 case DeviceType_Floppy:
1507 case DeviceType_DVD:
1508 findDVDOrFloppyImage(deviceType,
1509 NULL, /* guid */
1510 aLocation,
1511 false, /* aSetError */
1512 &pMedium);
1513
1514 // enforce read-only for DVDs even if caller specified ReadWrite
1515 if (deviceType == DeviceType_DVD)
1516 accessMode = AccessMode_ReadOnly;
1517 break;
1518
1519 default:
1520 return setError(E_INVALIDARG, "Device type must be HardDisk, DVD or Floppy");
1521 }
1522
1523 HRESULT rc = S_OK;
1524
1525 if (pMedium.isNull())
1526 {
1527 pMedium.createObject();
1528 rc = pMedium->init(this,
1529 aLocation,
1530 (accessMode == AccessMode_ReadWrite) ? Medium::OpenReadWrite : Medium::OpenReadOnly,
1531 deviceType);
1532
1533 if (SUCCEEDED(rc))
1534 {
1535 AutoWriteLock treeLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1536
1537 switch (deviceType)
1538 {
1539 case DeviceType_HardDisk:
1540 rc = registerHardDisk(pMedium, NULL /* pllRegistriesThatNeedSaving */);
1541 break;
1542
1543 case DeviceType_DVD:
1544 case DeviceType_Floppy:
1545 rc = registerImage(pMedium,
1546 deviceType,
1547 NULL /* pllRegistriesThatNeedSaving */);
1548 break;
1549 }
1550
1551 treeLock.release();
1552
1553 /* Note that it's important to call uninit() on failure to register
1554 * because the differencing hard disk would have been already associated
1555 * with the parent and this association needs to be broken. */
1556
1557 if (FAILED(rc))
1558 pMedium->uninit();
1559 }
1560 }
1561
1562 if (SUCCEEDED(rc))
1563 pMedium.queryInterfaceTo(aMedium);
1564
1565 return rc;
1566}
1567
1568STDMETHODIMP VirtualBox::FindMedium(IN_BSTR aLocation,
1569 DeviceType_T aDeviceType,
1570 IMedium **aMedium)
1571{
1572 CheckComArgStrNotEmptyOrNull(aLocation);
1573 CheckComArgOutSafeArrayPointerValid(aMedium);
1574
1575 AutoCaller autoCaller(this);
1576 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1577
1578 Guid id(aLocation);
1579 Utf8Str strLocation(aLocation);
1580
1581 HRESULT rc;
1582 ComObjPtr<Medium> pMedium;
1583
1584 switch (aDeviceType)
1585 {
1586 case DeviceType_HardDisk:
1587 if (!id.isEmpty())
1588 rc = findHardDiskById(id, true /* setError */, &pMedium);
1589 else
1590 rc = findHardDiskByLocation(strLocation, true /* setError */, &pMedium);
1591 break;
1592
1593 case DeviceType_Floppy:
1594 case DeviceType_DVD:
1595 if (!id.isEmpty())
1596 rc = findDVDOrFloppyImage(aDeviceType, &id, Utf8Str::Empty, true /* setError */, &pMedium);
1597 else
1598 rc = findDVDOrFloppyImage(aDeviceType, NULL, strLocation, true /* setError */, &pMedium);
1599 break;
1600
1601 default:
1602 return setError(E_INVALIDARG,
1603 tr("Invalid device type %d"), aDeviceType);
1604 }
1605
1606 /* the below will set *aHardDisk to NULL if hardDisk is null */
1607 pMedium.queryInterfaceTo(aMedium);
1608
1609 return rc;
1610}
1611
1612/** @note Locks this object for reading. */
1613STDMETHODIMP VirtualBox::GetGuestOSType(IN_BSTR aId, IGuestOSType **aType)
1614{
1615 /* Old ID to new ID conversion table. See r39691 for a source */
1616 static const wchar_t *kOldNewIDs[] =
1617 {
1618 L"unknown", L"Other",
1619 L"win31", L"Windows31",
1620 L"win95", L"Windows95",
1621 L"win98", L"Windows98",
1622 L"winme", L"WindowsMe",
1623 L"winnt4", L"WindowsNT4",
1624 L"win2k", L"Windows2000",
1625 L"winxp", L"WindowsXP",
1626 L"win2k3", L"Windows2003",
1627 L"winvista", L"WindowsVista",
1628 L"win2k8", L"Windows2008",
1629 L"ecs", L"OS2eCS",
1630 L"fedoracore", L"Fedora",
1631 /* the rest is covered by the case-insensitive comparison */
1632 };
1633
1634 CheckComArgNotNull(aType);
1635
1636 AutoCaller autoCaller(this);
1637 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1638
1639 /* first, look for a substitution */
1640 Bstr id = aId;
1641 for (size_t i = 0; i < RT_ELEMENTS(kOldNewIDs) / 2; i += 2)
1642 {
1643 if (id == kOldNewIDs[i])
1644 {
1645 id = kOldNewIDs[i + 1];
1646 break;
1647 }
1648 }
1649
1650 *aType = NULL;
1651
1652 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1653 for (GuestOSTypesOList::iterator it = m->allGuestOSTypes.begin();
1654 it != m->allGuestOSTypes.end();
1655 ++it)
1656 {
1657 const Bstr &typeId = (*it)->id();
1658 AssertMsg(!typeId.isEmpty(), ("ID must not be NULL"));
1659 if (typeId.compare(id, Bstr::CaseInsensitive) == 0)
1660 {
1661 (*it).queryInterfaceTo(aType);
1662 break;
1663 }
1664 }
1665
1666 return (*aType) ? S_OK :
1667 setError(E_INVALIDARG,
1668 tr("'%ls' is not a valid Guest OS type"),
1669 aId);
1670}
1671
1672STDMETHODIMP VirtualBox::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath,
1673 BOOL /* aWritable */, BOOL /* aAutoMount */)
1674{
1675 CheckComArgStrNotEmptyOrNull(aName);
1676 CheckComArgStrNotEmptyOrNull(aHostPath);
1677
1678 AutoCaller autoCaller(this);
1679 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1680
1681 return setError(E_NOTIMPL, "Not yet implemented");
1682}
1683
1684STDMETHODIMP VirtualBox::RemoveSharedFolder(IN_BSTR aName)
1685{
1686 CheckComArgStrNotEmptyOrNull(aName);
1687
1688 AutoCaller autoCaller(this);
1689 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1690
1691 return setError(E_NOTIMPL, "Not yet implemented");
1692}
1693
1694/**
1695 * @note Locks this object for reading.
1696 */
1697STDMETHODIMP VirtualBox::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
1698{
1699 using namespace settings;
1700
1701 if (ComSafeArrayOutIsNull(aKeys))
1702 return E_POINTER;
1703
1704 AutoCaller autoCaller(this);
1705 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1706
1707 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1708
1709 com::SafeArray<BSTR> saKeys(m->pMainConfigFile->mapExtraDataItems.size());
1710 int i = 0;
1711 for (StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.begin();
1712 it != m->pMainConfigFile->mapExtraDataItems.end();
1713 ++it, ++i)
1714 {
1715 const Utf8Str &strName = it->first; // the key
1716 strName.cloneTo(&saKeys[i]);
1717 }
1718 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
1719
1720 return S_OK;
1721}
1722
1723/**
1724 * @note Locks this object for reading.
1725 */
1726STDMETHODIMP VirtualBox::GetExtraData(IN_BSTR aKey,
1727 BSTR *aValue)
1728{
1729 CheckComArgStrNotEmptyOrNull(aKey);
1730 CheckComArgNotNull(aValue);
1731
1732 AutoCaller autoCaller(this);
1733 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1734
1735 /* start with nothing found */
1736 Utf8Str strKey(aKey);
1737 Bstr bstrResult;
1738
1739 settings::StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(strKey);
1740 if (it != m->pMainConfigFile->mapExtraDataItems.end())
1741 // found:
1742 bstrResult = it->second; // source is a Utf8Str
1743
1744 /* return the result to caller (may be empty) */
1745 bstrResult.cloneTo(aValue);
1746
1747 return S_OK;
1748}
1749
1750/**
1751 * @note Locks this object for writing.
1752 */
1753STDMETHODIMP VirtualBox::SetExtraData(IN_BSTR aKey,
1754 IN_BSTR aValue)
1755{
1756 CheckComArgStrNotEmptyOrNull(aKey);
1757
1758 AutoCaller autoCaller(this);
1759 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1760
1761 Utf8Str strKey(aKey);
1762 Utf8Str strValue(aValue);
1763 Utf8Str strOldValue; // empty
1764
1765 // locking note: we only hold the read lock briefly to look up the old value,
1766 // then release it and call the onExtraCanChange callbacks. There is a small
1767 // chance of a race insofar as the callback might be called twice if two callers
1768 // change the same key at the same time, but that's a much better solution
1769 // than the deadlock we had here before. The actual changing of the extradata
1770 // is then performed under the write lock and race-free.
1771
1772 // look up the old value first; if nothing has changed then we need not do anything
1773 {
1774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
1775 settings::StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(strKey);
1776 if (it != m->pMainConfigFile->mapExtraDataItems.end())
1777 strOldValue = it->second;
1778 }
1779
1780 bool fChanged;
1781 if ((fChanged = (strOldValue != strValue)))
1782 {
1783 // ask for permission from all listeners outside the locks;
1784 // onExtraDataCanChange() only briefly requests the VirtualBox
1785 // lock to copy the list of callbacks to invoke
1786 Bstr error;
1787 Bstr bstrValue(aValue);
1788
1789 if (!onExtraDataCanChange(Guid::Empty, aKey, bstrValue.raw(), error))
1790 {
1791 const char *sep = error.isEmpty() ? "" : ": ";
1792 CBSTR err = error.raw();
1793 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
1794 sep, err));
1795 return setError(E_ACCESSDENIED,
1796 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
1797 aKey,
1798 bstrValue.raw(),
1799 sep,
1800 err);
1801 }
1802
1803 // data is changing and change not vetoed: then write it out under the lock
1804
1805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1806
1807 if (strValue.isEmpty())
1808 m->pMainConfigFile->mapExtraDataItems.erase(strKey);
1809 else
1810 m->pMainConfigFile->mapExtraDataItems[strKey] = strValue;
1811 // creates a new key if needed
1812
1813 /* save settings on success */
1814 HRESULT rc = saveSettings();
1815 if (FAILED(rc)) return rc;
1816 }
1817
1818 // fire notification outside the lock
1819 if (fChanged)
1820 onExtraDataChange(Guid::Empty, aKey, aValue);
1821
1822 return S_OK;
1823}
1824
1825// public methods only for internal purposes
1826/////////////////////////////////////////////////////////////////////////////
1827
1828#ifdef DEBUG
1829void VirtualBox::dumpAllBackRefs()
1830{
1831 {
1832 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1833 for (MediaList::const_iterator mt = m->allHardDisks.begin();
1834 mt != m->allHardDisks.end();
1835 ++mt)
1836 {
1837 ComObjPtr<Medium> pMedium = *mt;
1838 pMedium->dumpBackRefs();
1839 }
1840 }
1841 {
1842 AutoReadLock al(m->allDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1843 for (MediaList::const_iterator mt = m->allDVDImages.begin();
1844 mt != m->allDVDImages.end();
1845 ++mt)
1846 {
1847 ComObjPtr<Medium> pMedium = *mt;
1848 pMedium->dumpBackRefs();
1849 }
1850 }
1851}
1852#endif
1853
1854/**
1855 * Posts an event to the event queue that is processed asynchronously
1856 * on a dedicated thread.
1857 *
1858 * Posting events to the dedicated event queue is useful to perform secondary
1859 * actions outside any object locks -- for example, to iterate over a list
1860 * of callbacks and inform them about some change caused by some object's
1861 * method call.
1862 *
1863 * @param event event to post; must have been allocated using |new|, will
1864 * be deleted automatically by the event thread after processing
1865 *
1866 * @note Doesn't lock any object.
1867 */
1868HRESULT VirtualBox::postEvent(Event *event)
1869{
1870 AssertReturn(event, E_FAIL);
1871
1872 HRESULT rc;
1873 AutoCaller autoCaller(this);
1874 if (SUCCEEDED((rc = autoCaller.rc())))
1875 {
1876 if (autoCaller.state() != Ready)
1877 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the event is discarded!\n",
1878 autoCaller.state()));
1879 // return S_OK
1880 else if ( (m->pAsyncEventQ)
1881 && (m->pAsyncEventQ->postEvent(event))
1882 )
1883 return S_OK;
1884 else
1885 rc = E_FAIL;
1886 }
1887
1888 // in any event of failure, we must clean up here, or we'll leak;
1889 // the caller has allocated the object using new()
1890 delete event;
1891 return rc;
1892}
1893
1894/**
1895 * Adds a progress to the global collection of pending operations.
1896 * Usually gets called upon progress object initialization.
1897 *
1898 * @param aProgress Operation to add to the collection.
1899 *
1900 * @note Doesn't lock objects.
1901 */
1902HRESULT VirtualBox::addProgress(IProgress *aProgress)
1903{
1904 CheckComArgNotNull(aProgress);
1905
1906 AutoCaller autoCaller(this);
1907 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1908
1909 Bstr id;
1910 HRESULT rc = aProgress->COMGETTER(Id)(id.asOutParam());
1911 AssertComRCReturnRC(rc);
1912
1913 /* protect mProgressOperations */
1914 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
1915
1916 m->mapProgressOperations.insert(ProgressMap::value_type(Guid(id), aProgress));
1917 return S_OK;
1918}
1919
1920/**
1921 * Removes the progress from the global collection of pending operations.
1922 * Usually gets called upon progress completion.
1923 *
1924 * @param aId UUID of the progress operation to remove
1925 *
1926 * @note Doesn't lock objects.
1927 */
1928HRESULT VirtualBox::removeProgress(IN_GUID aId)
1929{
1930 AutoCaller autoCaller(this);
1931 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1932
1933 ComPtr<IProgress> progress;
1934
1935 /* protect mProgressOperations */
1936 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
1937
1938 size_t cnt = m->mapProgressOperations.erase(aId);
1939 Assert(cnt == 1);
1940 NOREF(cnt);
1941
1942 return S_OK;
1943}
1944
1945#ifdef RT_OS_WINDOWS
1946
1947struct StartSVCHelperClientData
1948{
1949 ComObjPtr<VirtualBox> that;
1950 ComObjPtr<Progress> progress;
1951 bool privileged;
1952 VirtualBox::SVCHelperClientFunc func;
1953 void *user;
1954};
1955
1956/**
1957 * Helper method that starts a worker thread that:
1958 * - creates a pipe communication channel using SVCHlpClient;
1959 * - starts an SVC Helper process that will inherit this channel;
1960 * - executes the supplied function by passing it the created SVCHlpClient
1961 * and opened instance to communicate to the Helper process and the given
1962 * Progress object.
1963 *
1964 * The user function is supposed to communicate to the helper process
1965 * using the \a aClient argument to do the requested job and optionally expose
1966 * the progress through the \a aProgress object. The user function should never
1967 * call notifyComplete() on it: this will be done automatically using the
1968 * result code returned by the function.
1969 *
1970 * Before the user function is started, the communication channel passed to
1971 * the \a aClient argument is fully set up, the function should start using
1972 * its write() and read() methods directly.
1973 *
1974 * The \a aVrc parameter of the user function may be used to return an error
1975 * code if it is related to communication errors (for example, returned by
1976 * the SVCHlpClient members when they fail). In this case, the correct error
1977 * message using this value will be reported to the caller. Note that the
1978 * value of \a aVrc is inspected only if the user function itself returns
1979 * success.
1980 *
1981 * If a failure happens anywhere before the user function would be normally
1982 * called, it will be called anyway in special "cleanup only" mode indicated
1983 * by \a aClient, \a aProgress and \aVrc arguments set to NULL. In this mode,
1984 * all the function is supposed to do is to cleanup its aUser argument if
1985 * necessary (it's assumed that the ownership of this argument is passed to
1986 * the user function once #startSVCHelperClient() returns a success, thus
1987 * making it responsible for the cleanup).
1988 *
1989 * After the user function returns, the thread will send the SVCHlpMsg::Null
1990 * message to indicate a process termination.
1991 *
1992 * @param aPrivileged |true| to start the SVC Helper process as a privileged
1993 * user that can perform administrative tasks
1994 * @param aFunc user function to run
1995 * @param aUser argument to the user function
1996 * @param aProgress progress object that will track operation completion
1997 *
1998 * @note aPrivileged is currently ignored (due to some unsolved problems in
1999 * Vista) and the process will be started as a normal (unprivileged)
2000 * process.
2001 *
2002 * @note Doesn't lock anything.
2003 */
2004HRESULT VirtualBox::startSVCHelperClient(bool aPrivileged,
2005 SVCHelperClientFunc aFunc,
2006 void *aUser, Progress *aProgress)
2007{
2008 AssertReturn(aFunc, E_POINTER);
2009 AssertReturn(aProgress, E_POINTER);
2010
2011 AutoCaller autoCaller(this);
2012 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2013
2014 /* create the SVCHelperClientThread() argument */
2015 std::auto_ptr <StartSVCHelperClientData>
2016 d(new StartSVCHelperClientData());
2017 AssertReturn(d.get(), E_OUTOFMEMORY);
2018
2019 d->that = this;
2020 d->progress = aProgress;
2021 d->privileged = aPrivileged;
2022 d->func = aFunc;
2023 d->user = aUser;
2024
2025 RTTHREAD tid = NIL_RTTHREAD;
2026 int vrc = RTThreadCreate(&tid, SVCHelperClientThread,
2027 static_cast <void *>(d.get()),
2028 0, RTTHREADTYPE_MAIN_WORKER,
2029 RTTHREADFLAGS_WAITABLE, "SVCHelper");
2030 if (RT_FAILURE(vrc))
2031 return setError(E_FAIL, "Could not create SVCHelper thread (%Rrc)", vrc);
2032
2033 /* d is now owned by SVCHelperClientThread(), so release it */
2034 d.release();
2035
2036 return S_OK;
2037}
2038
2039/**
2040 * Worker thread for startSVCHelperClient().
2041 */
2042/* static */
2043DECLCALLBACK(int)
2044VirtualBox::SVCHelperClientThread(RTTHREAD aThread, void *aUser)
2045{
2046 LogFlowFuncEnter();
2047
2048 std::auto_ptr<StartSVCHelperClientData>
2049 d(static_cast<StartSVCHelperClientData*>(aUser));
2050
2051 HRESULT rc = S_OK;
2052 bool userFuncCalled = false;
2053
2054 do
2055 {
2056 AssertBreakStmt(d.get(), rc = E_POINTER);
2057 AssertReturn(!d->progress.isNull(), E_POINTER);
2058
2059 /* protect VirtualBox from uninitialization */
2060 AutoCaller autoCaller(d->that);
2061 if (!autoCaller.isOk())
2062 {
2063 /* it's too late */
2064 rc = autoCaller.rc();
2065 break;
2066 }
2067
2068 int vrc = VINF_SUCCESS;
2069
2070 Guid id;
2071 id.create();
2072 SVCHlpClient client;
2073 vrc = client.create(Utf8StrFmt("VirtualBox\\SVCHelper\\{%RTuuid}",
2074 id.raw()).c_str());
2075 if (RT_FAILURE(vrc))
2076 {
2077 rc = d->that->setError(E_FAIL,
2078 tr("Could not create the communication channel (%Rrc)"), vrc);
2079 break;
2080 }
2081
2082 /* get the path to the executable */
2083 char exePathBuf[RTPATH_MAX];
2084 char *exePath = RTProcGetExecutablePath(exePathBuf, RTPATH_MAX);
2085 if (!exePath)
2086 {
2087 rc = d->that->setError(E_FAIL, tr("Cannot get executable name"));
2088 break;
2089 }
2090
2091 Utf8Str argsStr = Utf8StrFmt("/Helper %s", client.name().c_str());
2092
2093 LogFlowFunc(("Starting '\"%s\" %s'...\n", exePath, argsStr.c_str()));
2094
2095 RTPROCESS pid = NIL_RTPROCESS;
2096
2097 if (d->privileged)
2098 {
2099 /* Attempt to start a privileged process using the Run As dialog */
2100
2101 Bstr file = exePath;
2102 Bstr parameters = argsStr;
2103
2104 SHELLEXECUTEINFO shExecInfo;
2105
2106 shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
2107
2108 shExecInfo.fMask = NULL;
2109 shExecInfo.hwnd = NULL;
2110 shExecInfo.lpVerb = L"runas";
2111 shExecInfo.lpFile = file.raw();
2112 shExecInfo.lpParameters = parameters.raw();
2113 shExecInfo.lpDirectory = NULL;
2114 shExecInfo.nShow = SW_NORMAL;
2115 shExecInfo.hInstApp = NULL;
2116
2117 if (!ShellExecuteEx(&shExecInfo))
2118 {
2119 int vrc2 = RTErrConvertFromWin32(GetLastError());
2120 /* hide excessive details in case of a frequent error
2121 * (pressing the Cancel button to close the Run As dialog) */
2122 if (vrc2 == VERR_CANCELLED)
2123 rc = d->that->setError(E_FAIL,
2124 tr("Operation canceled by the user"));
2125 else
2126 rc = d->that->setError(E_FAIL,
2127 tr("Could not launch a privileged process '%s' (%Rrc)"),
2128 exePath, vrc2);
2129 break;
2130 }
2131 }
2132 else
2133 {
2134 const char *args[] = { exePath, "/Helper", client.name().c_str(), 0 };
2135 vrc = RTProcCreate(exePath, args, RTENV_DEFAULT, 0, &pid);
2136 if (RT_FAILURE(vrc))
2137 {
2138 rc = d->that->setError(E_FAIL,
2139 tr("Could not launch a process '%s' (%Rrc)"), exePath, vrc);
2140 break;
2141 }
2142 }
2143
2144 /* wait for the client to connect */
2145 vrc = client.connect();
2146 if (RT_SUCCESS(vrc))
2147 {
2148 /* start the user supplied function */
2149 rc = d->func(&client, d->progress, d->user, &vrc);
2150 userFuncCalled = true;
2151 }
2152
2153 /* send the termination signal to the process anyway */
2154 {
2155 int vrc2 = client.write(SVCHlpMsg::Null);
2156 if (RT_SUCCESS(vrc))
2157 vrc = vrc2;
2158 }
2159
2160 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
2161 {
2162 rc = d->that->setError(E_FAIL,
2163 tr("Could not operate the communication channel (%Rrc)"), vrc);
2164 break;
2165 }
2166 }
2167 while (0);
2168
2169 if (FAILED(rc) && !userFuncCalled)
2170 {
2171 /* call the user function in the "cleanup only" mode
2172 * to let it free resources passed to in aUser */
2173 d->func(NULL, NULL, d->user, NULL);
2174 }
2175
2176 d->progress->notifyComplete(rc);
2177
2178 LogFlowFuncLeave();
2179 return 0;
2180}
2181
2182#endif /* RT_OS_WINDOWS */
2183
2184/**
2185 * Sends a signal to the client watcher thread to rescan the set of machines
2186 * that have open sessions.
2187 *
2188 * @note Doesn't lock anything.
2189 */
2190void VirtualBox::updateClientWatcher()
2191{
2192 AutoCaller autoCaller(this);
2193 AssertComRCReturnVoid(autoCaller.rc());
2194
2195 AssertReturnVoid(m->threadClientWatcher != NIL_RTTHREAD);
2196
2197 /* sent an update request */
2198#if defined(RT_OS_WINDOWS)
2199 ::SetEvent(m->updateReq);
2200#elif defined(RT_OS_OS2)
2201 RTSemEventSignal(m->updateReq);
2202#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
2203 RTSemEventSignal(m->updateReq);
2204#else
2205# error "Port me!"
2206#endif
2207}
2208
2209/**
2210 * Adds the given child process ID to the list of processes to be reaped.
2211 * This call should be followed by #updateClientWatcher() to take the effect.
2212 */
2213void VirtualBox::addProcessToReap(RTPROCESS pid)
2214{
2215 AutoCaller autoCaller(this);
2216 AssertComRCReturnVoid(autoCaller.rc());
2217
2218 /// @todo (dmik) Win32?
2219#ifndef RT_OS_WINDOWS
2220 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2221 m->llProcesses.push_back(pid);
2222#endif
2223}
2224
2225/** Event for onMachineStateChange(), onMachineDataChange(), onMachineRegistered() */
2226struct MachineEvent : public VirtualBox::CallbackEvent
2227{
2228 MachineEvent(VirtualBox *aVB, const Guid &aId)
2229 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineDataChanged), id(aId.toUtf16())
2230 {}
2231
2232 MachineEvent(VirtualBox *aVB, const Guid &aId, MachineState_T aState)
2233 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineStateChanged), id(aId.toUtf16())
2234 , state(aState)
2235 {}
2236
2237 MachineEvent(VirtualBox *aVB, const Guid &aId, BOOL aRegistered)
2238 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineRegistered), id(aId.toUtf16())
2239 , registered(aRegistered)
2240 {}
2241
2242 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2243 {
2244 switch (mWhat)
2245 {
2246 case VirtualBoxCallbackRegistration::kOnMachineDataChanged:
2247 aEvDesc.init(aSource, VBoxEventType_OnMachineDataChanged, id.raw());
2248 break;
2249
2250 case VirtualBoxCallbackRegistration::kOnMachineStateChanged:
2251 aEvDesc.init(aSource, VBoxEventType_OnMachineStateChanged, id.raw(), state);
2252 break;
2253
2254 case VirtualBoxCallbackRegistration::kOnMachineRegistered:
2255 aEvDesc.init(aSource, VBoxEventType_OnMachineRegistered, id.raw(), registered);
2256 break;
2257
2258 default:
2259 AssertFailedReturn(S_OK);
2260 }
2261 return S_OK;
2262 }
2263
2264 Bstr id;
2265 MachineState_T state;
2266 BOOL registered;
2267};
2268
2269/**
2270 * @note Doesn't lock any object.
2271 */
2272void VirtualBox::onMachineStateChange(const Guid &aId, MachineState_T aState)
2273{
2274 postEvent(new MachineEvent(this, aId, aState));
2275}
2276
2277/**
2278 * @note Doesn't lock any object.
2279 */
2280void VirtualBox::onMachineDataChange(const Guid &aId)
2281{
2282 postEvent(new MachineEvent(this, aId));
2283}
2284
2285/**
2286 * @note Locks this object for reading.
2287 */
2288BOOL VirtualBox::onExtraDataCanChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue,
2289 Bstr &aError)
2290{
2291 LogFlowThisFunc(("machine={%s} aKey={%ls} aValue={%ls}\n",
2292 aId.toString().c_str(), aKey, aValue));
2293
2294 AutoCaller autoCaller(this);
2295 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2296
2297 BOOL allowChange = TRUE;
2298 Bstr id = aId.toUtf16();
2299
2300 VBoxEventDesc evDesc;
2301 evDesc.init(m->pEventSource, VBoxEventType_OnExtraDataCanChange, id.raw(), aKey, aValue);
2302 BOOL fDelivered = evDesc.fire(3000); /* Wait up to 3 secs for delivery */
2303 //Assert(fDelivered);
2304 if (fDelivered)
2305 {
2306 ComPtr<IEvent> aEvent;
2307 evDesc.getEvent(aEvent.asOutParam());
2308 ComPtr<IExtraDataCanChangeEvent> aCanChangeEvent = aEvent;
2309 Assert(aCanChangeEvent);
2310 BOOL fVetoed = FALSE;
2311 aCanChangeEvent->IsVetoed(&fVetoed);
2312 allowChange = !fVetoed;
2313
2314 if (!allowChange)
2315 {
2316 SafeArray<BSTR> aVetos;
2317 aCanChangeEvent->GetVetos(ComSafeArrayAsOutParam(aVetos));
2318 if (aVetos.size() > 0)
2319 aError = aVetos[0];
2320 }
2321 }
2322 else
2323 allowChange = TRUE;
2324
2325 LogFlowThisFunc(("allowChange=%RTbool\n", allowChange));
2326 return allowChange;
2327}
2328
2329/** Event for onExtraDataChange() */
2330struct ExtraDataEvent : public VirtualBox::CallbackEvent
2331{
2332 ExtraDataEvent(VirtualBox *aVB, const Guid &aMachineId,
2333 IN_BSTR aKey, IN_BSTR aVal)
2334 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnExtraDataChanged)
2335 , machineId(aMachineId.toUtf16()), key(aKey), val(aVal)
2336 {}
2337
2338 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2339 {
2340 return aEvDesc.init(aSource, VBoxEventType_OnExtraDataChanged, machineId.raw(), key.raw(), val.raw());
2341 }
2342
2343 Bstr machineId, key, val;
2344};
2345
2346/**
2347 * @note Doesn't lock any object.
2348 */
2349void VirtualBox::onExtraDataChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue)
2350{
2351 postEvent(new ExtraDataEvent(this, aId, aKey, aValue));
2352}
2353
2354/**
2355 * @note Doesn't lock any object.
2356 */
2357void VirtualBox::onMachineRegistered(const Guid &aId, BOOL aRegistered)
2358{
2359 postEvent(new MachineEvent(this, aId, aRegistered));
2360}
2361
2362/** Event for onSessionStateChange() */
2363struct SessionEvent : public VirtualBox::CallbackEvent
2364{
2365 SessionEvent(VirtualBox *aVB, const Guid &aMachineId, SessionState_T aState)
2366 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnSessionStateChanged)
2367 , machineId(aMachineId.toUtf16()), sessionState(aState)
2368 {}
2369
2370 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2371 {
2372 return aEvDesc.init(aSource, VBoxEventType_OnSessionStateChanged, machineId.raw(), sessionState);
2373 }
2374 Bstr machineId;
2375 SessionState_T sessionState;
2376};
2377
2378/**
2379 * @note Doesn't lock any object.
2380 */
2381void VirtualBox::onSessionStateChange(const Guid &aId, SessionState_T aState)
2382{
2383 postEvent(new SessionEvent(this, aId, aState));
2384}
2385
2386/** Event for onSnapshotTaken(), onSnapshotDeleted() and onSnapshotChange() */
2387struct SnapshotEvent : public VirtualBox::CallbackEvent
2388{
2389 SnapshotEvent(VirtualBox *aVB, const Guid &aMachineId, const Guid &aSnapshotId,
2390 VirtualBoxCallbackRegistration::CallbackBit aWhat)
2391 : CallbackEvent(aVB, aWhat)
2392 , machineId(aMachineId), snapshotId(aSnapshotId)
2393 {}
2394
2395 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2396 {
2397 return aEvDesc.init(aSource, VBoxEventType_OnSnapshotTaken,
2398 machineId.toUtf16().raw(), snapshotId.toUtf16().raw());
2399 }
2400
2401 Guid machineId;
2402 Guid snapshotId;
2403};
2404
2405/**
2406 * @note Doesn't lock any object.
2407 */
2408void VirtualBox::onSnapshotTaken(const Guid &aMachineId, const Guid &aSnapshotId)
2409{
2410 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2411 VirtualBoxCallbackRegistration::kOnSnapshotTaken));
2412}
2413
2414/**
2415 * @note Doesn't lock any object.
2416 */
2417void VirtualBox::onSnapshotDeleted(const Guid &aMachineId, const Guid &aSnapshotId)
2418{
2419 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2420 VirtualBoxCallbackRegistration::kOnSnapshotDeleted));
2421}
2422
2423/**
2424 * @note Doesn't lock any object.
2425 */
2426void VirtualBox::onSnapshotChange(const Guid &aMachineId, const Guid &aSnapshotId)
2427{
2428 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2429 VirtualBoxCallbackRegistration::kOnSnapshotChanged));
2430}
2431
2432/** Event for onGuestPropertyChange() */
2433struct GuestPropertyEvent : public VirtualBox::CallbackEvent
2434{
2435 GuestPropertyEvent(VirtualBox *aVBox, const Guid &aMachineId,
2436 IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
2437 : CallbackEvent(aVBox, VirtualBoxCallbackRegistration::kOnGuestPropertyChanged),
2438 machineId(aMachineId),
2439 name(aName),
2440 value(aValue),
2441 flags(aFlags)
2442 {}
2443
2444 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2445 {
2446 return aEvDesc.init(aSource, VBoxEventType_OnGuestPropertyChanged,
2447 machineId.toUtf16().raw(), name.raw(), value.raw(), flags.raw());
2448 }
2449
2450 Guid machineId;
2451 Bstr name, value, flags;
2452};
2453
2454/**
2455 * @note Doesn't lock any object.
2456 */
2457void VirtualBox::onGuestPropertyChange(const Guid &aMachineId, IN_BSTR aName,
2458 IN_BSTR aValue, IN_BSTR aFlags)
2459{
2460 postEvent(new GuestPropertyEvent(this, aMachineId, aName, aValue, aFlags));
2461}
2462
2463/** Event for onMachineUninit(), this is not a CallbackEvent */
2464class MachineUninitEvent : public Event
2465{
2466public:
2467
2468 MachineUninitEvent(VirtualBox *aVirtualBox, Machine *aMachine)
2469 : mVirtualBox(aVirtualBox), mMachine(aMachine)
2470 {
2471 Assert(aVirtualBox);
2472 Assert(aMachine);
2473 }
2474
2475 void *handler()
2476 {
2477#ifdef VBOX_WITH_RESOURCE_USAGE_API
2478 /* Handle unregistering metrics here, as it is not vital to get
2479 * it done immediately. It reduces the number of locks needed and
2480 * the lock contention in SessionMachine::uninit. */
2481 {
2482 AutoWriteLock mLock(mMachine COMMA_LOCKVAL_SRC_POS);
2483 mMachine->unregisterMetrics(mVirtualBox->performanceCollector(), mMachine);
2484 }
2485#endif /* VBOX_WITH_RESOURCE_USAGE_API */
2486
2487 return NULL;
2488 }
2489
2490private:
2491
2492 /**
2493 * Note that this is a weak ref -- the CallbackEvent handler thread
2494 * is bound to the lifetime of the VirtualBox instance, so it's safe.
2495 */
2496 VirtualBox *mVirtualBox;
2497
2498 /** Reference to the machine object. */
2499 ComObjPtr<Machine> mMachine;
2500};
2501
2502/**
2503 * Trigger internal event. This isn't meant to be signalled to clients.
2504 * @note Doesn't lock any object.
2505 */
2506void VirtualBox::onMachineUninit(Machine *aMachine)
2507{
2508 postEvent(new MachineUninitEvent(this, aMachine));
2509}
2510
2511/**
2512 * @note Doesn't lock any object.
2513 */
2514void VirtualBox::onNatRedirectChange(const Guid &aMachineId, ULONG ulSlot, bool fRemove, IN_BSTR aName,
2515 NATProtocol_T aProto, IN_BSTR aHostIp, uint16_t aHostPort,
2516 IN_BSTR aGuestIp, uint16_t aGuestPort)
2517{
2518 fireNATRedirectEvent(m->pEventSource, aMachineId.toUtf16().raw(), ulSlot, fRemove, aName, aProto, aHostIp,
2519 aHostPort, aGuestIp, aGuestPort);
2520}
2521
2522/**
2523 * @note Locks this object for reading.
2524 */
2525ComObjPtr<GuestOSType> VirtualBox::getUnknownOSType()
2526{
2527 ComObjPtr<GuestOSType> type;
2528 AutoCaller autoCaller(this);
2529 AssertComRCReturn(autoCaller.rc(), type);
2530
2531 /* unknown type must always be the first */
2532 ComAssertRet(m->allGuestOSTypes.size() > 0, type);
2533
2534 return m->allGuestOSTypes.front();
2535}
2536
2537/**
2538 * Returns the list of opened machines (machines having direct sessions opened
2539 * by client processes) and optionally the list of direct session controls.
2540 *
2541 * @param aMachines Where to put opened machines (will be empty if none).
2542 * @param aControls Where to put direct session controls (optional).
2543 *
2544 * @note The returned lists contain smart pointers. So, clear it as soon as
2545 * it becomes no more necessary to release instances.
2546 *
2547 * @note It can be possible that a session machine from the list has been
2548 * already uninitialized, so do a usual AutoCaller/AutoReadLock sequence
2549 * when accessing unprotected data directly.
2550 *
2551 * @note Locks objects for reading.
2552 */
2553void VirtualBox::getOpenedMachines(SessionMachinesList &aMachines,
2554 InternalControlList *aControls /*= NULL*/)
2555{
2556 AutoCaller autoCaller(this);
2557 AssertComRCReturnVoid(autoCaller.rc());
2558
2559 aMachines.clear();
2560 if (aControls)
2561 aControls->clear();
2562
2563 AutoReadLock alock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2564
2565 for (MachinesOList::iterator it = m->allMachines.begin();
2566 it != m->allMachines.end();
2567 ++it)
2568 {
2569 ComObjPtr<SessionMachine> sm;
2570 ComPtr<IInternalSessionControl> ctl;
2571 if ((*it)->isSessionOpen(sm, &ctl))
2572 {
2573 aMachines.push_back(sm);
2574 if (aControls)
2575 aControls->push_back(ctl);
2576 }
2577 }
2578}
2579
2580/**
2581 * Searches for a machine object with the given ID in the collection
2582 * of registered machines.
2583 *
2584 * @param aId Machine UUID to look for.
2585 * @param aPermitInaccessible If true, inaccessible machines will be found;
2586 * if false, this will fail if the given machine is inaccessible.
2587 * @param aSetError If true, set errorinfo if the machine is not found.
2588 * @param aMachine Returned machine, if found.
2589 * @return
2590 */
2591HRESULT VirtualBox::findMachine(const Guid &aId,
2592 bool fPermitInaccessible,
2593 bool aSetError,
2594 ComObjPtr<Machine> *aMachine /* = NULL */)
2595{
2596 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
2597
2598 AutoCaller autoCaller(this);
2599 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2600
2601 {
2602 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2603
2604 for (MachinesOList::iterator it = m->allMachines.begin();
2605 it != m->allMachines.end();
2606 ++it)
2607 {
2608 ComObjPtr<Machine> pMachine2 = *it;
2609
2610 if (!fPermitInaccessible)
2611 {
2612 // skip inaccessible machines
2613 AutoCaller machCaller(pMachine2);
2614 if (FAILED(machCaller.rc()))
2615 continue;
2616 }
2617
2618 if (pMachine2->getId() == aId)
2619 {
2620 rc = S_OK;
2621 if (aMachine)
2622 *aMachine = pMachine2;
2623 break;
2624 }
2625 }
2626 }
2627
2628 if (aSetError && FAILED(rc))
2629 rc = setError(rc,
2630 tr("Could not find a registered machine with UUID {%RTuuid}"),
2631 aId.raw());
2632
2633 return rc;
2634}
2635
2636/**
2637 * Searches for a Medium object with the given ID in the list of registered
2638 * hard disks.
2639 *
2640 * @param aId ID of the hard disk. Must not be empty.
2641 * @param aSetError If @c true , the appropriate error info is set in case
2642 * when the hard disk is not found.
2643 * @param aHardDisk Where to store the found hard disk object (can be NULL).
2644 *
2645 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
2646 *
2647 * @note Locks the media tree for reading.
2648 */
2649HRESULT VirtualBox::findHardDiskById(const Guid &id,
2650 bool aSetError,
2651 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
2652{
2653 AssertReturn(!id.isEmpty(), E_INVALIDARG);
2654
2655 // we use the hard disks map, but it is protected by the
2656 // hard disk _list_ lock handle
2657 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2658
2659 HardDiskMap::const_iterator it = m->mapHardDisks.find(id);
2660 if (it != m->mapHardDisks.end())
2661 {
2662 if (aHardDisk)
2663 *aHardDisk = (*it).second;
2664 return S_OK;
2665 }
2666
2667 if (aSetError)
2668 return setError(VBOX_E_OBJECT_NOT_FOUND,
2669 tr("Could not find an open hard disk with UUID {%RTuuid}"),
2670 id.raw());
2671
2672 return VBOX_E_OBJECT_NOT_FOUND;
2673}
2674
2675/**
2676 * Searches for a Medium object with the given ID or location in the list of
2677 * registered hard disks. If both ID and location are specified, the first
2678 * object that matches either of them (not necessarily both) is returned.
2679 *
2680 * @param aLocation Full location specification. Must not be empty.
2681 * @param aSetError If @c true , the appropriate error info is set in case
2682 * when the hard disk is not found.
2683 * @param aHardDisk Where to store the found hard disk object (can be NULL).
2684 *
2685 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
2686 *
2687 * @note Locks the media tree for reading.
2688 */
2689HRESULT VirtualBox::findHardDiskByLocation(const Utf8Str &strLocation,
2690 bool aSetError,
2691 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
2692{
2693 AssertReturn(!strLocation.isEmpty(), E_INVALIDARG);
2694
2695 // we use the hard disks map, but it is protected by the
2696 // hard disk _list_ lock handle
2697 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2698
2699 for (HardDiskMap::const_iterator it = m->mapHardDisks.begin();
2700 it != m->mapHardDisks.end();
2701 ++it)
2702 {
2703 const ComObjPtr<Medium> &pHD = (*it).second;
2704
2705 AutoCaller autoCaller(pHD);
2706 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2707 AutoWriteLock mlock(pHD COMMA_LOCKVAL_SRC_POS);
2708
2709 Utf8Str strLocationFull = pHD->getLocationFull();
2710
2711 if (0 == RTPathCompare(strLocationFull.c_str(), strLocation.c_str()))
2712 {
2713 if (aHardDisk)
2714 *aHardDisk = pHD;
2715 return S_OK;
2716 }
2717 }
2718
2719 if (aSetError)
2720 return setError(VBOX_E_OBJECT_NOT_FOUND,
2721 tr("Could not find an open hard disk with location '%s'"),
2722 strLocation.c_str());
2723
2724 return VBOX_E_OBJECT_NOT_FOUND;
2725}
2726
2727/**
2728 * Searches for a Medium object with the given ID or location in the list of
2729 * registered DVD or floppy images, depending on the @a mediumType argument.
2730 * If both ID and file path are specified, the first object that matches either
2731 * of them (not necessarily both) is returned.
2732 *
2733 * @param mediumType Must be either DeviceType_DVD or DeviceType_Floppy.
2734 * @param aId ID of the image file (unused when NULL).
2735 * @param aLocation Full path to the image file (unused when NULL).
2736 * @param aSetError If @c true, the appropriate error info is set in case when
2737 * the image is not found.
2738 * @param aImage Where to store the found image object (can be NULL).
2739 *
2740 * @return S_OK when found or E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
2741 *
2742 * @note Locks the media tree for reading.
2743 */
2744HRESULT VirtualBox::findDVDOrFloppyImage(DeviceType_T mediumType,
2745 const Guid *aId,
2746 const Utf8Str &aLocation,
2747 bool aSetError,
2748 ComObjPtr<Medium> *aImage /* = NULL */)
2749{
2750 AssertReturn(aId || !aLocation.isEmpty(), E_INVALIDARG);
2751
2752 Utf8Str location;
2753 if (!aLocation.isEmpty())
2754 {
2755 int vrc = calculateFullPath(aLocation, location);
2756 if (RT_FAILURE(vrc))
2757 return setError(VBOX_E_FILE_ERROR,
2758 tr("Invalid image file location '%s' (%Rrc)"),
2759 aLocation.c_str(),
2760 vrc);
2761 }
2762
2763 MediaOList *pMediaList;
2764
2765 switch (mediumType)
2766 {
2767 case DeviceType_DVD:
2768 pMediaList = &m->allDVDImages;
2769 break;
2770
2771 case DeviceType_Floppy:
2772 pMediaList = &m->allFloppyImages;
2773 break;
2774
2775 default:
2776 return E_INVALIDARG;
2777 }
2778
2779 AutoReadLock alock(pMediaList->getLockHandle() COMMA_LOCKVAL_SRC_POS);
2780
2781 bool found = false;
2782
2783 for (MediaList::const_iterator it = pMediaList->begin();
2784 it != pMediaList->end();
2785 ++it)
2786 {
2787 // no AutoCaller, registered image life time is bound to this
2788 Medium *pMedium = *it;
2789 AutoReadLock imageLock(pMedium COMMA_LOCKVAL_SRC_POS);
2790 const Utf8Str &strLocationFull = pMedium->getLocationFull();
2791
2792 found = ( aId
2793 && pMedium->getId() == *aId)
2794 || ( !aLocation.isEmpty()
2795 && RTPathCompare(location.c_str(),
2796 strLocationFull.c_str()) == 0);
2797 if (found)
2798 {
2799 if (pMedium->getDeviceType() != mediumType)
2800 {
2801 if (mediumType == DeviceType_DVD)
2802 return setError(E_INVALIDARG,
2803 "Cannot mount DVD medium '%s' as floppy", strLocationFull.c_str());
2804 else
2805 return setError(E_INVALIDARG,
2806 "Cannot mount floppy medium '%s' as DVD", strLocationFull.c_str());
2807 }
2808
2809 if (aImage)
2810 *aImage = pMedium;
2811 break;
2812 }
2813 }
2814
2815 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
2816
2817 if (aSetError && !found)
2818 {
2819 if (aId)
2820 setError(rc,
2821 tr("Could not find an image file with UUID {%RTuuid} in the media registry ('%s')"),
2822 aId->raw(),
2823 m->strSettingsFilePath.c_str());
2824 else
2825 setError(rc,
2826 tr("Could not find an image file with location '%s' in the media registry ('%s')"),
2827 aLocation.c_str(),
2828 m->strSettingsFilePath.c_str());
2829 }
2830
2831 return rc;
2832}
2833
2834/**
2835 * Searches for an IMedium object that represents the given UUID.
2836 *
2837 * If the UUID is empty (indicating an empty drive), this sets pMedium
2838 * to NULL and returns S_OK.
2839 *
2840 * If the UUID refers to a host drive of the given device type, this
2841 * sets pMedium to the object from the list in IHost and returns S_OK.
2842 *
2843 * If the UUID is an image file, this sets pMedium to the object that
2844 * findDVDOrFloppyImage() returned.
2845 *
2846 * If none of the above apply, this returns VBOX_E_OBJECT_NOT_FOUND.
2847 *
2848 * @param mediumType Must be DeviceType_DVD or DeviceType_Floppy.
2849 * @param uuid UUID to search for; must refer to a host drive or an image file or be null.
2850 * @param fRefresh Whether to refresh the list of host drives in IHost (see Host::getDrives())
2851 * @param pMedium out: IMedium object found.
2852 * @return
2853 */
2854HRESULT VirtualBox::findRemoveableMedium(DeviceType_T mediumType,
2855 const Guid &uuid,
2856 bool fRefresh,
2857 bool aSetError,
2858 ComObjPtr<Medium> &pMedium)
2859{
2860 if (uuid.isEmpty())
2861 {
2862 // that's easy
2863 pMedium.setNull();
2864 return S_OK;
2865 }
2866
2867 // first search for host drive with that UUID
2868 HRESULT rc = m->pHost->findHostDriveById(mediumType,
2869 uuid,
2870 fRefresh,
2871 pMedium);
2872 if (rc == VBOX_E_OBJECT_NOT_FOUND)
2873 // then search for an image with that UUID
2874 rc = findDVDOrFloppyImage(mediumType, &uuid, Utf8Str::Empty, aSetError, &pMedium);
2875
2876 return rc;
2877}
2878
2879HRESULT VirtualBox::findGuestOSType(const Bstr &bstrOSType,
2880 GuestOSType*& pGuestOSType)
2881{
2882 /* Look for a GuestOSType object */
2883 AssertMsg(m->allGuestOSTypes.size() != 0,
2884 ("Guest OS types array must be filled"));
2885
2886 if (bstrOSType.isEmpty())
2887 {
2888 pGuestOSType = NULL;
2889 return S_OK;
2890 }
2891
2892 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2893 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
2894 it != m->allGuestOSTypes.end();
2895 ++it)
2896 {
2897 if ((*it)->id() == bstrOSType)
2898 {
2899 pGuestOSType = *it;
2900 return S_OK;
2901 }
2902 }
2903
2904 return setError(VBOX_E_OBJECT_NOT_FOUND,
2905 tr("Guest OS type '%ls' is invalid"),
2906 bstrOSType.raw());
2907}
2908
2909/**
2910 * Returns the constant pseudo-machine UUID that is used to identify the
2911 * global media registry.
2912 *
2913 * Starting with VirtualBox 4.0 each medium remembers in its instance data
2914 * in which media registry it is saved (if any): this can either be a machine
2915 * UUID, if it's in a per-machine media registry, or this global ID.
2916 *
2917 * This UUID is only used to identify the VirtualBox object while VirtualBox
2918 * is running. It is a compile-time constant and not saved anywhere.
2919 *
2920 * @return
2921 */
2922const Guid& VirtualBox::getGlobalRegistryId() const
2923{
2924 return m->uuidMediaRegistry;
2925}
2926
2927const ComObjPtr<Host>& VirtualBox::host() const
2928{
2929 return m->pHost;
2930}
2931
2932SystemProperties* VirtualBox::getSystemProperties() const
2933{
2934 return m->pSystemProperties;
2935}
2936
2937#ifdef VBOX_WITH_EXTPACK
2938/**
2939 * Getter that SystemProperties and others can use to talk to the extension
2940 * pack manager.
2941 */
2942ExtPackManager* VirtualBox::getExtPackManager() const
2943{
2944 return m->ptrExtPackManager;
2945}
2946#endif
2947
2948#ifdef VBOX_WITH_RESOURCE_USAGE_API
2949const ComObjPtr<PerformanceCollector>& VirtualBox::performanceCollector() const
2950{
2951 return m->pPerformanceCollector;
2952}
2953#endif /* VBOX_WITH_RESOURCE_USAGE_API */
2954
2955/**
2956 * Returns the default machine folder from the system properties
2957 * with proper locking.
2958 * @return
2959 */
2960void VirtualBox::getDefaultMachineFolder(Utf8Str &str) const
2961{
2962 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
2963 str = m->pSystemProperties->m->strDefaultMachineFolder;
2964}
2965
2966/**
2967 * Returns the default hard disk format from the system properties
2968 * with proper locking.
2969 * @return
2970 */
2971void VirtualBox::getDefaultHardDiskFormat(Utf8Str &str) const
2972{
2973 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
2974 str = m->pSystemProperties->m->strDefaultHardDiskFormat;
2975}
2976
2977const Utf8Str& VirtualBox::homeDir() const
2978{
2979 return m->strHomeDir;
2980}
2981
2982/**
2983 * Calculates the absolute path of the given path taking the VirtualBox home
2984 * directory as the current directory.
2985 *
2986 * @param aPath Path to calculate the absolute path for.
2987 * @param aResult Where to put the result (used only on success, can be the
2988 * same Utf8Str instance as passed in @a aPath).
2989 * @return IPRT result.
2990 *
2991 * @note Doesn't lock any object.
2992 */
2993int VirtualBox::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
2994{
2995 AutoCaller autoCaller(this);
2996 AssertComRCReturn(autoCaller.rc(), VERR_GENERAL_FAILURE);
2997
2998 /* no need to lock since mHomeDir is const */
2999
3000 char folder[RTPATH_MAX];
3001 int vrc = RTPathAbsEx(m->strHomeDir.c_str(),
3002 strPath.c_str(),
3003 folder,
3004 sizeof(folder));
3005 if (RT_SUCCESS(vrc))
3006 aResult = folder;
3007
3008 return vrc;
3009}
3010
3011/**
3012 * Copies strSource to strTarget, making it relative to the VirtualBox config folder
3013 * if it is a subdirectory thereof, or simply copying it otherwise.
3014 *
3015 * @param strSource Path to evalue and copy.
3016 * @param strTarget Buffer to receive target path.
3017 */
3018void VirtualBox::copyPathRelativeToConfig(const Utf8Str &strSource,
3019 Utf8Str &strTarget)
3020{
3021 AutoCaller autoCaller(this);
3022 AssertComRCReturnVoid(autoCaller.rc());
3023
3024 // no need to lock since mHomeDir is const
3025
3026 // use strTarget as a temporary buffer to hold the machine settings dir
3027 strTarget = m->strHomeDir;
3028 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
3029 // is relative: then append what's left
3030 strTarget.append(strSource.c_str() + strTarget.length()); // include '/'
3031 else
3032 // is not relative: then overwrite
3033 strTarget = strSource;
3034}
3035
3036// private methods
3037/////////////////////////////////////////////////////////////////////////////
3038
3039/**
3040 * Checks if there is a hard disk, DVD or floppy image with the given ID or
3041 * location already registered.
3042 *
3043 * On return, sets @a aConflict to the string describing the conflicting medium,
3044 * or sets it to @c Null if no conflicting media is found. Returns S_OK in
3045 * either case. A failure is unexpected.
3046 *
3047 * @param aId UUID to check.
3048 * @param aLocation Location to check.
3049 * @param aConflict Where to return parameters of the conflicting medium.
3050 *
3051 * @note Locks the media tree and media objects for reading.
3052 */
3053HRESULT VirtualBox::checkMediaForConflicts(const Guid &aId,
3054 const Utf8Str &aLocation,
3055 Utf8Str &aConflict,
3056 bool &fIdentical)
3057{
3058 aConflict.setNull();
3059
3060 AssertReturn(!aId.isEmpty() && !aLocation.isEmpty(), E_FAIL);
3061
3062 AutoReadLock alock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3063
3064 HRESULT rc = S_OK;
3065
3066 aConflict.setNull();
3067 fIdentical = false;
3068
3069 ComObjPtr<Medium> pMediumFound;
3070 const char *pcszType = NULL;
3071
3072 if (!aId.isEmpty())
3073 rc = findHardDiskById(aId, false /* aSetError */, &pMediumFound);
3074 if (FAILED(rc) && !aLocation.isEmpty())
3075 rc = findHardDiskByLocation(aLocation, false /* aSetError */, &pMediumFound);
3076 if (SUCCEEDED(rc))
3077 pcszType = tr("hard disk");
3078
3079 if (!pcszType)
3080 {
3081 rc = findDVDOrFloppyImage(DeviceType_DVD, &aId, aLocation, false /* aSetError */, &pMediumFound);
3082 if (SUCCEEDED(rc))
3083 pcszType = tr("CD/DVD image");
3084 }
3085
3086 if (!pcszType)
3087 {
3088 rc = findDVDOrFloppyImage(DeviceType_Floppy, &aId, aLocation, false /* aSetError */, &pMediumFound);
3089 if (SUCCEEDED(rc))
3090 pcszType = tr("floppy image");
3091 }
3092
3093 if (pcszType && pMediumFound)
3094 {
3095 /* Note: no AutoCaller since bound to this */
3096 AutoReadLock mlock(pMediumFound COMMA_LOCKVAL_SRC_POS);
3097
3098 Utf8Str strLocFound = pMediumFound->getLocationFull();
3099 Guid idFound = pMediumFound->getId();
3100
3101 if ( (strLocFound == aLocation)
3102 && (idFound == aId)
3103 )
3104 fIdentical = true;
3105
3106 aConflict = Utf8StrFmt(tr("%s '%s' with UUID {%RTuuid}"),
3107 pcszType,
3108 strLocFound.c_str(),
3109 idFound.raw());
3110 }
3111
3112 return S_OK;
3113}
3114
3115/**
3116 * Called from Machine::prepareSaveSettings() when it has detected
3117 * that a machine has been renamed. Such renames will require
3118 * updating the global media registry during the
3119 * VirtualBox::saveSettings() that follows later.
3120*
3121 * When a machine is renamed, there may well be media (in particular,
3122 * diff images for snapshots) in the global registry that will need
3123 * to have their paths updated. Before 3.2, Machine::saveSettings
3124 * used to call VirtualBox::saveSettings implicitly, which was both
3125 * unintuitive and caused locking order problems. Now, we remember
3126 * such pending name changes with this method so that
3127 * VirtualBox::saveSettings() can process them properly.
3128 */
3129void VirtualBox::rememberMachineNameChangeForMedia(const Utf8Str &strOldConfigDir,
3130 const Utf8Str &strNewConfigDir)
3131{
3132 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3133
3134 Data::PendingMachineRename pmr;
3135 pmr.strConfigDirOld = strOldConfigDir;
3136 pmr.strConfigDirNew = strNewConfigDir;
3137 m->llPendingMachineRenames.push_back(pmr);
3138}
3139
3140/**
3141 * Goes through all known media (hard disks, floppies and DVDs) and saves
3142 * those into the given settings::MediaRegistry structures whose registry
3143 * ID match the given UUID.
3144 *
3145 * Before actually writing to the structures, all media paths (not just the
3146 * ones for the given registry) are updated if machines have been renamed
3147 * since the last call.
3148 *
3149 * This gets called from two contexts:
3150 *
3151 * -- VirtualBox::saveSettings() with the UUID of the global registry
3152 * (VirtualBox::Data.uuidRegistry); this will save those media
3153 * which had been loaded from the global registry or have been
3154 * attached to a "legacy" machine which can't save its own registry;
3155 *
3156 * -- Machine::saveSettings() with the UUID of a machine, if a medium
3157 * has been attached to a machine created with VirtualBox 4.0 or later.
3158 *
3159 * Media which have only been temporarily opened without having been
3160 * attached to a machine have a NULL registry UUID and therefore don't
3161 * get saved.
3162 *
3163 * This locks the media tree. Throws HRESULT on errors!
3164 *
3165 * @param mediaRegistry Settings structure to fill.
3166 * @param uuidRegistry The UUID of the media registry; either a machine UUID (if machine registry) or the UUID of the global registry.
3167 * @param hardDiskFolder The machine folder for relative paths, if machine registry, or an empty string otherwise.
3168 */
3169void VirtualBox::saveMediaRegistry(settings::MediaRegistry &mediaRegistry,
3170 const Guid &uuidRegistry,
3171 const Utf8Str &strMachineFolder)
3172{
3173 // lock all media for the following; use a write lock because we're
3174 // modifying the PendingMachineRenamesList, which is protected by this
3175 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3176
3177 // if a machine was renamed, then we'll need to refresh media paths
3178 if (m->llPendingMachineRenames.size())
3179 {
3180 // make a single list from the three media lists so we don't need three loops
3181 MediaList llAllMedia;
3182 // with hard disks, we must use the map, not the list, because the list only has base images
3183 for (HardDiskMap::iterator it = m->mapHardDisks.begin(); it != m->mapHardDisks.end(); ++it)
3184 llAllMedia.push_back(it->second);
3185 for (MediaList::iterator it = m->allDVDImages.begin(); it != m->allDVDImages.end(); ++it)
3186 llAllMedia.push_back(*it);
3187 for (MediaList::iterator it = m->allFloppyImages.begin(); it != m->allFloppyImages.end(); ++it)
3188 llAllMedia.push_back(*it);
3189
3190 for (MediaList::iterator it = llAllMedia.begin();
3191 it != llAllMedia.end();
3192 ++it)
3193 {
3194 Medium *pMedium = *it;
3195 for (Data::PendingMachineRenamesList::iterator it2 = m->llPendingMachineRenames.begin();
3196 it2 != m->llPendingMachineRenames.end();
3197 ++it2)
3198 {
3199 const Data::PendingMachineRename &pmr = *it2;
3200 pMedium->updatePath(pmr.strConfigDirOld,
3201 pmr.strConfigDirNew);
3202 }
3203 }
3204 // done, don't do it again until we have more machine renames
3205 m->llPendingMachineRenames.clear();
3206 }
3207
3208 struct {
3209 MediaOList &llSource;
3210 settings::MediaList &llTarget;
3211 } s[] =
3212 {
3213 // hard disks
3214 { m->allHardDisks, mediaRegistry.llHardDisks },
3215 // CD/DVD images
3216 { m->allDVDImages, mediaRegistry.llDvdImages },
3217 // floppy images
3218 { m->allFloppyImages, mediaRegistry.llFloppyImages }
3219 };
3220
3221 HRESULT rc;
3222
3223 for (size_t i = 0; i < RT_ELEMENTS(s); ++i)
3224 {
3225 MediaOList &llSource = s[i].llSource;
3226 settings::MediaList &llTarget = s[i].llTarget;
3227 llTarget.clear();
3228 for (MediaList::const_iterator it = llSource.begin();
3229 it != llSource.end();
3230 ++it)
3231 {
3232 Medium *pMedium = *it;
3233 AutoCaller autoCaller(pMedium);
3234 if (FAILED(autoCaller.rc())) throw autoCaller.rc();
3235 AutoReadLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
3236
3237 if (pMedium->isInRegistry(uuidRegistry))
3238 {
3239 settings::Medium med;
3240 rc = pMedium->saveSettings(med, strMachineFolder); // this recurses into child hard disks
3241 if (FAILED(rc)) throw rc;
3242 llTarget.push_back(med);
3243 }
3244 }
3245 }
3246}
3247
3248/**
3249 * Helper function which actually writes out VirtualBox.xml, the main configuration file.
3250 * Gets called from the public VirtualBox::SaveSettings() as well as from various other
3251 * places internally when settings need saving.
3252 *
3253 * @note Caller must have locked the VirtualBox object for writing and must not hold any
3254 * other locks since this locks all kinds of member objects and trees temporarily,
3255 * which could cause conflicts.
3256 */
3257HRESULT VirtualBox::saveSettings()
3258{
3259 AutoCaller autoCaller(this);
3260 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3261
3262 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
3263 AssertReturn(!m->strSettingsFilePath.isEmpty(), E_FAIL);
3264
3265 HRESULT rc = S_OK;
3266
3267 try
3268 {
3269 // machines
3270 m->pMainConfigFile->llMachines.clear();
3271 {
3272 AutoReadLock machinesLock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3273 for (MachinesOList::iterator it = m->allMachines.begin();
3274 it != m->allMachines.end();
3275 ++it)
3276 {
3277 Machine *pMachine = *it;
3278 // save actual machine registry entry
3279 settings::MachineRegistryEntry mre;
3280 rc = pMachine->saveRegistryEntry(mre);
3281 m->pMainConfigFile->llMachines.push_back(mre);
3282 }
3283 }
3284
3285 saveMediaRegistry(m->pMainConfigFile->mediaRegistry,
3286 m->uuidMediaRegistry, // global media registry ID
3287 Utf8Str::Empty); // strMachineFolder
3288
3289 m->pMainConfigFile->llDhcpServers.clear();
3290 {
3291 AutoReadLock dhcpLock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3292 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
3293 it != m->allDHCPServers.end();
3294 ++it)
3295 {
3296 settings::DHCPServer d;
3297 rc = (*it)->saveSettings(d);
3298 if (FAILED(rc)) throw rc;
3299 m->pMainConfigFile->llDhcpServers.push_back(d);
3300 }
3301 }
3302
3303 // leave extra data alone, it's still in the config file
3304
3305 // host data (USB filters)
3306 rc = m->pHost->saveSettings(m->pMainConfigFile->host);
3307 if (FAILED(rc)) throw rc;
3308
3309 rc = m->pSystemProperties->saveSettings(m->pMainConfigFile->systemProperties);
3310 if (FAILED(rc)) throw rc;
3311
3312 // and write out the XML, still under the lock
3313 m->pMainConfigFile->write(m->strSettingsFilePath);
3314 }
3315 catch (HRESULT err)
3316 {
3317 /* we assume that error info is set by the thrower */
3318 rc = err;
3319 }
3320 catch (...)
3321 {
3322 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
3323 }
3324
3325 return rc;
3326}
3327
3328/**
3329 * Helper to register the machine.
3330 *
3331 * When called during VirtualBox startup, adds the given machine to the
3332 * collection of registered machines. Otherwise tries to mark the machine
3333 * as registered, and, if succeeded, adds it to the collection and
3334 * saves global settings.
3335 *
3336 * @note The caller must have added itself as a caller of the @a aMachine
3337 * object if calls this method not on VirtualBox startup.
3338 *
3339 * @param aMachine machine to register
3340 *
3341 * @note Locks objects!
3342 */
3343HRESULT VirtualBox::registerMachine(Machine *aMachine)
3344{
3345 ComAssertRet(aMachine, E_INVALIDARG);
3346
3347 AutoCaller autoCaller(this);
3348 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3349
3350 HRESULT rc = S_OK;
3351
3352 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3353
3354 {
3355 ComObjPtr<Machine> pMachine;
3356 rc = findMachine(aMachine->getId(),
3357 true /* fPermitInaccessible */,
3358 false /* aDoSetError */,
3359 &pMachine);
3360 if (SUCCEEDED(rc))
3361 {
3362 /* sanity */
3363 AutoLimitedCaller machCaller(pMachine);
3364 AssertComRC(machCaller.rc());
3365
3366 return setError(E_INVALIDARG,
3367 tr("Registered machine with UUID {%RTuuid} ('%s') already exists"),
3368 aMachine->getId().raw(),
3369 pMachine->getSettingsFileFull().c_str());
3370 }
3371
3372 ComAssertRet(rc == VBOX_E_OBJECT_NOT_FOUND, rc);
3373 rc = S_OK;
3374 }
3375
3376 if (autoCaller.state() != InInit)
3377 {
3378 rc = aMachine->prepareRegister();
3379 if (FAILED(rc)) return rc;
3380 }
3381
3382 /* add to the collection of registered machines */
3383 m->allMachines.addChild(aMachine);
3384
3385 if (autoCaller.state() != InInit)
3386 rc = saveSettings();
3387
3388 return rc;
3389}
3390
3391/**
3392 * Remembers the given hard disk by storing it in either the global hard disk registry
3393 * or a machine one.
3394 *
3395 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3396 *
3397 * @param aHardDisk Hard disk object to remember.
3398 * @param uuidMachineRegistry UUID of machine whose registry should be used, or a NULL UUID for the global registry.
3399 * @param pllRegistriesThatNeedSaving Optional pointer to a list of UUIDs of media registries that need saving.
3400 * @return
3401 */
3402HRESULT VirtualBox::registerHardDisk(Medium *pMedium,
3403 GuidList *pllRegistriesThatNeedSaving)
3404{
3405 AssertReturn(pMedium != NULL, E_INVALIDARG);
3406
3407 AutoCaller autoCaller(this);
3408 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3409
3410 AutoCaller hardDiskCaller(pMedium);
3411 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3412
3413 // caller must hold the media tree write lock
3414 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3415
3416 Guid id;
3417 Utf8Str strLocationFull;
3418 ComObjPtr<Medium> pParent;
3419 {
3420 AutoReadLock hardDiskLock(pMedium COMMA_LOCKVAL_SRC_POS);
3421 id = pMedium->getId();
3422 strLocationFull = pMedium->getLocationFull();
3423 pParent = pMedium->getParent();
3424 }
3425
3426 HRESULT rc;
3427
3428 Utf8Str strConflict;
3429 bool fIdentical;
3430 rc = checkMediaForConflicts(id,
3431 strLocationFull,
3432 strConflict,
3433 fIdentical);
3434 if (FAILED(rc)) return rc;
3435
3436 if (!fIdentical)
3437 {
3438 if (strConflict.length())
3439 return setError(E_INVALIDARG,
3440 tr("Cannot register the hard disk '%s' {%RTuuid} because a %s already exists"),
3441 strLocationFull.c_str(),
3442 id.raw(),
3443 strConflict.c_str(),
3444 m->strSettingsFilePath.c_str());
3445
3446 // store base (root) hard disks in the list
3447 if (pParent.isNull())
3448 m->allHardDisks.getList().push_back(pMedium);
3449 // access the list directly because we already locked the list above
3450
3451 // store all hard disks (even differencing images) in the map
3452 m->mapHardDisks[id] = pMedium;
3453
3454 if (pllRegistriesThatNeedSaving)
3455 pMedium->addToRegistryIDList(*pllRegistriesThatNeedSaving);
3456 }
3457
3458 return rc;
3459}
3460
3461/**
3462 * Removes the given hard disk from the hard disk registry.
3463 *
3464 * @param aHardDisk Hard disk object to remove.
3465 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3466 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3467 *
3468 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3469 */
3470HRESULT VirtualBox::unregisterHardDisk(Medium *aHardDisk,
3471 GuidList *pllRegistriesThatNeedSaving)
3472{
3473 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3474
3475 AutoCaller autoCaller(this);
3476 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3477
3478 AutoCaller hardDiskCaller(aHardDisk);
3479 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3480
3481 // caller must hold the media tree write lock
3482 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3483
3484 Guid id;
3485 ComObjPtr<Medium> pParent;
3486 {
3487 AutoReadLock hardDiskLock(aHardDisk COMMA_LOCKVAL_SRC_POS);
3488 id = aHardDisk->getId();
3489 pParent = aHardDisk->getParent();
3490 }
3491
3492 // remove base (root) hard disks from the list
3493 if (pParent.isNull())
3494 m->allHardDisks.getList().remove(aHardDisk);
3495 // access the list directly because caller must have locked the list
3496
3497 // remove all hard disks (even differencing images) from map
3498 size_t cnt = m->mapHardDisks.erase(id);
3499 Assert(cnt == 1);
3500 NOREF(cnt);
3501
3502 if (pllRegistriesThatNeedSaving)
3503 aHardDisk->addToRegistryIDList(*pllRegistriesThatNeedSaving);
3504
3505 return S_OK;
3506}
3507
3508/**
3509 * Remembers the given image by storing it in the CD/DVD or floppy image registry.
3510 *
3511 * @param argImage Image object to remember.
3512 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3513 * @param uuidMachineRegistry UUID of machine whose registry should be used, or a NULL UUID for the global registry.
3514 *
3515 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3516 */
3517HRESULT VirtualBox::registerImage(Medium *pMedium,
3518 DeviceType_T argType,
3519 GuidList *pllRegistriesThatNeedSaving)
3520{
3521 AssertReturn(pMedium != NULL, E_INVALIDARG);
3522
3523 AutoCaller autoCaller(this);
3524 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3525
3526 AutoCaller imageCaller(pMedium);
3527 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3528
3529 // caller must hold the media tree write lock
3530 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3531
3532 Guid id;
3533 Utf8Str strLocationFull;
3534 ComObjPtr<Medium> pParent;
3535 {
3536 AutoReadLock al(pMedium COMMA_LOCKVAL_SRC_POS);
3537 id = pMedium->getId();
3538 strLocationFull = pMedium->getLocationFull();
3539 pParent = pMedium->getParent();
3540 }
3541
3542 // work on DVDs or floppies list?
3543 ObjectsList<Medium> &all = (argType == DeviceType_DVD) ? m->allDVDImages : m->allFloppyImages;
3544
3545 HRESULT rc;
3546 // lock the images lists (list + map) while checking for conflicts
3547 AutoWriteLock al(all.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3548
3549 Utf8Str strConflict;
3550 bool fIdentical;
3551 rc = checkMediaForConflicts(id,
3552 strLocationFull,
3553 strConflict,
3554 fIdentical);
3555 if (FAILED(rc)) return rc;
3556
3557 if (!fIdentical)
3558 {
3559 if (strConflict.length())
3560 return setError(VBOX_E_INVALID_OBJECT_STATE,
3561 tr("Cannot register the image '%s' with UUID {%RTuuid} because a %s already exists"),
3562 strLocationFull.c_str(),
3563 id.raw(),
3564 strConflict.c_str());
3565
3566 // add to the collection
3567 all.getList().push_back(pMedium);
3568 // access the list directly because we already locked the list above
3569
3570 if (pllRegistriesThatNeedSaving)
3571 pMedium->addToRegistryIDList(*pllRegistriesThatNeedSaving);
3572 }
3573
3574 return rc;
3575}
3576
3577/**
3578 * Removes the given image from the CD/DVD or floppy image registry.
3579 *
3580 * @param argImage Image object to remove.
3581 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3582 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3583 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3584 *
3585 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3586 */
3587HRESULT VirtualBox::unregisterImage(Medium *argImage,
3588 DeviceType_T argType,
3589 GuidList *pllRegistriesThatNeedSaving)
3590{
3591 AssertReturn(argImage != NULL, E_INVALIDARG);
3592
3593 AutoCaller autoCaller(this);
3594 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3595
3596 AutoCaller imageCaller(argImage);
3597 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3598
3599 // caller must hold the media tree write lock
3600 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3601
3602 Guid id;
3603 ComObjPtr<Medium> pParent;
3604 {
3605 AutoReadLock al(argImage COMMA_LOCKVAL_SRC_POS);
3606 id = argImage->getId();
3607 pParent = argImage->getParent();
3608 }
3609
3610 // work on DVDs or floppies list?
3611 ObjectsList<Medium> &all = (argType == DeviceType_DVD) ? m->allDVDImages : m->allFloppyImages;
3612
3613 // access the list directly because the caller must have requested the lock
3614 all.getList().remove(argImage);
3615
3616 HRESULT rc = S_OK;
3617
3618 if (pllRegistriesThatNeedSaving)
3619 argImage->addToRegistryIDList(*pllRegistriesThatNeedSaving);
3620
3621 return rc;
3622}
3623
3624/**
3625 * Little helper called from unregisterMachineMedia() to recursively add media to the given list,
3626 * with children appearing before their parents.
3627 * @param llMedia
3628 * @param pMedium
3629 */
3630void VirtualBox::pushMediumToListWithChildren(MediaList &llMedia, Medium *pMedium)
3631{
3632 // recurse first, then add ourselves; this way children end up on the
3633 // list before their parents
3634
3635 const MediaList &llChildren = pMedium->getChildren();
3636 for (MediaList::const_iterator it = llChildren.begin();
3637 it != llChildren.end();
3638 ++it)
3639 {
3640 Medium *pChild = *it;
3641 pushMediumToListWithChildren(llMedia, pChild);
3642 }
3643
3644 Log(("Pushing medium %RTuuid\n", pMedium->getId().raw()));
3645 llMedia.push_back(pMedium);
3646}
3647
3648/**
3649 * Unregisters all Medium objects which belong to the given machine registry.
3650 * Gets called from Machine::uninit() just before the machine object dies
3651 * and must only be called with a machine UUID as the registry ID.
3652 *
3653 * Locks the media tree.
3654 *
3655 * @param uuidMachine Medium registry ID (always a machine UUID)
3656 * @return
3657 */
3658HRESULT VirtualBox::unregisterMachineMedia(const Guid &uuidMachine)
3659{
3660 Assert(!uuidMachine.isEmpty());
3661
3662 LogFlowFuncEnter();
3663
3664 AutoCaller autoCaller(this);
3665 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3666
3667 MediaList llMedia2Close;
3668
3669 {
3670 AutoWriteLock tlock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3671
3672 for (MediaOList::iterator it = m->allHardDisks.getList().begin();
3673 it != m->allHardDisks.getList().end();
3674 ++it)
3675 {
3676 ComObjPtr<Medium> pMedium = *it;
3677 AutoCaller medCaller(pMedium);
3678 if (FAILED(medCaller.rc())) return medCaller.rc();
3679 AutoReadLock medlock(pMedium COMMA_LOCKVAL_SRC_POS);
3680
3681 if (pMedium->isInRegistry(uuidMachine))
3682 // recursively with children first
3683 pushMediumToListWithChildren(llMedia2Close, pMedium);
3684 }
3685 }
3686
3687 for (MediaList::iterator it = llMedia2Close.begin();
3688 it != llMedia2Close.end();
3689 ++it)
3690 {
3691 ComObjPtr<Medium> pMedium = *it;
3692 Log(("Closing medium %RTuuid\n", pMedium->getId().raw()));
3693 AutoCaller mac(pMedium);
3694 pMedium->close(NULL /* pfNeedsGlobalSaveSettings*/, mac);
3695 }
3696
3697 LogFlowFuncLeave();
3698
3699 return S_OK;
3700}
3701
3702/**
3703 * Removes the given machine object from the internal list of registered machines.
3704 * Called from Machine::Unregister().
3705 * @param pMachine
3706 * @param id UUID of the machine. Must be passed by caller because machine may be dead by this time.
3707 * @return
3708 */
3709HRESULT VirtualBox::unregisterMachine(Machine *pMachine,
3710 const Guid &id)
3711{
3712 // remove from the collection of registered machines
3713 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3714 m->allMachines.removeChild(pMachine);
3715 // save the global registry
3716 HRESULT rc = saveSettings();
3717 alock.release();
3718
3719 /*
3720 * Now go over all known media and checks if they were registered in the
3721 * media registry of the given machine. Each such medium is then moved to
3722 * a different media registry to make sure it doesn't get lost since its
3723 * media registry is about to go away.
3724 *
3725 * This fixes the following use case: Image A.vdi of machine A is also used
3726 * by machine B, but registered in the media registry of machine A. If machine
3727 * A is deleted, A.vdi must be moved to the registry of B, or else B will
3728 * become inaccessible.
3729 */
3730 GuidList llRegistriesThatNeedSaving;
3731 {
3732 AutoReadLock tlock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3733 // iterate over the list of *base* images
3734 for (MediaOList::iterator it = m->allHardDisks.getList().begin();
3735 it != m->allHardDisks.getList().end();
3736 ++it)
3737 {
3738 ComObjPtr<Medium> &pMedium = *it;
3739 AutoCaller medCaller(pMedium);
3740 if (FAILED(medCaller.rc())) return medCaller.rc();
3741 AutoWriteLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
3742
3743 if (pMedium->removeRegistry(id, true /* fRecurse */))
3744 {
3745 // machine ID was found in base medium's registry list:
3746 // move this base image and all its children to another registry then
3747 // 1) first, find a better registry to add things to
3748 const Guid *puuidBetter = pMedium->getAnyMachineBackref();
3749 if (puuidBetter)
3750 {
3751 // 2) better registry found: then use that
3752 pMedium->addRegistry(*puuidBetter, true /* fRecurse */);
3753 // 3) and make sure the registry is saved below
3754 addGuidToListUniquely(llRegistriesThatNeedSaving, *puuidBetter);
3755 }
3756 }
3757 }
3758 }
3759
3760 saveRegistries(llRegistriesThatNeedSaving);
3761
3762 /* fire an event */
3763 onMachineRegistered(id, FALSE);
3764
3765 return rc;
3766}
3767
3768/**
3769 * Adds uuid to llRegistriesThatNeedSaving unless it's already on the list.
3770 *
3771 * @todo maybe there's something in libstdc++ for this
3772 *
3773 * @param llRegistriesThatNeedSaving
3774 * @param uuid
3775 */
3776void VirtualBox::addGuidToListUniquely(GuidList &llRegistriesThatNeedSaving,
3777 const Guid &uuid)
3778{
3779 for (GuidList::const_iterator it = llRegistriesThatNeedSaving.begin();
3780 it != llRegistriesThatNeedSaving.end();
3781 ++it)
3782 {
3783 if (*it == uuid)
3784 // uuid is already in list:
3785 return;
3786 }
3787
3788 llRegistriesThatNeedSaving.push_back(uuid);
3789}
3790
3791/**
3792 * Saves all settings files according to the given list of UUIDs, which are
3793 * either machine IDs (in which case Machine::saveSettings is invoked) or
3794 * the global registry UUID (in which case VirtualBox::saveSettings is invoked).
3795 *
3796 * This locks machines and the VirtualBox object as necessary, so better not
3797 * hold any locks before calling this.
3798 *
3799 * @param llRegistriesThatNeedSaving
3800 * @return
3801 */
3802HRESULT VirtualBox::saveRegistries(const GuidList &llRegistriesThatNeedSaving)
3803{
3804 bool fNeedsGlobalSettings = false;
3805 HRESULT rc = S_OK;
3806
3807 for (GuidList::const_iterator it = llRegistriesThatNeedSaving.begin();
3808 it != llRegistriesThatNeedSaving.end();
3809 ++it)
3810 {
3811 const Guid &uuid = *it;
3812
3813 if (uuid == getGlobalRegistryId())
3814 fNeedsGlobalSettings = true;
3815 else
3816 {
3817 // should be machine ID then:
3818 ComObjPtr<Machine> pMachine;
3819 rc = findMachine(uuid,
3820 false /* fPermitInaccessible */,
3821 false /* aSetError */,
3822 &pMachine);
3823 if (SUCCEEDED(rc))
3824 {
3825 AutoCaller autoCaller(pMachine);
3826 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3827 AutoWriteLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
3828 rc = pMachine->saveSettings(&fNeedsGlobalSettings,
3829 Machine::SaveS_Force); // caller said save, so stop arguing
3830 }
3831
3832 if (FAILED(rc))
3833 return rc;
3834 }
3835 }
3836
3837 if (fNeedsGlobalSettings)
3838 {
3839 AutoWriteLock vlock(this COMMA_LOCKVAL_SRC_POS);
3840 rc = saveSettings();
3841 }
3842
3843 return S_OK;
3844}
3845
3846/**
3847 * Creates the path to the specified file according to the path information
3848 * present in the file name.
3849 *
3850 * Note that the given file name must contain the full path otherwise the
3851 * extracted relative path will be created based on the current working
3852 * directory which is normally unknown.
3853 *
3854 * @param aFileName Full file name which path needs to be created.
3855 *
3856 * @return Extended error information on failure to create the path.
3857 */
3858/* static */
3859HRESULT VirtualBox::ensureFilePathExists(const Utf8Str &strFileName)
3860{
3861 Utf8Str strDir(strFileName);
3862 strDir.stripFilename();
3863 if (!RTDirExists(strDir.c_str()))
3864 {
3865 int vrc = RTDirCreateFullPath(strDir.c_str(), 0777);
3866 if (RT_FAILURE(vrc))
3867 return setErrorStatic(E_FAIL,
3868 Utf8StrFmt(tr("Could not create the directory '%s' (%Rrc)"),
3869 strDir.c_str(),
3870 vrc));
3871 }
3872
3873 return S_OK;
3874}
3875
3876/**
3877 * Handles unexpected exceptions by turning them into COM errors in release
3878 * builds or by hitting a breakpoint in the release builds.
3879 *
3880 * Usage pattern:
3881 * @code
3882 try
3883 {
3884 // ...
3885 }
3886 catch (LaLalA)
3887 {
3888 // ...
3889 }
3890 catch (...)
3891 {
3892 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3893 }
3894 * @endcode
3895 *
3896 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
3897 */
3898/* static */
3899HRESULT VirtualBox::handleUnexpectedExceptions(RT_SRC_POS_DECL)
3900{
3901 try
3902 {
3903 /* re-throw the current exception */
3904 throw;
3905 }
3906 catch (const RTCError &err) // includes all XML exceptions
3907 {
3908 return setErrorStatic(E_FAIL,
3909 Utf8StrFmt(tr("%s.\n%s[%d] (%s)"),
3910 err.what(),
3911 pszFile, iLine, pszFunction).c_str());
3912 }
3913 catch (const std::exception &err)
3914 {
3915 return setErrorStatic(E_FAIL,
3916 Utf8StrFmt(tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
3917 err.what(), typeid(err).name(),
3918 pszFile, iLine, pszFunction).c_str());
3919 }
3920 catch (...)
3921 {
3922 return setErrorStatic(E_FAIL,
3923 Utf8StrFmt(tr("Unknown exception\n%s[%d] (%s)"),
3924 pszFile, iLine, pszFunction).c_str());
3925 }
3926
3927 /* should not get here */
3928 AssertFailed();
3929 return E_FAIL;
3930}
3931
3932const Utf8Str& VirtualBox::settingsFilePath()
3933{
3934 return m->strSettingsFilePath;
3935}
3936
3937/**
3938 * Returns the lock handle which protects the media trees (hard disks,
3939 * DVDs, floppies). As opposed to version 3.1 and earlier, these lists
3940 * are no longer protected by the VirtualBox lock, but by this more
3941 * specialized lock. Mind the locking order: always request this lock
3942 * after the VirtualBox object lock but before the locks of the media
3943 * objects contained in these lists. See AutoLock.h.
3944 */
3945RWLockHandle& VirtualBox::getMediaTreeLockHandle()
3946{
3947 return m->lockMedia;
3948}
3949
3950/**
3951 * Thread function that watches the termination of all client processes
3952 * that have opened sessions using IMachine::LockMachine()
3953 */
3954// static
3955DECLCALLBACK(int) VirtualBox::ClientWatcher(RTTHREAD /* thread */, void *pvUser)
3956{
3957 LogFlowFuncEnter();
3958
3959 VirtualBox *that = (VirtualBox*)pvUser;
3960 Assert(that);
3961
3962 typedef std::vector< ComObjPtr<Machine> > MachineVector;
3963 typedef std::vector< ComObjPtr<SessionMachine> > SessionMachineVector;
3964
3965 SessionMachineVector machines;
3966 MachineVector spawnedMachines;
3967
3968 size_t cnt = 0;
3969 size_t cntSpawned = 0;
3970
3971 VirtualBoxBase::initializeComForThread();
3972
3973#if defined(RT_OS_WINDOWS)
3974
3975 HRESULT hrc;
3976
3977 /// @todo (dmik) processes reaping!
3978
3979 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
3980 handles[0] = that->m->updateReq;
3981
3982 do
3983 {
3984 AutoCaller autoCaller(that);
3985 /* VirtualBox has been early uninitialized, terminate */
3986 if (!autoCaller.isOk())
3987 break;
3988
3989 do
3990 {
3991 /* release the caller to let uninit() ever proceed */
3992 autoCaller.release();
3993
3994 DWORD rc = ::WaitForMultipleObjects((DWORD)(1 + cnt + cntSpawned),
3995 handles,
3996 FALSE,
3997 INFINITE);
3998
3999 /* Restore the caller before using VirtualBox. If it fails, this
4000 * means VirtualBox is being uninitialized and we must terminate. */
4001 autoCaller.add();
4002 if (!autoCaller.isOk())
4003 break;
4004
4005 bool update = false;
4006
4007 if (rc == WAIT_OBJECT_0)
4008 {
4009 /* update event is signaled */
4010 update = true;
4011 }
4012 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
4013 {
4014 /* machine mutex is released */
4015 (machines[rc - WAIT_OBJECT_0 - 1])->checkForDeath();
4016 update = true;
4017 }
4018 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
4019 {
4020 /* machine mutex is abandoned due to client process termination */
4021 (machines[rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
4022 update = true;
4023 }
4024 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
4025 {
4026 /* spawned VM process has terminated (normally or abnormally) */
4027 (spawnedMachines[rc - WAIT_OBJECT_0 - cnt - 1])->
4028 checkForSpawnFailure();
4029 update = true;
4030 }
4031
4032 if (update)
4033 {
4034 /* close old process handles */
4035 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i)
4036 CloseHandle(handles[i]);
4037
4038 // lock the machines list for reading
4039 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4040
4041 /* obtain a new set of opened machines */
4042 cnt = 0;
4043 machines.clear();
4044
4045 for (MachinesOList::iterator it = that->m->allMachines.begin();
4046 it != that->m->allMachines.end();
4047 ++it)
4048 {
4049 /// @todo handle situations with more than 64 objects
4050 AssertMsgBreak((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
4051 ("MAXIMUM_WAIT_OBJECTS reached"));
4052
4053 ComObjPtr<SessionMachine> sm;
4054 HANDLE ipcSem;
4055 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
4056 {
4057 machines.push_back(sm);
4058 handles[1 + cnt] = ipcSem;
4059 ++cnt;
4060 }
4061 }
4062
4063 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4064
4065 /* obtain a new set of spawned machines */
4066 cntSpawned = 0;
4067 spawnedMachines.clear();
4068
4069 for (MachinesOList::iterator it = that->m->allMachines.begin();
4070 it != that->m->allMachines.end();
4071 ++it)
4072 {
4073 /// @todo handle situations with more than 64 objects
4074 AssertMsgBreak((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
4075 ("MAXIMUM_WAIT_OBJECTS reached"));
4076
4077 RTPROCESS pid;
4078 if ((*it)->isSessionSpawning(&pid))
4079 {
4080 HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid);
4081 AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
4082 pid, GetLastError()));
4083 if (rc == 0)
4084 {
4085 spawnedMachines.push_back(*it);
4086 handles[1 + cnt + cntSpawned] = ph;
4087 ++cntSpawned;
4088 }
4089 }
4090 }
4091
4092 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4093
4094 // machines lock unwinds here
4095 }
4096 }
4097 while (true);
4098 }
4099 while (0);
4100
4101 /* close old process handles */
4102 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
4103 CloseHandle(handles[i]);
4104
4105 /* release sets of machines if any */
4106 machines.clear();
4107 spawnedMachines.clear();
4108
4109 ::CoUninitialize();
4110
4111#elif defined(RT_OS_OS2)
4112
4113 /// @todo (dmik) processes reaping!
4114
4115 /* according to PMREF, 64 is the maximum for the muxwait list */
4116 SEMRECORD handles[64];
4117
4118 HMUX muxSem = NULLHANDLE;
4119
4120 do
4121 {
4122 AutoCaller autoCaller(that);
4123 /* VirtualBox has been early uninitialized, terminate */
4124 if (!autoCaller.isOk())
4125 break;
4126
4127 do
4128 {
4129 /* release the caller to let uninit() ever proceed */
4130 autoCaller.release();
4131
4132 int vrc = RTSemEventWait(that->m->updateReq, 500);
4133
4134 /* Restore the caller before using VirtualBox. If it fails, this
4135 * means VirtualBox is being uninitialized and we must terminate. */
4136 autoCaller.add();
4137 if (!autoCaller.isOk())
4138 break;
4139
4140 bool update = false;
4141 bool updateSpawned = false;
4142
4143 if (RT_SUCCESS(vrc))
4144 {
4145 /* update event is signaled */
4146 update = true;
4147 updateSpawned = true;
4148 }
4149 else
4150 {
4151 AssertMsg(vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
4152 ("RTSemEventWait returned %Rrc\n", vrc));
4153
4154 /* are there any mutexes? */
4155 if (cnt > 0)
4156 {
4157 /* figure out what's going on with machines */
4158
4159 unsigned long semId = 0;
4160 APIRET arc = ::DosWaitMuxWaitSem(muxSem,
4161 SEM_IMMEDIATE_RETURN, &semId);
4162
4163 if (arc == NO_ERROR)
4164 {
4165 /* machine mutex is normally released */
4166 Assert(semId >= 0 && semId < cnt);
4167 if (semId >= 0 && semId < cnt)
4168 {
4169#if 0//def DEBUG
4170 {
4171 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
4172 LogFlowFunc(("released mutex: machine='%ls'\n",
4173 machines[semId]->name().raw()));
4174 }
4175#endif
4176 machines[semId]->checkForDeath();
4177 }
4178 update = true;
4179 }
4180 else if (arc == ERROR_SEM_OWNER_DIED)
4181 {
4182 /* machine mutex is abandoned due to client process
4183 * termination; find which mutex is in the Owner Died
4184 * state */
4185 for (size_t i = 0; i < cnt; ++ i)
4186 {
4187 PID pid; TID tid;
4188 unsigned long reqCnt;
4189 arc = DosQueryMutexSem((HMTX)handles[i].hsemCur, &pid, &tid, &reqCnt);
4190 if (arc == ERROR_SEM_OWNER_DIED)
4191 {
4192 /* close the dead mutex as asked by PMREF */
4193 ::DosCloseMutexSem((HMTX)handles[i].hsemCur);
4194
4195 Assert(i >= 0 && i < cnt);
4196 if (i >= 0 && i < cnt)
4197 {
4198#if 0//def DEBUG
4199 {
4200 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
4201 LogFlowFunc(("mutex owner dead: machine='%ls'\n",
4202 machines[i]->name().raw()));
4203 }
4204#endif
4205 machines[i]->checkForDeath();
4206 }
4207 }
4208 }
4209 update = true;
4210 }
4211 else
4212 AssertMsg(arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
4213 ("DosWaitMuxWaitSem returned %d\n", arc));
4214 }
4215
4216 /* are there any spawning sessions? */
4217 if (cntSpawned > 0)
4218 {
4219 for (size_t i = 0; i < cntSpawned; ++ i)
4220 updateSpawned |= (spawnedMachines[i])->
4221 checkForSpawnFailure();
4222 }
4223 }
4224
4225 if (update || updateSpawned)
4226 {
4227 AutoReadLock thatLock(that COMMA_LOCKVAL_SRC_POS);
4228
4229 if (update)
4230 {
4231 /* close the old muxsem */
4232 if (muxSem != NULLHANDLE)
4233 ::DosCloseMuxWaitSem(muxSem);
4234
4235 /* obtain a new set of opened machines */
4236 cnt = 0;
4237 machines.clear();
4238
4239 for (MachinesOList::iterator it = that->m->allMachines.begin();
4240 it != that->m->allMachines.end(); ++ it)
4241 {
4242 /// @todo handle situations with more than 64 objects
4243 AssertMsg(cnt <= 64 /* according to PMREF */,
4244 ("maximum of 64 mutex semaphores reached (%d)",
4245 cnt));
4246
4247 ComObjPtr<SessionMachine> sm;
4248 HMTX ipcSem;
4249 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
4250 {
4251 machines.push_back(sm);
4252 handles[cnt].hsemCur = (HSEM)ipcSem;
4253 handles[cnt].ulUser = cnt;
4254 ++ cnt;
4255 }
4256 }
4257
4258 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4259
4260 if (cnt > 0)
4261 {
4262 /* create a new muxsem */
4263 APIRET arc = ::DosCreateMuxWaitSem(NULL, &muxSem, cnt,
4264 handles,
4265 DCMW_WAIT_ANY);
4266 AssertMsg(arc == NO_ERROR,
4267 ("DosCreateMuxWaitSem returned %d\n", arc));
4268 NOREF(arc);
4269 }
4270 }
4271
4272 if (updateSpawned)
4273 {
4274 /* obtain a new set of spawned machines */
4275 spawnedMachines.clear();
4276
4277 for (MachinesOList::iterator it = that->m->allMachines.begin();
4278 it != that->m->allMachines.end(); ++ it)
4279 {
4280 if ((*it)->isSessionSpawning())
4281 spawnedMachines.push_back(*it);
4282 }
4283
4284 cntSpawned = spawnedMachines.size();
4285 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4286 }
4287 }
4288 }
4289 while (true);
4290 }
4291 while (0);
4292
4293 /* close the muxsem */
4294 if (muxSem != NULLHANDLE)
4295 ::DosCloseMuxWaitSem(muxSem);
4296
4297 /* release sets of machines if any */
4298 machines.clear();
4299 spawnedMachines.clear();
4300
4301#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
4302
4303 bool update = false;
4304 bool updateSpawned = false;
4305
4306 do
4307 {
4308 AutoCaller autoCaller(that);
4309 if (!autoCaller.isOk())
4310 break;
4311
4312 do
4313 {
4314 /* release the caller to let uninit() ever proceed */
4315 autoCaller.release();
4316
4317 int rc = RTSemEventWait(that->m->updateReq, 500);
4318
4319 /*
4320 * Restore the caller before using VirtualBox. If it fails, this
4321 * means VirtualBox is being uninitialized and we must terminate.
4322 */
4323 autoCaller.add();
4324 if (!autoCaller.isOk())
4325 break;
4326
4327 if (RT_SUCCESS(rc) || update || updateSpawned)
4328 {
4329 /* RT_SUCCESS(rc) means an update event is signaled */
4330
4331 // lock the machines list for reading
4332 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4333
4334 if (RT_SUCCESS(rc) || update)
4335 {
4336 /* obtain a new set of opened machines */
4337 machines.clear();
4338
4339 for (MachinesOList::iterator it = that->m->allMachines.begin();
4340 it != that->m->allMachines.end();
4341 ++it)
4342 {
4343 ComObjPtr<SessionMachine> sm;
4344 if ((*it)->isSessionOpenOrClosing(sm))
4345 machines.push_back(sm);
4346 }
4347
4348 cnt = machines.size();
4349 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4350 }
4351
4352 if (RT_SUCCESS(rc) || updateSpawned)
4353 {
4354 /* obtain a new set of spawned machines */
4355 spawnedMachines.clear();
4356
4357 for (MachinesOList::iterator it = that->m->allMachines.begin();
4358 it != that->m->allMachines.end();
4359 ++it)
4360 {
4361 if ((*it)->isSessionSpawning())
4362 spawnedMachines.push_back(*it);
4363 }
4364
4365 cntSpawned = spawnedMachines.size();
4366 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4367 }
4368
4369 // machines lock unwinds here
4370 }
4371
4372 update = false;
4373 for (size_t i = 0; i < cnt; ++ i)
4374 update |= (machines[i])->checkForDeath();
4375
4376 updateSpawned = false;
4377 for (size_t i = 0; i < cntSpawned; ++ i)
4378 updateSpawned |= (spawnedMachines[i])->checkForSpawnFailure();
4379
4380 /* reap child processes */
4381 {
4382 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
4383 if (that->m->llProcesses.size())
4384 {
4385 LogFlowFunc(("UPDATE: child process count = %d\n",
4386 that->m->llProcesses.size()));
4387 VirtualBox::Data::ProcessList::iterator it = that->m->llProcesses.begin();
4388 while (it != that->m->llProcesses.end())
4389 {
4390 RTPROCESS pid = *it;
4391 RTPROCSTATUS status;
4392 int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status);
4393 if (vrc == VINF_SUCCESS)
4394 {
4395 LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n",
4396 pid, pid, status.iStatus,
4397 status.enmReason));
4398 it = that->m->llProcesses.erase(it);
4399 }
4400 else
4401 {
4402 LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
4403 pid, pid, vrc));
4404 if (vrc != VERR_PROCESS_RUNNING)
4405 {
4406 /* remove the process if it is not already running */
4407 it = that->m->llProcesses.erase(it);
4408 }
4409 else
4410 ++ it;
4411 }
4412 }
4413 }
4414 }
4415 }
4416 while (true);
4417 }
4418 while (0);
4419
4420 /* release sets of machines if any */
4421 machines.clear();
4422 spawnedMachines.clear();
4423
4424#else
4425# error "Port me!"
4426#endif
4427
4428 VirtualBoxBase::uninitializeComForThread();
4429 LogFlowFuncLeave();
4430 return 0;
4431}
4432
4433/**
4434 * Thread function that handles custom events posted using #postEvent().
4435 */
4436// static
4437DECLCALLBACK(int) VirtualBox::AsyncEventHandler(RTTHREAD thread, void *pvUser)
4438{
4439 LogFlowFuncEnter();
4440
4441 AssertReturn(pvUser, VERR_INVALID_POINTER);
4442
4443 com::Initialize();
4444
4445 // create an event queue for the current thread
4446 EventQueue *eventQ = new EventQueue();
4447 AssertReturn(eventQ, VERR_NO_MEMORY);
4448
4449 // return the queue to the one who created this thread
4450 *(static_cast <EventQueue **>(pvUser)) = eventQ;
4451 // signal that we're ready
4452 RTThreadUserSignal(thread);
4453
4454 while (RT_SUCCESS(eventQ->processEventQueue(RT_INDEFINITE_WAIT)))
4455 /* nothing */ ;
4456
4457 delete eventQ;
4458
4459 com::Shutdown();
4460
4461
4462 LogFlowFuncLeave();
4463
4464 return 0;
4465}
4466
4467
4468////////////////////////////////////////////////////////////////////////////////
4469
4470/**
4471 * Takes the current list of registered callbacks of the managed VirtualBox
4472 * instance, and calls #handleCallback() for every callback item from the
4473 * list, passing the item as an argument.
4474 *
4475 * @note Locks the managed VirtualBox object for reading but leaves the lock
4476 * before iterating over callbacks and calling their methods.
4477 */
4478void *VirtualBox::CallbackEvent::handler()
4479{
4480 if (!mVirtualBox)
4481 return NULL;
4482
4483 AutoCaller autoCaller(mVirtualBox);
4484 if (!autoCaller.isOk())
4485 {
4486 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",
4487 autoCaller.state()));
4488 /* We don't need mVirtualBox any more, so release it */
4489 mVirtualBox = NULL;
4490 return NULL;
4491 }
4492
4493 {
4494 VBoxEventDesc evDesc;
4495 prepareEventDesc(mVirtualBox->m->pEventSource, evDesc);
4496
4497 evDesc.fire(/* don't wait for delivery */0);
4498 }
4499
4500 mVirtualBox = NULL; /* Not needed any longer. Still make sense to do this? */
4501 return NULL;
4502}
4503
4504//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface(/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
4505//{
4506// return E_NOTIMPL;
4507//}
4508
4509STDMETHODIMP VirtualBox::CreateDHCPServer(IN_BSTR aName, IDHCPServer ** aServer)
4510{
4511 CheckComArgStrNotEmptyOrNull(aName);
4512 CheckComArgNotNull(aServer);
4513
4514 AutoCaller autoCaller(this);
4515 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4516
4517 ComObjPtr<DHCPServer> dhcpServer;
4518 dhcpServer.createObject();
4519 HRESULT rc = dhcpServer->init(this, aName);
4520 if (FAILED(rc)) return rc;
4521
4522 rc = registerDHCPServer(dhcpServer, true);
4523 if (FAILED(rc)) return rc;
4524
4525 dhcpServer.queryInterfaceTo(aServer);
4526
4527 return rc;
4528}
4529
4530STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName(IN_BSTR aName, IDHCPServer ** aServer)
4531{
4532 CheckComArgStrNotEmptyOrNull(aName);
4533 CheckComArgNotNull(aServer);
4534
4535 AutoCaller autoCaller(this);
4536 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4537
4538 HRESULT rc;
4539 Bstr bstr;
4540 ComPtr<DHCPServer> found;
4541
4542 AutoReadLock alock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4543
4544 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
4545 it != m->allDHCPServers.end();
4546 ++it)
4547 {
4548 rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());
4549 if (FAILED(rc)) return rc;
4550
4551 if (bstr == aName)
4552 {
4553 found = *it;
4554 break;
4555 }
4556 }
4557
4558 if (!found)
4559 return E_INVALIDARG;
4560
4561 return found.queryInterfaceTo(aServer);
4562}
4563
4564STDMETHODIMP VirtualBox::RemoveDHCPServer(IDHCPServer * aServer)
4565{
4566 CheckComArgNotNull(aServer);
4567
4568 AutoCaller autoCaller(this);
4569 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4570
4571 HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);
4572
4573 return rc;
4574}
4575
4576/**
4577 * Remembers the given dhcp server by storing it in the hard disk registry.
4578 *
4579 * @param aDHCPServer Dhcp Server object to remember.
4580 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4581 *
4582 * When @a aSaveRegistry is @c true, this operation may fail because of the
4583 * failed #saveSettings() method it calls. In this case, the dhcp server object
4584 * will not be remembered. It is therefore the responsibility of the caller to
4585 * call this method as the last step of some action that requires registration
4586 * in order to make sure that only fully functional dhcp server objects get
4587 * registered.
4588 *
4589 * @note Locks this object for writing and @a aDHCPServer for reading.
4590 */
4591HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,
4592 bool aSaveRegistry /*= true*/)
4593{
4594 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4595
4596 AutoCaller autoCaller(this);
4597 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4598
4599 AutoCaller dhcpServerCaller(aDHCPServer);
4600 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4601
4602 Bstr name;
4603 HRESULT rc;
4604 rc = aDHCPServer->COMGETTER(NetworkName)(name.asOutParam());
4605 if (FAILED(rc)) return rc;
4606
4607 ComPtr<IDHCPServer> existing;
4608 rc = FindDHCPServerByNetworkName(name.raw(), existing.asOutParam());
4609 if (SUCCEEDED(rc))
4610 return E_INVALIDARG;
4611
4612 rc = S_OK;
4613
4614 m->allDHCPServers.addChild(aDHCPServer);
4615
4616 if (aSaveRegistry)
4617 {
4618 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4619 rc = saveSettings();
4620 vboxLock.release();
4621
4622 if (FAILED(rc))
4623 unregisterDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4624 }
4625
4626 return rc;
4627}
4628
4629/**
4630 * Removes the given hard disk from the hard disk registry.
4631 *
4632 * @param aHardDisk Hard disk object to remove.
4633 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4634 *
4635 * When @a aSaveRegistry is @c true, this operation may fail because of the
4636 * failed #saveSettings() method it calls. In this case, the hard disk object
4637 * will NOT be removed from the registry when this method returns. It is
4638 * therefore the responsibility of the caller to call this method as the first
4639 * step of some action that requires unregistration, before calling uninit() on
4640 * @a aHardDisk.
4641 *
4642 * @note Locks this object for writing and @a aHardDisk for reading.
4643 */
4644HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,
4645 bool aSaveRegistry /*= true*/)
4646{
4647 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4648
4649 AutoCaller autoCaller(this);
4650 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4651
4652 AutoCaller dhcpServerCaller(aDHCPServer);
4653 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4654
4655 m->allDHCPServers.removeChild(aDHCPServer);
4656
4657 HRESULT rc = S_OK;
4658
4659 if (aSaveRegistry)
4660 {
4661 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4662 rc = saveSettings();
4663 vboxLock.release();
4664
4665 if (FAILED(rc))
4666 registerDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4667 }
4668
4669 return rc;
4670}
4671
4672/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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