VirtualBox

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

Last change on this file since 37924 was 37779, checked in by vboxsync, 14 years ago

Main/VirtualBox: add two attributes for querying the list of internal networks and generic network drivers
Frontend/VirtualBox: use new methods to decrease time needed before the settings dialog opens

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

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