VirtualBox

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

Last change on this file since 29034 was 28955, checked in by vboxsync, 14 years ago

VirtualBox::RegisterCallback,Console::RegisterCallback: Explicitly query the interface instead of trusting it to be of the right kind. (ComPtr<> doesn't throw errors if it's QueryInterface fails, so we'll crash if we're not careful here.)

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