VirtualBox

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

Last change on this file since 37952 was 37928, checked in by vboxsync, 13 years ago

Main/MachineDataChangedEvent: new attribute, currently unimplemented

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

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