VirtualBox

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

Last change on this file since 28930 was 28930, checked in by vboxsync, 15 years ago

Main/IVirtualBoxCallback: Added and implemented handling of VBOX_E_DONT_CALL_AGAIN. This is an optimization for reducing unnecessary IPC caused by NOP methods in IVirtualBoxCallback implementations.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 146.6 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 28930 2010-04-30 12:03:36Z 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#if 0 /** @todo r=bird,r=pritesh: must check that the interface id match correct or we might screw up with old code! */
2068 void *dummy;
2069 HRESULT hrc = aCallback->QueryInterface(NS_GET_IID(IVirtualBoxCallback), &dummy);
2070 if (FAILED(hrc))
2071 return hrc;
2072 aCallback->Release();
2073#endif
2074
2075 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2076 m->llCallbacks.push_back(CallbackList::value_type(aCallback));
2077
2078 return S_OK;
2079}
2080
2081/**
2082 * @note Locks this object for writing.
2083 */
2084STDMETHODIMP VirtualBox::UnregisterCallback(IVirtualBoxCallback *aCallback)
2085{
2086 CheckComArgNotNull(aCallback);
2087
2088 AutoCaller autoCaller(this);
2089 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2090
2091 HRESULT rc = S_OK;
2092
2093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2094
2095 CallbackList::iterator it;
2096 it = std::find(m->llCallbacks.begin(),
2097 m->llCallbacks.end(),
2098 CallbackList::value_type(aCallback));
2099 if (it == m->llCallbacks.end())
2100 rc = E_INVALIDARG;
2101 else
2102 m->llCallbacks.erase(it);
2103
2104 LogFlowThisFunc(("aCallback=%p, rc=%08X\n", aCallback, rc));
2105 return rc;
2106}
2107
2108
2109STDMETHODIMP VirtualBox::WaitForPropertyChange(IN_BSTR /* aWhat */,
2110 ULONG /* aTimeout */,
2111 BSTR * /* aChanged */,
2112 BSTR * /* aValues */)
2113{
2114 ReturnComNotImplemented();
2115}
2116
2117// public methods only for internal purposes
2118/////////////////////////////////////////////////////////////////////////////
2119
2120#ifdef DEBUG
2121void VirtualBox::dumpAllBackRefs()
2122{
2123 {
2124 AutoReadLock al(m->ollHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2125 for (MediaList::const_iterator mt = m->ollHardDisks.begin();
2126 mt != m->ollHardDisks.end();
2127 ++mt)
2128 {
2129 ComObjPtr<Medium> pMedium = *mt;
2130 pMedium->dumpBackRefs();
2131 }
2132 }
2133 {
2134 AutoReadLock al(m->ollDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2135 for (MediaList::const_iterator mt = m->ollDVDImages.begin();
2136 mt != m->ollDVDImages.end();
2137 ++mt)
2138 {
2139 ComObjPtr<Medium> pMedium = *mt;
2140 pMedium->dumpBackRefs();
2141 }
2142 }
2143}
2144#endif
2145
2146/**
2147 * Posts an event to the event queue that is processed asynchronously
2148 * on a dedicated thread.
2149 *
2150 * Posting events to the dedicated event queue is useful to perform secondary
2151 * actions outside any object locks -- for example, to iterate over a list
2152 * of callbacks and inform them about some change caused by some object's
2153 * method call.
2154 *
2155 * @param event event to post
2156 * (must be allocated using |new|, will be deleted automatically
2157 * by the event thread after processing)
2158 *
2159 * @note Doesn't lock any object.
2160 */
2161HRESULT VirtualBox::postEvent(Event *event)
2162{
2163 AutoCaller autoCaller(this);
2164 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2165
2166 if (autoCaller.state() != Ready)
2167 {
2168 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the event is discarded!\n",
2169 autoCaller.state()));
2170 return S_OK;
2171 }
2172
2173 AssertReturn(event, E_FAIL);
2174 AssertReturn(m->pAsyncEventQ, E_FAIL);
2175
2176 if (m->pAsyncEventQ->postEvent(event))
2177 return S_OK;
2178
2179 return E_FAIL;
2180}
2181
2182/**
2183 * Adds a progress to the global collection of pending operations.
2184 * Usually gets called upon progress object initialization.
2185 *
2186 * @param aProgress Operation to add to the collection.
2187 *
2188 * @note Doesn't lock objects.
2189 */
2190HRESULT VirtualBox::addProgress(IProgress *aProgress)
2191{
2192 CheckComArgNotNull(aProgress);
2193
2194 AutoCaller autoCaller(this);
2195 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2196
2197 Bstr id;
2198 HRESULT rc = aProgress->COMGETTER(Id)(id.asOutParam());
2199 AssertComRCReturnRC(rc);
2200
2201 /* protect mProgressOperations */
2202 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
2203
2204 m->mapProgressOperations.insert(ProgressMap::value_type(Guid(id), aProgress));
2205 return S_OK;
2206}
2207
2208/**
2209 * Removes the progress from the global collection of pending operations.
2210 * Usually gets called upon progress completion.
2211 *
2212 * @param aId UUID of the progress operation to remove
2213 *
2214 * @note Doesn't lock objects.
2215 */
2216HRESULT VirtualBox::removeProgress(IN_GUID aId)
2217{
2218 AutoCaller autoCaller(this);
2219 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2220
2221 ComPtr<IProgress> progress;
2222
2223 /* protect mProgressOperations */
2224 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
2225
2226 size_t cnt = m->mapProgressOperations.erase(aId);
2227 Assert(cnt == 1);
2228 NOREF(cnt);
2229
2230 return S_OK;
2231}
2232
2233#ifdef RT_OS_WINDOWS
2234
2235struct StartSVCHelperClientData
2236{
2237 ComObjPtr<VirtualBox> that;
2238 ComObjPtr<Progress> progress;
2239 bool privileged;
2240 VirtualBox::SVCHelperClientFunc func;
2241 void *user;
2242};
2243
2244/**
2245 * Helper method that starts a worker thread that:
2246 * - creates a pipe communication channel using SVCHlpClient;
2247 * - starts an SVC Helper process that will inherit this channel;
2248 * - executes the supplied function by passing it the created SVCHlpClient
2249 * and opened instance to communicate to the Helper process and the given
2250 * Progress object.
2251 *
2252 * The user function is supposed to communicate to the helper process
2253 * using the \a aClient argument to do the requested job and optionally expose
2254 * the progress through the \a aProgress object. The user function should never
2255 * call notifyComplete() on it: this will be done automatically using the
2256 * result code returned by the function.
2257 *
2258 * Before the user function is started, the communication channel passed to
2259 * the \a aClient argument is fully set up, the function should start using
2260 * its write() and read() methods directly.
2261 *
2262 * The \a aVrc parameter of the user function may be used to return an error
2263 * code if it is related to communication errors (for example, returned by
2264 * the SVCHlpClient members when they fail). In this case, the correct error
2265 * message using this value will be reported to the caller. Note that the
2266 * value of \a aVrc is inspected only if the user function itself returns
2267 * success.
2268 *
2269 * If a failure happens anywhere before the user function would be normally
2270 * called, it will be called anyway in special "cleanup only" mode indicated
2271 * by \a aClient, \a aProgress and \aVrc arguments set to NULL. In this mode,
2272 * all the function is supposed to do is to cleanup its aUser argument if
2273 * necessary (it's assumed that the ownership of this argument is passed to
2274 * the user function once #startSVCHelperClient() returns a success, thus
2275 * making it responsible for the cleanup).
2276 *
2277 * After the user function returns, the thread will send the SVCHlpMsg::Null
2278 * message to indicate a process termination.
2279 *
2280 * @param aPrivileged |true| to start the SVC Helper process as a privileged
2281 * user that can perform administrative tasks
2282 * @param aFunc user function to run
2283 * @param aUser argument to the user function
2284 * @param aProgress progress object that will track operation completion
2285 *
2286 * @note aPrivileged is currently ignored (due to some unsolved problems in
2287 * Vista) and the process will be started as a normal (unprivileged)
2288 * process.
2289 *
2290 * @note Doesn't lock anything.
2291 */
2292HRESULT VirtualBox::startSVCHelperClient(bool aPrivileged,
2293 SVCHelperClientFunc aFunc,
2294 void *aUser, Progress *aProgress)
2295{
2296 AssertReturn(aFunc, E_POINTER);
2297 AssertReturn(aProgress, E_POINTER);
2298
2299 AutoCaller autoCaller(this);
2300 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2301
2302 /* create the SVCHelperClientThread() argument */
2303 std::auto_ptr <StartSVCHelperClientData>
2304 d(new StartSVCHelperClientData());
2305 AssertReturn(d.get(), E_OUTOFMEMORY);
2306
2307 d->that = this;
2308 d->progress = aProgress;
2309 d->privileged = aPrivileged;
2310 d->func = aFunc;
2311 d->user = aUser;
2312
2313 RTTHREAD tid = NIL_RTTHREAD;
2314 int vrc = RTThreadCreate(&tid, SVCHelperClientThread,
2315 static_cast <void *>(d.get()),
2316 0, RTTHREADTYPE_MAIN_WORKER,
2317 RTTHREADFLAGS_WAITABLE, "SVCHelper");
2318
2319 ComAssertMsgRCRet(vrc, ("Could not create SVCHelper thread (%Rrc)", vrc),
2320 E_FAIL);
2321
2322 /* d is now owned by SVCHelperClientThread(), so release it */
2323 d.release();
2324
2325 return S_OK;
2326}
2327
2328/**
2329 * Worker thread for startSVCHelperClient().
2330 */
2331/* static */
2332DECLCALLBACK(int)
2333VirtualBox::SVCHelperClientThread(RTTHREAD aThread, void *aUser)
2334{
2335 LogFlowFuncEnter();
2336
2337 std::auto_ptr <StartSVCHelperClientData>
2338 d(static_cast <StartSVCHelperClientData *>(aUser));
2339
2340 HRESULT rc = S_OK;
2341 bool userFuncCalled = false;
2342
2343 do
2344 {
2345 AssertBreakStmt(d.get(), rc = E_POINTER);
2346 AssertReturn(!d->progress.isNull(), E_POINTER);
2347
2348 /* protect VirtualBox from uninitialization */
2349 AutoCaller autoCaller(d->that);
2350 if (!autoCaller.isOk())
2351 {
2352 /* it's too late */
2353 rc = autoCaller.rc();
2354 break;
2355 }
2356
2357 int vrc = VINF_SUCCESS;
2358
2359 Guid id;
2360 id.create();
2361 SVCHlpClient client;
2362 vrc = client.create(Utf8StrFmt("VirtualBox\\SVCHelper\\{%RTuuid}",
2363 id.raw()).c_str());
2364 if (RT_FAILURE(vrc))
2365 {
2366 rc = setError(E_FAIL,
2367 tr("Could not create the communication channel (%Rrc)"), vrc);
2368 break;
2369 }
2370
2371 /* get the path to the executable */
2372 char exePathBuf[RTPATH_MAX];
2373 char *exePath = RTProcGetExecutableName(exePathBuf, RTPATH_MAX);
2374 ComAssertBreak(exePath, E_FAIL);
2375
2376 Utf8Str argsStr = Utf8StrFmt("/Helper %s", client.name().raw());
2377
2378 LogFlowFunc(("Starting '\"%s\" %s'...\n", exePath, argsStr.raw()));
2379
2380 RTPROCESS pid = NIL_RTPROCESS;
2381
2382 if (d->privileged)
2383 {
2384 /* Attempt to start a privileged process using the Run As dialog */
2385
2386 Bstr file = exePath;
2387 Bstr parameters = argsStr;
2388
2389 SHELLEXECUTEINFO shExecInfo;
2390
2391 shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
2392
2393 shExecInfo.fMask = NULL;
2394 shExecInfo.hwnd = NULL;
2395 shExecInfo.lpVerb = L"runas";
2396 shExecInfo.lpFile = file;
2397 shExecInfo.lpParameters = parameters;
2398 shExecInfo.lpDirectory = NULL;
2399 shExecInfo.nShow = SW_NORMAL;
2400 shExecInfo.hInstApp = NULL;
2401
2402 if (!ShellExecuteEx(&shExecInfo))
2403 {
2404 int vrc2 = RTErrConvertFromWin32(GetLastError());
2405 /* hide excessive details in case of a frequent error
2406 * (pressing the Cancel button to close the Run As dialog) */
2407 if (vrc2 == VERR_CANCELLED)
2408 rc = setError(E_FAIL,
2409 tr("Operation cancelled by the user"));
2410 else
2411 rc = setError(E_FAIL,
2412 tr("Could not launch a privileged process '%s' (%Rrc)"),
2413 exePath, vrc2);
2414 break;
2415 }
2416 }
2417 else
2418 {
2419 const char *args[] = { exePath, "/Helper", client.name().c_str(), 0 };
2420 vrc = RTProcCreate(exePath, args, RTENV_DEFAULT, 0, &pid);
2421 if (RT_FAILURE(vrc))
2422 {
2423 rc = setError(E_FAIL,
2424 tr("Could not launch a process '%s' (%Rrc)"), exePath, vrc);
2425 break;
2426 }
2427 }
2428
2429 /* wait for the client to connect */
2430 vrc = client.connect();
2431 if (RT_SUCCESS(vrc))
2432 {
2433 /* start the user supplied function */
2434 rc = d->func(&client, d->progress, d->user, &vrc);
2435 userFuncCalled = true;
2436 }
2437
2438 /* send the termination signal to the process anyway */
2439 {
2440 int vrc2 = client.write(SVCHlpMsg::Null);
2441 if (RT_SUCCESS(vrc))
2442 vrc = vrc2;
2443 }
2444
2445 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
2446 {
2447 rc = setError(E_FAIL,
2448 tr("Could not operate the communication channel (%Rrc)"), vrc);
2449 break;
2450 }
2451 }
2452 while (0);
2453
2454 if (FAILED(rc) && !userFuncCalled)
2455 {
2456 /* call the user function in the "cleanup only" mode
2457 * to let it free resources passed to in aUser */
2458 d->func(NULL, NULL, d->user, NULL);
2459 }
2460
2461 d->progress->notifyComplete(rc);
2462
2463 LogFlowFuncLeave();
2464 return 0;
2465}
2466
2467#endif /* RT_OS_WINDOWS */
2468
2469/**
2470 * Sends a signal to the client watcher thread to rescan the set of machines
2471 * that have open sessions.
2472 *
2473 * @note Doesn't lock anything.
2474 */
2475void VirtualBox::updateClientWatcher()
2476{
2477 AutoCaller autoCaller(this);
2478 AssertComRCReturnVoid(autoCaller.rc());
2479
2480 AssertReturnVoid(m->threadClientWatcher != NIL_RTTHREAD);
2481
2482 /* sent an update request */
2483#if defined(RT_OS_WINDOWS)
2484 ::SetEvent(m->updateReq);
2485#elif defined(RT_OS_OS2)
2486 RTSemEventSignal(m->updateReq);
2487#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
2488 RTSemEventSignal(m->updateReq);
2489#else
2490# error "Port me!"
2491#endif
2492}
2493
2494/**
2495 * Adds the given child process ID to the list of processes to be reaped.
2496 * This call should be followed by #updateClientWatcher() to take the effect.
2497 */
2498void VirtualBox::addProcessToReap(RTPROCESS pid)
2499{
2500 AutoCaller autoCaller(this);
2501 AssertComRCReturnVoid(autoCaller.rc());
2502
2503 /// @todo (dmik) Win32?
2504#ifndef RT_OS_WINDOWS
2505 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2506 m->llProcesses.push_back(pid);
2507#endif
2508}
2509
2510/** Event for onMachineStateChange(), onMachineDataChange(), onMachineRegistered() */
2511struct MachineEvent : public VirtualBox::CallbackEvent
2512{
2513 MachineEvent(VirtualBox *aVB, const Guid &aId)
2514 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineDataChange), id(aId)
2515 {}
2516
2517 MachineEvent(VirtualBox *aVB, const Guid &aId, MachineState_T aState)
2518 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineStateChange), id(aId)
2519 , state(aState)
2520 {}
2521
2522 MachineEvent(VirtualBox *aVB, const Guid &aId, BOOL aRegistered)
2523 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineRegistered), id(aId)
2524 , registered(aRegistered)
2525 {}
2526
2527 HRESULT handleCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2528 {
2529 switch (mWhat)
2530 {
2531 case VirtualBoxCallbackRegistration::kOnMachineDataChange:
2532 LogFlow(("OnMachineDataChange: id={%RTuuid}\n", id.ptr()));
2533 return aCallback->OnMachineDataChange(id.toUtf16());
2534
2535 case VirtualBoxCallbackRegistration::kOnMachineStateChange:
2536 LogFlow(("OnMachineStateChange: id={%RTuuid}, state=%d\n",
2537 id.ptr(), state));
2538 return aCallback->OnMachineStateChange(id.toUtf16(), state);
2539
2540 case VirtualBoxCallbackRegistration::kOnMachineRegistered:
2541 LogFlow(("OnMachineRegistered: id={%RTuuid}, registered=%d\n",
2542 id.ptr(), registered));
2543 return aCallback->OnMachineRegistered(id.toUtf16(), registered);
2544
2545 default:
2546 AssertFailedReturn(S_OK);
2547 }
2548 }
2549
2550 Guid id;
2551 MachineState_T state;
2552 BOOL registered;
2553};
2554
2555/**
2556 * @note Doesn't lock any object.
2557 */
2558void VirtualBox::onMachineStateChange(const Guid &aId, MachineState_T aState)
2559{
2560 postEvent(new MachineEvent(this, aId, aState));
2561}
2562
2563/**
2564 * @note Doesn't lock any object.
2565 */
2566void VirtualBox::onMachineDataChange(const Guid &aId)
2567{
2568 postEvent(new MachineEvent(this, aId));
2569}
2570
2571/**
2572 * @note Locks this object for reading.
2573 */
2574BOOL VirtualBox::onExtraDataCanChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue,
2575 Bstr &aError)
2576{
2577 LogFlowThisFunc(("machine={%s} aKey={%ls} aValue={%ls}\n",
2578 aId.toString().raw(), aKey, aValue));
2579
2580 AutoCaller autoCaller(this);
2581 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2582
2583 CallbackList list;
2584 {
2585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2586 list = m->llCallbacks;
2587 }
2588
2589 BOOL allowChange = TRUE;
2590 CallbackList::iterator it = list.begin();
2591 Bstr id = aId.toUtf16();
2592 while ((it != list.end()) && allowChange)
2593 {
2594 if (it->isWanted(VirtualBoxCallbackRegistration::kOnExtraDataCanChange))
2595 {
2596 HRESULT rc = it->ptrICb->OnExtraDataCanChange(id, aKey, aValue,
2597 aError.asOutParam(),
2598 &allowChange);
2599 if (FAILED(rc))
2600 {
2601 if (rc == VBOX_E_DONT_CALL_AGAIN)
2602 {
2603 /* Have to update the original. */
2604 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2605 CallbackList::iterator itOrg;
2606 itOrg = std::find(m->llCallbacks.begin(),
2607 m->llCallbacks.end(),
2608 CallbackList::value_type(it->ptrICb));
2609 if (itOrg != m->llCallbacks.end())
2610 itOrg->setDontCallAgain(VirtualBoxCallbackRegistration::kOnExtraDataCanChange);
2611 }
2612 /* if a call to this method fails for some reason (for ex., because
2613 * the other side is dead), we ensure allowChange stays true
2614 * (MS COM RPC implementation seems to zero all output vars before
2615 * issuing an IPC call or after a failure, so it's essential
2616 * there) */
2617 allowChange = TRUE;
2618 }
2619 }
2620 ++it;
2621 }
2622
2623 LogFlowThisFunc(("allowChange=%RTbool\n", allowChange));
2624 return allowChange;
2625}
2626
2627/** Event for onExtraDataChange() */
2628struct ExtraDataEvent : public VirtualBox::CallbackEvent
2629{
2630 ExtraDataEvent(VirtualBox *aVB, const Guid &aMachineId,
2631 IN_BSTR aKey, IN_BSTR aVal)
2632 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnExtraDataChange)
2633 , machineId(aMachineId), key(aKey), val(aVal)
2634 {}
2635
2636 HRESULT handleCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2637 {
2638 LogFlow(("OnExtraDataChange: machineId={%RTuuid}, key='%ls', val='%ls'\n",
2639 machineId.ptr(), key.raw(), val.raw()));
2640 return aCallback->OnExtraDataChange(machineId.toUtf16(), key, val);
2641 }
2642
2643 Guid machineId;
2644 Bstr key, val;
2645};
2646
2647/**
2648 * @note Doesn't lock any object.
2649 */
2650void VirtualBox::onExtraDataChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue)
2651{
2652 postEvent(new ExtraDataEvent(this, aId, aKey, aValue));
2653}
2654
2655/**
2656 * @note Doesn't lock any object.
2657 */
2658void VirtualBox::onMachineRegistered(const Guid &aId, BOOL aRegistered)
2659{
2660 postEvent(new MachineEvent(this, aId, aRegistered));
2661}
2662
2663/** Event for onSessionStateChange() */
2664struct SessionEvent : public VirtualBox::CallbackEvent
2665{
2666 SessionEvent(VirtualBox *aVB, const Guid &aMachineId, SessionState_T aState)
2667 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnSessionStateChange)
2668 , machineId(aMachineId), sessionState(aState)
2669 {}
2670
2671 HRESULT handleCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2672 {
2673 LogFlow(("OnSessionStateChange: machineId={%RTuuid}, sessionState=%d\n",
2674 machineId.ptr(), sessionState));
2675 return aCallback->OnSessionStateChange(machineId.toUtf16(), sessionState);
2676 }
2677
2678 Guid machineId;
2679 SessionState_T sessionState;
2680};
2681
2682/**
2683 * @note Doesn't lock any object.
2684 */
2685void VirtualBox::onSessionStateChange(const Guid &aId, SessionState_T aState)
2686{
2687 postEvent(new SessionEvent(this, aId, aState));
2688}
2689
2690/** Event for onSnapshotTaken(), onSnapshotDeleted() and onSnapshotChange() */
2691struct SnapshotEvent : public VirtualBox::CallbackEvent
2692{
2693 SnapshotEvent(VirtualBox *aVB, const Guid &aMachineId, const Guid &aSnapshotId,
2694 VirtualBoxCallbackRegistration::CallbackBit aWhat)
2695 : CallbackEvent(aVB, aWhat)
2696 , machineId(aMachineId), snapshotId(aSnapshotId)
2697 {}
2698
2699 HRESULT handleCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2700 {
2701 Bstr mid = machineId.toUtf16();
2702 Bstr sid = snapshotId.toUtf16();
2703
2704 switch (mWhat)
2705 {
2706 case VirtualBoxCallbackRegistration::kOnSnapshotTaken:
2707 LogFlow(("OnSnapshotTaken: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2708 machineId.ptr(), snapshotId.ptr()));
2709 return aCallback->OnSnapshotTaken(mid, sid);
2710
2711 case VirtualBoxCallbackRegistration::kOnSnapshotDeleted:
2712 LogFlow(("OnSnapshotDeleted: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2713 machineId.ptr(), snapshotId.ptr()));
2714 return aCallback->OnSnapshotDeleted(mid, sid);
2715
2716 case VirtualBoxCallbackRegistration::kOnSnapshotChange:
2717 LogFlow(("OnSnapshotChange: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2718 machineId.ptr(), snapshotId.ptr()));
2719 return aCallback->OnSnapshotChange(mid, sid);
2720
2721 default:
2722 AssertFailedReturn(S_OK);
2723 }
2724 }
2725
2726 Guid machineId;
2727 Guid snapshotId;
2728};
2729
2730/**
2731 * @note Doesn't lock any object.
2732 */
2733void VirtualBox::onSnapshotTaken(const Guid &aMachineId, const Guid &aSnapshotId)
2734{
2735 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2736 VirtualBoxCallbackRegistration::kOnSnapshotTaken));
2737}
2738
2739/**
2740 * @note Doesn't lock any object.
2741 */
2742void VirtualBox::onSnapshotDeleted(const Guid &aMachineId, const Guid &aSnapshotId)
2743{
2744 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2745 VirtualBoxCallbackRegistration::kOnSnapshotDeleted));
2746}
2747
2748/**
2749 * @note Doesn't lock any object.
2750 */
2751void VirtualBox::onSnapshotChange(const Guid &aMachineId, const Guid &aSnapshotId)
2752{
2753 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2754 VirtualBoxCallbackRegistration::kOnSnapshotChange));
2755}
2756
2757/** Event for onGuestPropertyChange() */
2758struct GuestPropertyEvent : public VirtualBox::CallbackEvent
2759{
2760 GuestPropertyEvent(VirtualBox *aVBox, const Guid &aMachineId,
2761 IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
2762 : CallbackEvent(aVBox, VirtualBoxCallbackRegistration::kOnGuestPropertyChange),
2763 machineId(aMachineId),
2764 name(aName),
2765 value(aValue),
2766 flags(aFlags)
2767 {}
2768
2769 HRESULT handleCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2770 {
2771 LogFlow(("OnGuestPropertyChange: machineId={%RTuuid}, name='%ls', value='%ls', flags='%ls'\n",
2772 machineId.ptr(), name.raw(), value.raw(), flags.raw()));
2773 return aCallback->OnGuestPropertyChange(machineId.toUtf16(), name, value, flags);
2774 }
2775
2776 Guid machineId;
2777 Bstr name, value, flags;
2778};
2779
2780/**
2781 * @note Doesn't lock any object.
2782 */
2783void VirtualBox::onGuestPropertyChange(const Guid &aMachineId, IN_BSTR aName,
2784 IN_BSTR aValue, IN_BSTR aFlags)
2785{
2786 postEvent(new GuestPropertyEvent(this, aMachineId, aName, aValue, aFlags));
2787}
2788
2789/**
2790 * @note Locks this object for reading.
2791 */
2792ComObjPtr<GuestOSType> VirtualBox::getUnknownOSType()
2793{
2794 ComObjPtr<GuestOSType> type;
2795 AutoCaller autoCaller(this);
2796 AssertComRCReturn(autoCaller.rc(), type);
2797
2798 /* unknown type must always be the first */
2799 ComAssertRet(m->ollGuestOSTypes.size() > 0, type);
2800
2801 return m->ollGuestOSTypes.front();
2802}
2803
2804/**
2805 * Returns the list of opened machines (machines having direct sessions opened
2806 * by client processes) and optionally the list of direct session controls.
2807 *
2808 * @param aMachines Where to put opened machines (will be empty if none).
2809 * @param aControls Where to put direct session controls (optional).
2810 *
2811 * @note The returned lists contain smart pointers. So, clear it as soon as
2812 * it becomes no more necessary to release instances.
2813 *
2814 * @note It can be possible that a session machine from the list has been
2815 * already uninitialized, so do a usual AutoCaller/AutoReadLock sequence
2816 * when accessing unprotected data directly.
2817 *
2818 * @note Locks objects for reading.
2819 */
2820void VirtualBox::getOpenedMachines(SessionMachinesList &aMachines,
2821 InternalControlList *aControls /*= NULL*/)
2822{
2823 AutoCaller autoCaller(this);
2824 AssertComRCReturnVoid(autoCaller.rc());
2825
2826 aMachines.clear();
2827 if (aControls)
2828 aControls->clear();
2829
2830 AutoReadLock alock(m->ollMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2831
2832 for (MachinesOList::iterator it = m->ollMachines.begin();
2833 it != m->ollMachines.end();
2834 ++it)
2835 {
2836 ComObjPtr<SessionMachine> sm;
2837 ComPtr<IInternalSessionControl> ctl;
2838 if ((*it)->isSessionOpen(sm, &ctl))
2839 {
2840 aMachines.push_back(sm);
2841 if (aControls)
2842 aControls->push_back(ctl);
2843 }
2844 }
2845}
2846
2847/**
2848 * Searches for a Machine object with the given ID in the collection
2849 * of registered machines.
2850 *
2851 * @param id
2852 * ID of the machine
2853 * @param doSetError
2854 * if TRUE, the appropriate error info is set in case when the machine
2855 * is not found
2856 * @param machine
2857 * where to store the found machine object (can be NULL)
2858 *
2859 * @return
2860 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
2861 *
2862 * @note Locks this object for reading.
2863 */
2864HRESULT VirtualBox::findMachine(const Guid &aId,
2865 bool aSetError,
2866 ComObjPtr<Machine> *aMachine /* = NULL */)
2867{
2868 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
2869
2870 AutoCaller autoCaller(this);
2871 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2872
2873 {
2874 AutoReadLock al(m->ollMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2875
2876 for (MachinesOList::iterator it = m->ollMachines.begin();
2877 it != m->ollMachines.end();
2878 ++it)
2879 {
2880 ComObjPtr<Machine> pMachine2 = *it;
2881 /* sanity */
2882 AutoLimitedCaller machCaller(pMachine2);
2883 AssertComRC(machCaller.rc());
2884
2885 if (pMachine2->getId() == aId)
2886 {
2887 rc = S_OK;
2888 if (aMachine)
2889 *aMachine = pMachine2;
2890 break;
2891 }
2892 }
2893 }
2894
2895 if (aSetError && FAILED(rc))
2896 setError(VBOX_E_OBJECT_NOT_FOUND,
2897 tr("Could not find a registered machine with UUID {%RTuuid}"),
2898 aId.raw());
2899
2900 return rc;
2901}
2902
2903/**
2904 * Searches for a Medium object with the given ID or location in the list of
2905 * registered hard disks. If both ID and location are specified, the first
2906 * object that matches either of them (not necessarily both) is returned.
2907 *
2908 * @param aId ID of the hard disk (unused when NULL).
2909 * @param aLocation Full location specification (unused NULL).
2910 * @param aSetError If @c true , the appropriate error info is set in case
2911 * when the hard disk is not found.
2912 * @param aHardDisk Where to store the found hard disk object (can be NULL).
2913 *
2914 * @return S_OK when found or E_INVALIDARG when not found.
2915 *
2916 * @note Locks the media tree for reading.
2917 */
2918HRESULT VirtualBox::findHardDisk(const Guid *aId,
2919 CBSTR aLocation,
2920 bool aSetError,
2921 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
2922{
2923 AssertReturn(aId || aLocation, E_INVALIDARG);
2924
2925 // we use the hard disks map, but it is protected by the
2926 // hard disk _list_ lock handle
2927 AutoReadLock alock(m->ollHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2928
2929 /* first, look up by UUID in the map if UUID is provided */
2930 if (aId)
2931 {
2932 HardDiskMap::const_iterator it = m->mapHardDisks.find(*aId);
2933 if (it != m->mapHardDisks.end())
2934 {
2935 if (aHardDisk)
2936 *aHardDisk = (*it).second;
2937 return S_OK;
2938 }
2939 }
2940
2941 /* then iterate and search by location */
2942 int result = -1;
2943 if (aLocation)
2944 {
2945 Utf8Str location = aLocation;
2946
2947 for (HardDiskMap::const_iterator it = m->mapHardDisks.begin();
2948 it != m->mapHardDisks.end();
2949 ++ it)
2950 {
2951 const ComObjPtr<Medium> &hd = (*it).second;
2952
2953 HRESULT rc = hd->compareLocationTo(location.c_str(), result);
2954 if (FAILED(rc)) return rc;
2955
2956 if (result == 0)
2957 {
2958 if (aHardDisk)
2959 *aHardDisk = hd;
2960 break;
2961 }
2962 }
2963 }
2964
2965 HRESULT rc = result == 0 ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
2966
2967 if (aSetError && result != 0)
2968 {
2969 if (aId)
2970 setError(rc,
2971 tr("Could not find a hard disk with UUID {%RTuuid} in the media registry ('%s')"),
2972 aId->raw(),
2973 m->strSettingsFilePath.raw());
2974 else
2975 setError(rc,
2976 tr("Could not find a hard disk with location '%ls' in the media registry ('%s')"),
2977 aLocation,
2978 m->strSettingsFilePath.raw());
2979 }
2980
2981 return rc;
2982}
2983
2984/**
2985 * Searches for a Medium object with the given ID or location in the list of
2986 * registered DVD images. If both ID and file path are specified, the first
2987 * object that matches either of them (not necessarily both) is returned.
2988 *
2989 * @param aId ID of the DVD image (unused when NULL).
2990 * @param aLocation Full path to the image file (unused when NULL).
2991 * @param aSetError If @c true, the appropriate error info is set in case when
2992 * the image is not found.
2993 * @param aImage Where to store the found image object (can be NULL).
2994 *
2995 * @return S_OK when found or E_INVALIDARG when not found.
2996 *
2997 * @note Locks the media tree for reading.
2998 */
2999HRESULT VirtualBox::findDVDImage(const Guid *aId,
3000 CBSTR aLocation,
3001 bool aSetError,
3002 ComObjPtr<Medium> *aImage /* = NULL */)
3003{
3004 AssertReturn(aId || aLocation, E_INVALIDARG);
3005
3006 Utf8Str location;
3007
3008 if (aLocation != NULL)
3009 {
3010 int vrc = calculateFullPath(Utf8Str(aLocation), location);
3011 if (RT_FAILURE(vrc))
3012 return setError(VBOX_E_FILE_ERROR,
3013 tr("Invalid image file location '%ls' (%Rrc)"),
3014 aLocation,
3015 vrc);
3016 }
3017
3018 AutoReadLock alock(m->ollDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3019
3020 bool found = false;
3021
3022 for (MediaList::const_iterator it = m->ollDVDImages.begin();
3023 it != m->ollDVDImages.end();
3024 ++ it)
3025 {
3026 /* no AutoCaller, registered image life time is bound to this */
3027 AutoReadLock imageLock(*it COMMA_LOCKVAL_SRC_POS);
3028
3029 found = (aId && (*it)->getId() == *aId) ||
3030 (aLocation != NULL &&
3031 RTPathCompare(location.c_str(),
3032 (*it)->getLocationFull().c_str()
3033 ) == 0);
3034 if (found)
3035 {
3036 if (aImage)
3037 *aImage = *it;
3038 break;
3039 }
3040 }
3041
3042 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
3043
3044 if (aSetError && !found)
3045 {
3046 if (aId)
3047 setError(rc,
3048 tr("Could not find a CD/DVD image with UUID {%RTuuid} in the media registry ('%s')"),
3049 aId->raw(),
3050 m->strSettingsFilePath.raw());
3051 else
3052 setError(rc,
3053 tr("Could not find a CD/DVD image with location '%ls' in the media registry ('%s')"),
3054 aLocation,
3055 m->strSettingsFilePath.raw());
3056 }
3057
3058 return rc;
3059}
3060
3061/**
3062 * Searches for a Medium object with the given ID or location in the
3063 * collection of registered DVD images. If both ID and file path are specified,
3064 * the first object that matches either of them (not necessarily both) is
3065 * returned.
3066 *
3067 * @param aId ID of the DVD image (unused when NULL).
3068 * @param aLocation Full path to the image file (unused when NULL).
3069 * @param aSetError If @c true, the appropriate error info is set in case when
3070 * the image is not found.
3071 * @param aImage Where to store the found image object (can be NULL).
3072 *
3073 * @return S_OK when found or E_INVALIDARG when not found.
3074 *
3075 * @note Locks the media tree for reading.
3076 */
3077HRESULT VirtualBox::findFloppyImage(const Guid *aId,
3078 CBSTR aLocation,
3079 bool aSetError,
3080 ComObjPtr<Medium> *aImage /* = NULL */)
3081{
3082 AssertReturn(aId || aLocation, E_INVALIDARG);
3083
3084 Utf8Str location;
3085
3086 if (aLocation != NULL)
3087 {
3088 int vrc = calculateFullPath(Utf8Str(aLocation), location);
3089 if (RT_FAILURE(vrc))
3090 return setError(VBOX_E_FILE_ERROR,
3091 tr("Invalid image file location '%ls' (%Rrc)"),
3092 aLocation, vrc);
3093 }
3094
3095 AutoReadLock alock(m->ollFloppyImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3096
3097 bool found = false;
3098
3099 for (MediaList::const_iterator it = m->ollFloppyImages.begin();
3100 it != m->ollFloppyImages.end();
3101 ++ it)
3102 {
3103 /* no AutoCaller, registered image life time is bound to this */
3104 AutoReadLock imageLock(*it COMMA_LOCKVAL_SRC_POS);
3105
3106 found = (aId && (*it)->getId() == *aId) ||
3107 (aLocation != NULL &&
3108 RTPathCompare(location.c_str(),
3109 (*it)->getLocationFull().c_str()
3110 ) == 0);
3111 if (found)
3112 {
3113 if (aImage)
3114 *aImage = *it;
3115 break;
3116 }
3117 }
3118
3119 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
3120
3121 if (aSetError && !found)
3122 {
3123 if (aId)
3124 setError(rc,
3125 tr("Could not find a floppy image with UUID {%RTuuid} in the media registry ('%s')"),
3126 aId->raw(),
3127 m->strSettingsFilePath.raw());
3128 else
3129 setError(rc,
3130 tr("Could not find a floppy image with location '%ls' in the media registry ('%s')"),
3131 aLocation,
3132 m->strSettingsFilePath.raw());
3133 }
3134
3135 return rc;
3136}
3137
3138HRESULT VirtualBox::findGuestOSType(const Bstr &bstrOSType,
3139 GuestOSType*& pGuestOSType)
3140{
3141 /* Look for a GuestOSType object */
3142 AssertMsg(m->ollGuestOSTypes.size() != 0,
3143 ("Guest OS types array must be filled"));
3144
3145 if (bstrOSType.isEmpty())
3146 {
3147 pGuestOSType = NULL;
3148 return S_OK;
3149 }
3150
3151 AutoReadLock alock(m->ollGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3152 for (GuestOSTypesOList::const_iterator it = m->ollGuestOSTypes.begin();
3153 it != m->ollGuestOSTypes.end();
3154 ++it)
3155 {
3156 if ((*it)->id() == bstrOSType)
3157 {
3158 pGuestOSType = *it;
3159 return S_OK;
3160 }
3161 }
3162
3163 return setError(VBOX_E_OBJECT_NOT_FOUND,
3164 tr("Guest OS type '%ls' is invalid"),
3165 bstrOSType.raw());
3166}
3167
3168const ComObjPtr<Host>& VirtualBox::host() const
3169{
3170 return m->pHost;
3171}
3172
3173const ComObjPtr<SystemProperties>& VirtualBox::systemProperties() const
3174{
3175 return m->pSystemProperties;
3176}
3177
3178#ifdef VBOX_WITH_RESOURCE_USAGE_API
3179const ComObjPtr<PerformanceCollector>& VirtualBox::performanceCollector() const
3180{
3181 return m->pPerformanceCollector;
3182}
3183#endif /* VBOX_WITH_RESOURCE_USAGE_API */
3184
3185/**
3186 * Returns the default machine folder from the system properties
3187 * with proper locking.
3188 * @return
3189 */
3190const Utf8Str& VirtualBox::getDefaultMachineFolder() const
3191{
3192 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
3193 return m->pSystemProperties->m_strDefaultMachineFolder;
3194}
3195
3196/**
3197 * Returns the default hard disk folder from the system properties
3198 * with proper locking.
3199 * @return
3200 */
3201const Utf8Str& VirtualBox::getDefaultHardDiskFolder() const
3202{
3203 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
3204 return m->pSystemProperties->m_strDefaultHardDiskFolder;
3205}
3206
3207/**
3208 * Returns the default hard disk format from the system properties
3209 * with proper locking.
3210 * @return
3211 */
3212const Utf8Str& VirtualBox::getDefaultHardDiskFormat() const
3213{
3214 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
3215 return m->pSystemProperties->m_strDefaultHardDiskFormat;
3216}
3217
3218const Utf8Str& VirtualBox::homeDir() const
3219{
3220 return m->strHomeDir;
3221}
3222
3223/**
3224 * Calculates the absolute path of the given path taking the VirtualBox home
3225 * directory as the current directory.
3226 *
3227 * @param aPath Path to calculate the absolute path for.
3228 * @param aResult Where to put the result (used only on success, can be the
3229 * same Utf8Str instance as passed in @a aPath).
3230 * @return IPRT result.
3231 *
3232 * @note Doesn't lock any object.
3233 */
3234int VirtualBox::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
3235{
3236 AutoCaller autoCaller(this);
3237 AssertComRCReturn(autoCaller.rc(), VERR_GENERAL_FAILURE);
3238
3239 /* no need to lock since mHomeDir is const */
3240
3241 char folder[RTPATH_MAX];
3242 int vrc = RTPathAbsEx(m->strHomeDir.c_str(), strPath.c_str(), folder, sizeof(folder));
3243 if (RT_SUCCESS(vrc))
3244 aResult = folder;
3245
3246 return vrc;
3247}
3248
3249/**
3250 * Tries to calculate the relative path of the given absolute path using the
3251 * directory of the VirtualBox settings file as the base directory.
3252 *
3253 * @param aPath Absolute path to calculate the relative path for.
3254 * @param aResult Where to put the result (used only when it's possible to
3255 * make a relative path from the given absolute path; otherwise
3256 * left untouched).
3257 *
3258 * @note Doesn't lock any object.
3259 */
3260void VirtualBox::calculateRelativePath(const Utf8Str &strPath, Utf8Str &aResult)
3261{
3262 AutoCaller autoCaller(this);
3263 AssertComRCReturnVoid(autoCaller.rc());
3264
3265 /* no need to lock since mHomeDir is const */
3266
3267 Utf8Str settingsDir = m->strHomeDir;
3268
3269 if (RTPathStartsWith(strPath.c_str(), settingsDir.c_str()))
3270 {
3271 /* when assigning, we create a separate Utf8Str instance because both
3272 * aPath and aResult can point to the same memory location when this
3273 * func is called (if we just do aResult = aPath, aResult will be freed
3274 * first, and since its the same as aPath, an attempt to copy garbage
3275 * will be made. */
3276 aResult = Utf8Str(strPath.c_str() + settingsDir.length() + 1);
3277 }
3278}
3279
3280// private methods
3281/////////////////////////////////////////////////////////////////////////////
3282
3283/**
3284 * Checks if there is a hard disk, DVD or floppy image with the given ID or
3285 * location already registered.
3286 *
3287 * On return, sets @a aConflict to the string describing the conflicting medium,
3288 * or sets it to @c Null if no conflicting media is found. Returns S_OK in
3289 * either case. A failure is unexpected.
3290 *
3291 * @param aId UUID to check.
3292 * @param aLocation Location to check.
3293 * @param aConflict Where to return parameters of the conflicting medium.
3294 *
3295 * @note Locks the media tree and media objects for reading.
3296 */
3297HRESULT VirtualBox::checkMediaForConflicts2(const Guid &aId,
3298 const Utf8Str &aLocation,
3299 Utf8Str &aConflict)
3300{
3301 aConflict.setNull();
3302
3303 AssertReturn(!aId.isEmpty() && !aLocation.isEmpty(), E_FAIL);
3304
3305 AutoReadLock alock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3306
3307 HRESULT rc = S_OK;
3308
3309 Bstr bstrLocation(aLocation);
3310
3311 {
3312 ComObjPtr<Medium> hardDisk;
3313 rc = findHardDisk(&aId, bstrLocation, false /* aSetError */, &hardDisk);
3314 if (SUCCEEDED(rc))
3315 {
3316 /* Note: no AutoCaller since bound to this */
3317 AutoReadLock mediaLock(hardDisk COMMA_LOCKVAL_SRC_POS);
3318 aConflict = Utf8StrFmt(tr("hard disk '%s' with UUID {%RTuuid}"),
3319 hardDisk->getLocationFull().raw(),
3320 hardDisk->getId().raw());
3321 return S_OK;
3322 }
3323 }
3324
3325 {
3326 ComObjPtr<Medium> image;
3327 rc = findDVDImage(&aId, bstrLocation, false /* aSetError */, &image);
3328 if (SUCCEEDED(rc))
3329 {
3330 /* Note: no AutoCaller since bound to this */
3331 AutoReadLock mediaLock(image COMMA_LOCKVAL_SRC_POS);
3332 aConflict = Utf8StrFmt(tr("CD/DVD image '%s' with UUID {%RTuuid}"),
3333 image->getLocationFull().raw(),
3334 image->getId().raw());
3335 return S_OK;
3336 }
3337 }
3338
3339 {
3340 ComObjPtr<Medium> image;
3341 rc = findFloppyImage(&aId, bstrLocation, false /* aSetError */, &image);
3342 if (SUCCEEDED(rc))
3343 {
3344 /* Note: no AutoCaller since bound to this */
3345 AutoReadLock mediaLock(image COMMA_LOCKVAL_SRC_POS);
3346 aConflict = Utf8StrFmt(tr("floppy image '%s' with UUID {%RTuuid}"),
3347 image->getLocationFull().raw(),
3348 image->getId().raw());
3349 return S_OK;
3350 }
3351 }
3352
3353 return S_OK;
3354}
3355
3356/**
3357 * Called from Machine::prepareSaveSettings() when it has detected
3358 * that a machine has been renamed. Such renames will require
3359 * updating the global media registry during the
3360 * VirtualBox::saveSettings() that follows later.
3361*
3362 * When a machine is renamed, there may well be media (in particular,
3363 * diff images for snapshots) in the global registry that will need
3364 * to have their paths updated. Before 3.2, Machine::saveSettings
3365 * used to call VirtualBox::saveSettings implicitly, which was both
3366 * unintuitive and caused locking order problems. Now, we remeber
3367 * such pending name changes with this method so that
3368 * VirtualBox::saveSettings() can process them properly.
3369 */
3370void VirtualBox::rememberMachineNameChangeForMedia(const Utf8Str &strOldConfigDir,
3371 const Utf8Str &strNewConfigDir)
3372{
3373 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3374
3375 Data::PendingMachineRename pmr;
3376 pmr.strConfigDirOld = strOldConfigDir;
3377 pmr.strConfigDirNew = strNewConfigDir;
3378 m->llPendingMachineRenames.push_back(pmr);
3379}
3380
3381/**
3382 * Helper function to write out the configuration tree.
3383 *
3384 * @note Caller must have locked the VirtualBox object for writing!
3385 */
3386HRESULT VirtualBox::saveSettings()
3387{
3388 AutoCaller autoCaller(this);
3389 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3390
3391 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
3392 AssertReturn(!m->strSettingsFilePath.isEmpty(), E_FAIL);
3393
3394 HRESULT rc = S_OK;
3395
3396 try
3397 {
3398 // lock the lists while we're here
3399 AutoReadLock machinesLock(m->ollMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3400
3401 // machines
3402 settings::MachinesRegistry machinesTemp;
3403 {
3404 for (MachinesOList::iterator it = m->ollMachines.begin();
3405 it != m->ollMachines.end();
3406 ++it)
3407 {
3408 Machine *pMachine = *it;
3409 // save actual machine registry entry
3410 settings::MachineRegistryEntry mre;
3411 rc = pMachine->saveRegistryEntry(mre);
3412 machinesTemp.push_back(mre);
3413 }
3414 }
3415
3416 // lock all media for the following; use a write lock because we're
3417 // modifying the PendingMachineRenamesList, which is protected by this
3418 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3419
3420 // if a machine was renamed, then we'll need to refresh media paths
3421 if (m->llPendingMachineRenames.size())
3422 {
3423 // make a single list from the three media lists so we don't need three loops
3424 MediaList llAllMedia;
3425 // with hard disks, we must use the map, not the list, because the list only has base images
3426 for (HardDiskMap::iterator it = m->mapHardDisks.begin(); it != m->mapHardDisks.end(); ++it)
3427 llAllMedia.push_back(it->second);
3428 for (MediaList::iterator it = m->ollDVDImages.begin(); it != m->ollDVDImages.end(); ++it)
3429 llAllMedia.push_back(*it);
3430 for (MediaList::iterator it = m->ollFloppyImages.begin(); it != m->ollFloppyImages.end(); ++it)
3431 llAllMedia.push_back(*it);
3432
3433 for (MediaList::iterator it = llAllMedia.begin();
3434 it != llAllMedia.end();
3435 ++it)
3436 {
3437 Medium *pMedium = *it;
3438 for (Data::PendingMachineRenamesList::iterator it2 = m->llPendingMachineRenames.begin();
3439 it2 != m->llPendingMachineRenames.end();
3440 ++it2)
3441 {
3442 const Data::PendingMachineRename &pmr = *it2;
3443 const char *pcszOld = pmr.strConfigDirOld.c_str();
3444 const char *pcszNew = pmr.strConfigDirNew.c_str();
3445 pMedium->updatePath(pcszOld, pcszNew);
3446 }
3447 }
3448 // done, don't do it again until we have more machine renames
3449 m->llPendingMachineRenames.clear();
3450 }
3451
3452 // hard disks
3453 settings::MediaList hardDisksTemp;
3454 for (MediaList::const_iterator it = m->ollHardDisks.begin();
3455 it != m->ollHardDisks.end();
3456 ++it)
3457 {
3458 settings::Medium med;
3459 rc = (*it)->saveSettings(med); // this recurses into its children
3460 if (FAILED(rc)) throw rc;
3461 hardDisksTemp.push_back(med);
3462 }
3463
3464 /* CD/DVD images */
3465 settings::MediaList dvdsTemp;
3466 for (MediaList::const_iterator it = m->ollDVDImages.begin();
3467 it != m->ollDVDImages.end();
3468 ++it)
3469 {
3470 settings::Medium med;
3471 rc = (*it)->saveSettings(med);
3472 if (FAILED(rc)) throw rc;
3473 dvdsTemp.push_back(med);
3474 }
3475
3476 /* floppy images */
3477 settings::MediaList floppiesTemp;
3478 for (MediaList::const_iterator it = m->ollFloppyImages.begin();
3479 it != m->ollFloppyImages.end();
3480 ++it)
3481 {
3482 settings::Medium med;
3483 rc = (*it)->saveSettings(med);
3484 if (FAILED(rc)) throw rc;
3485 floppiesTemp.push_back(med);
3486 }
3487
3488 settings::DHCPServersList dhcpServersTemp;
3489 {
3490 AutoReadLock dhcpLock(m->ollDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3491 for (DHCPServersOList::const_iterator it = m->ollDHCPServers.begin();
3492 it != m->ollDHCPServers.end();
3493 ++it)
3494 {
3495 settings::DHCPServer d;
3496 rc = (*it)->saveSettings(d);
3497 if (FAILED(rc)) throw rc;
3498 dhcpServersTemp.push_back(d);
3499 }
3500 }
3501
3502 /* now copy the temp data to the config file under the VirtualBox lock */
3503 m->pMainConfigFile->llMachines = machinesTemp;
3504 m->pMainConfigFile->llHardDisks = hardDisksTemp;
3505 m->pMainConfigFile->llDvdImages = dvdsTemp;
3506 m->pMainConfigFile->llFloppyImages = floppiesTemp;
3507 m->pMainConfigFile->llDhcpServers = dhcpServersTemp;
3508
3509 // leave extra data alone, it's still in the config file
3510
3511 /* host data (USB filters) */
3512 rc = m->pHost->saveSettings(m->pMainConfigFile->host);
3513 if (FAILED(rc)) throw rc;
3514
3515 rc = m->pSystemProperties->saveSettings(m->pMainConfigFile->systemProperties);
3516 if (FAILED(rc)) throw rc;
3517
3518 // and write out the XML, still under the lock
3519 m->pMainConfigFile->write(m->strSettingsFilePath);
3520 }
3521 catch (HRESULT err)
3522 {
3523 /* we assume that error info is set by the thrower */
3524 rc = err;
3525 }
3526 catch (...)
3527 {
3528 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
3529 }
3530
3531 return rc;
3532}
3533
3534/**
3535 * Helper to register the machine.
3536 *
3537 * When called during VirtualBox startup, adds the given machine to the
3538 * collection of registered machines. Otherwise tries to mark the machine
3539 * as registered, and, if succeeded, adds it to the collection and
3540 * saves global settings.
3541 *
3542 * @note The caller must have added itself as a caller of the @a aMachine
3543 * object if calls this method not on VirtualBox startup.
3544 *
3545 * @param aMachine machine to register
3546 *
3547 * @note Locks objects!
3548 */
3549HRESULT VirtualBox::registerMachine(Machine *aMachine)
3550{
3551 ComAssertRet(aMachine, E_INVALIDARG);
3552
3553 AutoCaller autoCaller(this);
3554 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3555
3556 HRESULT rc = S_OK;
3557
3558 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3559
3560 {
3561 ComObjPtr<Machine> pMachine;
3562 rc = findMachine(aMachine->getId(), false /* aDoSetError */, &pMachine);
3563 if (SUCCEEDED(rc))
3564 {
3565 /* sanity */
3566 AutoLimitedCaller machCaller(pMachine);
3567 AssertComRC(machCaller.rc());
3568
3569 return setError(E_INVALIDARG,
3570 tr("Registered machine with UUID {%RTuuid} ('%ls') already exists"),
3571 aMachine->getId().raw(),
3572 pMachine->getSettingsFileFull().raw());
3573 }
3574
3575 ComAssertRet(rc == VBOX_E_OBJECT_NOT_FOUND, rc);
3576 rc = S_OK;
3577 }
3578
3579 if (autoCaller.state() != InInit)
3580 {
3581 rc = aMachine->trySetRegistered(TRUE);
3582 if (FAILED(rc)) return rc;
3583 }
3584
3585 /* add to the collection of registered machines */
3586 m->ollMachines.addChild(aMachine);
3587
3588 if (autoCaller.state() != InInit)
3589 rc = saveSettings();
3590
3591 return rc;
3592}
3593
3594/**
3595 * Remembers the given hard disk by storing it in the hard disk registry.
3596 *
3597 * @param aHardDisk Hard disk object to remember.
3598 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3599 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3600 *
3601 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3602 */
3603HRESULT VirtualBox::registerHardDisk(Medium *aHardDisk,
3604 bool *pfNeedsSaveSettings)
3605{
3606 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3607
3608 AutoCaller autoCaller(this);
3609 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3610
3611 AutoCaller hardDiskCaller(aHardDisk);
3612 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3613
3614 // caller must hold the media tree write lock
3615 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3616
3617 Guid id;
3618 Utf8Str strLocationFull;
3619 ComObjPtr<Medium> pParent;
3620 {
3621 AutoReadLock hardDiskLock(aHardDisk COMMA_LOCKVAL_SRC_POS);
3622 id = aHardDisk->getId();
3623 strLocationFull = aHardDisk->getLocationFull();
3624 pParent = aHardDisk->getParent();
3625 }
3626
3627 HRESULT rc;
3628
3629 Utf8Str strConflict;
3630 rc = checkMediaForConflicts2(id,
3631 strLocationFull,
3632 strConflict);
3633 if (FAILED(rc)) return rc;
3634
3635 if (strConflict.length())
3636 return setError(E_INVALIDARG,
3637 tr("Cannot register the hard disk '%s' with UUID {%RTuuid} because a %s already exists in the media registry ('%s')"),
3638 strLocationFull.raw(),
3639 id.raw(),
3640 strConflict.raw(),
3641 m->strSettingsFilePath.raw());
3642
3643 // store base (root) hard disks in the list
3644 if (pParent.isNull())
3645 m->ollHardDisks.getList().push_back(aHardDisk);
3646 // access the list directly because we already locked the list above
3647
3648 // store all hard disks (even differencing images) in the map
3649 m->mapHardDisks[id] = aHardDisk;
3650
3651 if (pfNeedsSaveSettings)
3652 *pfNeedsSaveSettings = true;
3653
3654 return rc;
3655}
3656
3657/**
3658 * Removes the given hard disk from the hard disk registry.
3659 *
3660 * @param aHardDisk Hard disk object to remove.
3661 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3662 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3663 *
3664 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3665 */
3666HRESULT VirtualBox::unregisterHardDisk(Medium *aHardDisk,
3667 bool *pfNeedsSaveSettings)
3668{
3669 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3670
3671 AutoCaller autoCaller(this);
3672 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3673
3674 AutoCaller hardDiskCaller(aHardDisk);
3675 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3676
3677 // caller must hold the media tree write lock
3678 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3679
3680 Guid id;
3681 ComObjPtr<Medium> pParent;
3682 {
3683 AutoReadLock hardDiskLock(aHardDisk COMMA_LOCKVAL_SRC_POS);
3684 id = aHardDisk->getId();
3685 pParent = aHardDisk->getParent();
3686 }
3687
3688 // remove base (root) hard disks from the list
3689 if (pParent.isNull())
3690 m->ollHardDisks.getList().remove(aHardDisk);
3691 // access the list directly because caller must have locked the list
3692
3693 // remove all hard disks (even differencing images) from map
3694 size_t cnt = m->mapHardDisks.erase(id);
3695 Assert(cnt == 1);
3696 NOREF(cnt);
3697
3698 if (pfNeedsSaveSettings)
3699 *pfNeedsSaveSettings = true;
3700
3701 return S_OK;
3702}
3703
3704/**
3705 * Remembers the given image by storing it in the CD/DVD or floppy image registry.
3706 *
3707 * @param argImage Image object to remember.
3708 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3709 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3710 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3711 *
3712 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3713 */
3714HRESULT VirtualBox::registerImage(Medium *argImage,
3715 DeviceType_T argType,
3716 bool *pfNeedsSaveSettings)
3717{
3718 AssertReturn(argImage != NULL, E_INVALIDARG);
3719
3720 AutoCaller autoCaller(this);
3721 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3722
3723 AutoCaller imageCaller(argImage);
3724 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3725
3726 // caller must hold the media tree write lock
3727 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3728
3729 Guid id;
3730 Utf8Str strLocationFull;
3731 ComObjPtr<Medium> pParent;
3732 {
3733 AutoReadLock al(argImage COMMA_LOCKVAL_SRC_POS);
3734 id = argImage->getId();
3735 strLocationFull = argImage->getLocationFull();
3736 pParent = argImage->getParent();
3737 }
3738
3739 // work on DVDs or floppies list?
3740 ObjectsList<Medium> &oll = (argType == DeviceType_DVD) ? m->ollDVDImages : m->ollFloppyImages;
3741
3742 HRESULT rc;
3743 // lock the images lists (list + map) while checking for conflicts
3744 AutoWriteLock al(oll.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3745
3746 Utf8Str strConflict;
3747 rc = checkMediaForConflicts2(id,
3748 strLocationFull,
3749 strConflict);
3750 if (FAILED(rc)) return rc;
3751
3752 if (strConflict.length())
3753 return setError(VBOX_E_INVALID_OBJECT_STATE,
3754 tr("Cannot register the image '%s' with UUID {%RTuuid} because a %s already exists in the media registry ('%s')"),
3755 strLocationFull.raw(),
3756 id.raw(),
3757 strConflict.raw(),
3758 m->strSettingsFilePath.raw());
3759
3760 // add to the collection
3761 oll.getList().push_back(argImage);
3762 // access the list directly because we already locked the list above
3763
3764 if (pfNeedsSaveSettings)
3765 *pfNeedsSaveSettings = true;
3766
3767 return rc;
3768}
3769
3770/**
3771 * Removes the given image from the CD/DVD or floppy image registry.
3772 *
3773 * @param argImage Image object to remove.
3774 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3775 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3776 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3777 *
3778 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3779 */
3780HRESULT VirtualBox::unregisterImage(Medium *argImage,
3781 DeviceType_T argType,
3782 bool *pfNeedsSaveSettings)
3783{
3784 AssertReturn(argImage != NULL, E_INVALIDARG);
3785
3786 AutoCaller autoCaller(this);
3787 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3788
3789 AutoCaller imageCaller(argImage);
3790 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3791
3792 // caller must hold the media tree write lock
3793 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3794
3795 Guid id;
3796 ComObjPtr<Medium> pParent;
3797 {
3798 AutoReadLock al(argImage COMMA_LOCKVAL_SRC_POS);
3799 id = argImage->getId();
3800 pParent = argImage->getParent();
3801 }
3802
3803 // work on DVDs or floppies list?
3804 ObjectsList<Medium> &oll = (argType == DeviceType_DVD) ? m->ollDVDImages : m->ollFloppyImages;
3805
3806 // access the list directly because the caller must have requested the lock
3807 oll.getList().remove(argImage);
3808
3809 HRESULT rc = S_OK;
3810
3811 if (pfNeedsSaveSettings)
3812 *pfNeedsSaveSettings = true;
3813
3814 return rc;
3815}
3816
3817/**
3818 * Creates the path to the specified file according to the path information
3819 * present in the file name.
3820 *
3821 * Note that the given file name must contain the full path otherwise the
3822 * extracted relative path will be created based on the current working
3823 * directory which is normally unknown.
3824 *
3825 * @param aFileName Full file name which path needs to be created.
3826 *
3827 * @return Extended error information on failure to create the path.
3828 */
3829/* static */
3830HRESULT VirtualBox::ensureFilePathExists(const Utf8Str &strFileName)
3831{
3832 Utf8Str strDir(strFileName);
3833 strDir.stripFilename();
3834 if (!RTDirExists(strDir.c_str()))
3835 {
3836 int vrc = RTDirCreateFullPath(strDir.c_str(), 0777);
3837 if (RT_FAILURE(vrc))
3838 return setError(E_FAIL,
3839 tr("Could not create the directory '%s' (%Rrc)"),
3840 strDir.c_str(),
3841 vrc);
3842 }
3843
3844 return S_OK;
3845}
3846
3847/**
3848 * Handles unexpected exceptions by turning them into COM errors in release
3849 * builds or by hitting a breakpoint in the release builds.
3850 *
3851 * Usage pattern:
3852 * @code
3853 try
3854 {
3855 // ...
3856 }
3857 catch (LaLalA)
3858 {
3859 // ...
3860 }
3861 catch (...)
3862 {
3863 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3864 }
3865 * @endcode
3866 *
3867 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
3868 */
3869/* static */
3870HRESULT VirtualBox::handleUnexpectedExceptions(RT_SRC_POS_DECL)
3871{
3872 try
3873 {
3874 /* re-throw the current exception */
3875 throw;
3876 }
3877 catch (const xml::Error &err)
3878 {
3879 return setError(E_FAIL, tr("%s.\n%s[%d] (%s)"),
3880 err.what(),
3881 pszFile, iLine, pszFunction);
3882 }
3883 catch (const std::exception &err)
3884 {
3885 return setError(E_FAIL, tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
3886 err.what(), typeid(err).name(),
3887 pszFile, iLine, pszFunction);
3888 }
3889 catch (...)
3890 {
3891 return setError(E_FAIL, tr("Unknown exception\n%s[%d] (%s)"),
3892 pszFile, iLine, pszFunction);
3893 }
3894
3895 /* should not get here */
3896 AssertFailed();
3897 return E_FAIL;
3898}
3899
3900const Utf8Str& VirtualBox::settingsFilePath()
3901{
3902 return m->strSettingsFilePath;
3903}
3904
3905/**
3906 * Returns the lock handle which protects the media trees (hard disks,
3907 * DVDs, floppies). As opposed to version 3.1 and earlier, these lists
3908 * are no longer protected by the VirtualBox lock, but by this more
3909 * specialized lock. Mind the locking order: always request this lock
3910 * after the VirtualBox object lock but before the locks of the media
3911 * objects contained in these lists. See AutoLock.h.
3912 */
3913RWLockHandle& VirtualBox::getMediaTreeLockHandle()
3914{
3915 return m->lockMedia;
3916}
3917
3918/**
3919 * Thread function that watches the termination of all client processes
3920 * that have opened sessions using IVirtualBox::OpenSession()
3921 */
3922// static
3923DECLCALLBACK(int) VirtualBox::ClientWatcher(RTTHREAD /* thread */, void *pvUser)
3924{
3925 LogFlowFuncEnter();
3926
3927 VirtualBox *that = (VirtualBox*)pvUser;
3928 Assert(that);
3929
3930 typedef std::vector< ComObjPtr<Machine> > MachineVector;
3931 typedef std::vector< ComObjPtr<SessionMachine> > SessionMachineVector;
3932
3933 SessionMachineVector machines;
3934 MachineVector spawnedMachines;
3935
3936 size_t cnt = 0;
3937 size_t cntSpawned = 0;
3938
3939#if defined(RT_OS_WINDOWS)
3940
3941 HRESULT hrc = CoInitializeEx(NULL,
3942 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
3943 COINIT_SPEED_OVER_MEMORY);
3944 AssertComRC(hrc);
3945
3946 /// @todo (dmik) processes reaping!
3947
3948 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
3949 handles[0] = that->m->updateReq;
3950
3951 do
3952 {
3953 AutoCaller autoCaller(that);
3954 /* VirtualBox has been early uninitialized, terminate */
3955 if (!autoCaller.isOk())
3956 break;
3957
3958 do
3959 {
3960 /* release the caller to let uninit() ever proceed */
3961 autoCaller.release();
3962
3963 DWORD rc = ::WaitForMultipleObjects((DWORD)(1 + cnt + cntSpawned),
3964 handles,
3965 FALSE,
3966 INFINITE);
3967
3968 /* Restore the caller before using VirtualBox. If it fails, this
3969 * means VirtualBox is being uninitialized and we must terminate. */
3970 autoCaller.add();
3971 if (!autoCaller.isOk())
3972 break;
3973
3974 bool update = false;
3975
3976 if (rc == WAIT_OBJECT_0)
3977 {
3978 /* update event is signaled */
3979 update = true;
3980 }
3981 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
3982 {
3983 /* machine mutex is released */
3984 (machines[rc - WAIT_OBJECT_0 - 1])->checkForDeath();
3985 update = true;
3986 }
3987 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
3988 {
3989 /* machine mutex is abandoned due to client process termination */
3990 (machines[rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
3991 update = true;
3992 }
3993 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
3994 {
3995 /* spawned VM process has terminated (normally or abnormally) */
3996 (spawnedMachines[rc - WAIT_OBJECT_0 - cnt - 1])->
3997 checkForSpawnFailure();
3998 update = true;
3999 }
4000
4001 if (update)
4002 {
4003 /* close old process handles */
4004 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i)
4005 CloseHandle(handles[i]);
4006
4007 // lock the machines list for reading
4008 AutoReadLock thatLock(that->m->ollMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4009
4010 /* obtain a new set of opened machines */
4011 cnt = 0;
4012 machines.clear();
4013
4014 for (MachinesOList::iterator it = that->m->ollMachines.begin();
4015 it != that->m->ollMachines.end();
4016 ++it)
4017 {
4018 /// @todo handle situations with more than 64 objects
4019 AssertMsgBreak((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
4020 ("MAXIMUM_WAIT_OBJECTS reached"));
4021
4022 ComObjPtr<SessionMachine> sm;
4023 HANDLE ipcSem;
4024 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
4025 {
4026 machines.push_back(sm);
4027 handles[1 + cnt] = ipcSem;
4028 ++cnt;
4029 }
4030 }
4031
4032 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4033
4034 /* obtain a new set of spawned machines */
4035 cntSpawned = 0;
4036 spawnedMachines.clear();
4037
4038 for (MachinesOList::iterator it = that->m->ollMachines.begin();
4039 it != that->m->ollMachines.end();
4040 ++it)
4041 {
4042 /// @todo handle situations with more than 64 objects
4043 AssertMsgBreak((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
4044 ("MAXIMUM_WAIT_OBJECTS reached"));
4045
4046 RTPROCESS pid;
4047 if ((*it)->isSessionSpawning(&pid))
4048 {
4049 HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid);
4050 AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
4051 pid, GetLastError()));
4052 if (rc == 0)
4053 {
4054 spawnedMachines.push_back(*it);
4055 handles[1 + cnt + cntSpawned] = ph;
4056 ++cntSpawned;
4057 }
4058 }
4059 }
4060
4061 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4062
4063 // machines lock unwinds here
4064 }
4065 }
4066 while (true);
4067 }
4068 while (0);
4069
4070 /* close old process handles */
4071 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
4072 CloseHandle(handles[i]);
4073
4074 /* release sets of machines if any */
4075 machines.clear();
4076 spawnedMachines.clear();
4077
4078 ::CoUninitialize();
4079
4080#elif defined(RT_OS_OS2)
4081
4082 /// @todo (dmik) processes reaping!
4083
4084 /* according to PMREF, 64 is the maximum for the muxwait list */
4085 SEMRECORD handles[64];
4086
4087 HMUX muxSem = NULLHANDLE;
4088
4089 do
4090 {
4091 AutoCaller autoCaller(that);
4092 /* VirtualBox has been early uninitialized, terminate */
4093 if (!autoCaller.isOk())
4094 break;
4095
4096 do
4097 {
4098 /* release the caller to let uninit() ever proceed */
4099 autoCaller.release();
4100
4101 int vrc = RTSemEventWait(that->m->updateReq, 500);
4102
4103 /* Restore the caller before using VirtualBox. If it fails, this
4104 * means VirtualBox is being uninitialized and we must terminate. */
4105 autoCaller.add();
4106 if (!autoCaller.isOk())
4107 break;
4108
4109 bool update = false;
4110 bool updateSpawned = false;
4111
4112 if (RT_SUCCESS(vrc))
4113 {
4114 /* update event is signaled */
4115 update = true;
4116 updateSpawned = true;
4117 }
4118 else
4119 {
4120 AssertMsg(vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
4121 ("RTSemEventWait returned %Rrc\n", vrc));
4122
4123 /* are there any mutexes? */
4124 if (cnt > 0)
4125 {
4126 /* figure out what's going on with machines */
4127
4128 unsigned long semId = 0;
4129 APIRET arc = ::DosWaitMuxWaitSem(muxSem,
4130 SEM_IMMEDIATE_RETURN, &semId);
4131
4132 if (arc == NO_ERROR)
4133 {
4134 /* machine mutex is normally released */
4135 Assert(semId >= 0 && semId < cnt);
4136 if (semId >= 0 && semId < cnt)
4137 {
4138#ifdef DEBUG
4139 {
4140 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
4141 LogFlowFunc(("released mutex: machine='%ls'\n",
4142 machines[semId]->name().raw()));
4143 }
4144#endif
4145 machines[semId]->checkForDeath();
4146 }
4147 update = true;
4148 }
4149 else if (arc == ERROR_SEM_OWNER_DIED)
4150 {
4151 /* machine mutex is abandoned due to client process
4152 * termination; find which mutex is in the Owner Died
4153 * state */
4154 for (size_t i = 0; i < cnt; ++ i)
4155 {
4156 PID pid; TID tid;
4157 unsigned long reqCnt;
4158 arc = DosQueryMutexSem((HMTX)handles[i].hsemCur, &pid, &tid, &reqCnt);
4159 if (arc == ERROR_SEM_OWNER_DIED)
4160 {
4161 /* close the dead mutex as asked by PMREF */
4162 ::DosCloseMutexSem((HMTX)handles[i].hsemCur);
4163
4164 Assert(i >= 0 && i < cnt);
4165 if (i >= 0 && i < cnt)
4166 {
4167#ifdef DEBUG
4168 {
4169 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
4170 LogFlowFunc(("mutex owner dead: machine='%ls'\n",
4171 machines[i]->name().raw()));
4172 }
4173#endif
4174 machines[i]->checkForDeath();
4175 }
4176 }
4177 }
4178 update = true;
4179 }
4180 else
4181 AssertMsg(arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
4182 ("DosWaitMuxWaitSem returned %d\n", arc));
4183 }
4184
4185 /* are there any spawning sessions? */
4186 if (cntSpawned > 0)
4187 {
4188 for (size_t i = 0; i < cntSpawned; ++ i)
4189 updateSpawned |= (spawnedMachines[i])->
4190 checkForSpawnFailure();
4191 }
4192 }
4193
4194 if (update || updateSpawned)
4195 {
4196 AutoReadLock thatLock(that COMMA_LOCKVAL_SRC_POS);
4197
4198 if (update)
4199 {
4200 /* close the old muxsem */
4201 if (muxSem != NULLHANDLE)
4202 ::DosCloseMuxWaitSem(muxSem);
4203
4204 /* obtain a new set of opened machines */
4205 cnt = 0;
4206 machines.clear();
4207
4208 for (MachinesOList::iterator it = that->m->llMachines.begin();
4209 it != that->m->llMachines.end(); ++ it)
4210 {
4211 /// @todo handle situations with more than 64 objects
4212 AssertMsg(cnt <= 64 /* according to PMREF */,
4213 ("maximum of 64 mutex semaphores reached (%d)",
4214 cnt));
4215
4216 ComObjPtr<SessionMachine> sm;
4217 HMTX ipcSem;
4218 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
4219 {
4220 machines.push_back(sm);
4221 handles[cnt].hsemCur = (HSEM)ipcSem;
4222 handles[cnt].ulUser = cnt;
4223 ++ cnt;
4224 }
4225 }
4226
4227 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4228
4229 if (cnt > 0)
4230 {
4231 /* create a new muxsem */
4232 APIRET arc = ::DosCreateMuxWaitSem(NULL, &muxSem, cnt,
4233 handles,
4234 DCMW_WAIT_ANY);
4235 AssertMsg(arc == NO_ERROR,
4236 ("DosCreateMuxWaitSem returned %d\n", arc));
4237 NOREF(arc);
4238 }
4239 }
4240
4241 if (updateSpawned)
4242 {
4243 /* obtain a new set of spawned machines */
4244 spawnedMachines.clear();
4245
4246 for (MachinesOList::iterator it = that->m->llMachines.begin();
4247 it != that->m->llMachines.end(); ++ it)
4248 {
4249 if ((*it)->isSessionSpawning())
4250 spawnedMachines.push_back(*it);
4251 }
4252
4253 cntSpawned = spawnedMachines.size();
4254 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4255 }
4256 }
4257 }
4258 while (true);
4259 }
4260 while (0);
4261
4262 /* close the muxsem */
4263 if (muxSem != NULLHANDLE)
4264 ::DosCloseMuxWaitSem(muxSem);
4265
4266 /* release sets of machines if any */
4267 machines.clear();
4268 spawnedMachines.clear();
4269
4270#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
4271
4272 bool update = false;
4273 bool updateSpawned = false;
4274
4275 do
4276 {
4277 AutoCaller autoCaller(that);
4278 if (!autoCaller.isOk())
4279 break;
4280
4281 do
4282 {
4283 /* release the caller to let uninit() ever proceed */
4284 autoCaller.release();
4285
4286 int rc = RTSemEventWait(that->m->updateReq, 500);
4287
4288 /*
4289 * Restore the caller before using VirtualBox. If it fails, this
4290 * means VirtualBox is being uninitialized and we must terminate.
4291 */
4292 autoCaller.add();
4293 if (!autoCaller.isOk())
4294 break;
4295
4296 if (RT_SUCCESS(rc) || update || updateSpawned)
4297 {
4298 /* RT_SUCCESS(rc) means an update event is signaled */
4299
4300 // lock the machines list for reading
4301 AutoReadLock thatLock(that->m->ollMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4302
4303 if (RT_SUCCESS(rc) || update)
4304 {
4305 /* obtain a new set of opened machines */
4306 machines.clear();
4307
4308 for (MachinesOList::iterator it = that->m->ollMachines.begin();
4309 it != that->m->ollMachines.end();
4310 ++it)
4311 {
4312 ComObjPtr<SessionMachine> sm;
4313 if ((*it)->isSessionOpenOrClosing(sm))
4314 machines.push_back(sm);
4315 }
4316
4317 cnt = machines.size();
4318 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4319 }
4320
4321 if (RT_SUCCESS(rc) || updateSpawned)
4322 {
4323 /* obtain a new set of spawned machines */
4324 spawnedMachines.clear();
4325
4326 for (MachinesOList::iterator it = that->m->ollMachines.begin();
4327 it != that->m->ollMachines.end();
4328 ++it)
4329 {
4330 if ((*it)->isSessionSpawning())
4331 spawnedMachines.push_back(*it);
4332 }
4333
4334 cntSpawned = spawnedMachines.size();
4335 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4336 }
4337
4338 // machines lock unwinds here
4339 }
4340
4341 update = false;
4342 for (size_t i = 0; i < cnt; ++ i)
4343 update |= (machines[i])->checkForDeath();
4344
4345 updateSpawned = false;
4346 for (size_t i = 0; i < cntSpawned; ++ i)
4347 updateSpawned |= (spawnedMachines[i])->checkForSpawnFailure();
4348
4349 /* reap child processes */
4350 {
4351 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
4352 if (that->m->llProcesses.size())
4353 {
4354 LogFlowFunc(("UPDATE: child process count = %d\n",
4355 that->m->llProcesses.size()));
4356 VirtualBox::Data::ProcessList::iterator it = that->m->llProcesses.begin();
4357 while (it != that->m->llProcesses.end())
4358 {
4359 RTPROCESS pid = *it;
4360 RTPROCSTATUS status;
4361 int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status);
4362 if (vrc == VINF_SUCCESS)
4363 {
4364 LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n",
4365 pid, pid, status.iStatus,
4366 status.enmReason));
4367 it = that->m->llProcesses.erase(it);
4368 }
4369 else
4370 {
4371 LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
4372 pid, pid, vrc));
4373 if (vrc != VERR_PROCESS_RUNNING)
4374 {
4375 /* remove the process if it is not already running */
4376 it = that->m->llProcesses.erase(it);
4377 }
4378 else
4379 ++ it;
4380 }
4381 }
4382 }
4383 }
4384 }
4385 while (true);
4386 }
4387 while (0);
4388
4389 /* release sets of machines if any */
4390 machines.clear();
4391 spawnedMachines.clear();
4392
4393#else
4394# error "Port me!"
4395#endif
4396
4397 LogFlowFuncLeave();
4398 return 0;
4399}
4400
4401/**
4402 * Thread function that handles custom events posted using #postEvent().
4403 */
4404// static
4405DECLCALLBACK(int) VirtualBox::AsyncEventHandler(RTTHREAD thread, void *pvUser)
4406{
4407 LogFlowFuncEnter();
4408
4409 AssertReturn(pvUser, VERR_INVALID_POINTER);
4410
4411 // create an event queue for the current thread
4412 EventQueue *eventQ = new EventQueue();
4413 AssertReturn(eventQ, VERR_NO_MEMORY);
4414
4415 // return the queue to the one who created this thread
4416 *(static_cast <EventQueue **>(pvUser)) = eventQ;
4417 // signal that we're ready
4418 RTThreadUserSignal(thread);
4419
4420 BOOL ok = TRUE;
4421 Event *event = NULL;
4422
4423 while ((ok = eventQ->waitForEvent(&event)) && event)
4424 eventQ->handleEvent(event);
4425
4426 AssertReturn(ok, VERR_GENERAL_FAILURE);
4427
4428 delete eventQ;
4429
4430 LogFlowFuncLeave();
4431
4432 return 0;
4433}
4434
4435////////////////////////////////////////////////////////////////////////////////
4436
4437/**
4438 * Takes the current list of registered callbacks of the managed VirtualBox
4439 * instance, and calls #handleCallback() for every callback item from the
4440 * list, passing the item as an argument.
4441 *
4442 * @note Locks the managed VirtualBox object for reading but leaves the lock
4443 * before iterating over callbacks and calling their methods.
4444 */
4445void *VirtualBox::CallbackEvent::handler()
4446{
4447 if (!mVirtualBox)
4448 return NULL;
4449
4450 AutoCaller autoCaller(mVirtualBox);
4451 if (!autoCaller.isOk())
4452 {
4453 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",
4454 autoCaller.state()));
4455 /* We don't need mVirtualBox any more, so release it */
4456 mVirtualBox = NULL;
4457 return NULL;
4458 }
4459
4460 CallbackList callbacks;
4461 {
4462 /* Make a copy to release the lock before iterating */
4463 AutoReadLock alock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
4464 callbacks = mVirtualBox->m->llCallbacks;
4465 }
4466
4467 for (CallbackList::const_iterator it = callbacks.begin();
4468 it != callbacks.end();
4469 ++it)
4470 {
4471 if (it->isWanted(mWhat))
4472 {
4473 HRESULT hrc = handleCallback(it->ptrICb);
4474 if (hrc == VBOX_E_DONT_CALL_AGAIN)
4475 {
4476 /* Have to update the original. */
4477 AutoReadLock alock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
4478 CallbackList::iterator itOrg;
4479 itOrg = std::find(mVirtualBox->m->llCallbacks.begin(),
4480 mVirtualBox->m->llCallbacks.end(),
4481 CallbackList::value_type(it->ptrICb));
4482 if (itOrg != mVirtualBox->m->llCallbacks.end())
4483 itOrg->setDontCallAgain(mWhat);
4484 }
4485 }
4486 }
4487
4488 mVirtualBox = NULL; /* Not needed any longer. Still make sense to do this? */
4489 return NULL;
4490}
4491
4492//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface(/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
4493//{
4494// return E_NOTIMPL;
4495//}
4496
4497STDMETHODIMP VirtualBox::CreateDHCPServer(IN_BSTR aName, IDHCPServer ** aServer)
4498{
4499 CheckComArgStrNotEmptyOrNull(aName);
4500 CheckComArgNotNull(aServer);
4501
4502 AutoCaller autoCaller(this);
4503 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4504
4505 ComObjPtr<DHCPServer> dhcpServer;
4506 dhcpServer.createObject();
4507 HRESULT rc = dhcpServer->init(this, aName);
4508 if (FAILED(rc)) return rc;
4509
4510 rc = registerDHCPServer(dhcpServer, true);
4511 if (FAILED(rc)) return rc;
4512
4513 dhcpServer.queryInterfaceTo(aServer);
4514
4515 return rc;
4516}
4517
4518STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName(IN_BSTR aName, IDHCPServer ** aServer)
4519{
4520 CheckComArgStrNotEmptyOrNull(aName);
4521 CheckComArgNotNull(aServer);
4522
4523 AutoCaller autoCaller(this);
4524 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4525
4526 HRESULT rc;
4527 Bstr bstr;
4528 ComPtr<DHCPServer> found;
4529
4530 AutoReadLock alock(m->ollDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4531
4532 for (DHCPServersOList::const_iterator it = m->ollDHCPServers.begin();
4533 it != m->ollDHCPServers.end();
4534 ++it)
4535 {
4536 rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());
4537 if (FAILED(rc)) throw rc;
4538
4539 if (bstr == aName)
4540 {
4541 found = *it;
4542 break;
4543 }
4544 }
4545
4546 if (!found)
4547 return E_INVALIDARG;
4548
4549 return found.queryInterfaceTo(aServer);
4550}
4551
4552STDMETHODIMP VirtualBox::RemoveDHCPServer(IDHCPServer * aServer)
4553{
4554 CheckComArgNotNull(aServer);
4555
4556 AutoCaller autoCaller(this);
4557 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4558
4559 HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);
4560
4561 return rc;
4562}
4563
4564/**
4565 * Remembers the given dhcp server by storing it in the hard disk registry.
4566 *
4567 * @param aDHCPServer Dhcp Server object to remember.
4568 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4569 *
4570 * When @a aSaveRegistry is @c true, this operation may fail because of the
4571 * failed #saveSettings() method it calls. In this case, the dhcp server object
4572 * will not be remembered. It is therefore the responsibility of the caller to
4573 * call this method as the last step of some action that requires registration
4574 * in order to make sure that only fully functional dhcp server objects get
4575 * registered.
4576 *
4577 * @note Locks this object for writing and @a aDHCPServer for reading.
4578 */
4579HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,
4580 bool aSaveRegistry /*= true*/)
4581{
4582 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4583
4584 AutoCaller autoCaller(this);
4585 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4586
4587 AutoCaller dhcpServerCaller(aDHCPServer);
4588 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4589
4590 Bstr name;
4591 HRESULT rc;
4592 rc = aDHCPServer->COMGETTER(NetworkName)(name.asOutParam());
4593 if (FAILED(rc)) return rc;
4594
4595 ComPtr<IDHCPServer> existing;
4596 rc = FindDHCPServerByNetworkName(name, existing.asOutParam());
4597 if (SUCCEEDED(rc))
4598 {
4599 return E_INVALIDARG;
4600 }
4601
4602 rc = S_OK;
4603
4604 m->ollDHCPServers.addChild(aDHCPServer);
4605
4606 if (aSaveRegistry)
4607 {
4608 rc = saveSettings();
4609 if (FAILED(rc))
4610 unregisterDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4611 }
4612
4613 return rc;
4614}
4615
4616/**
4617 * Removes the given hard disk from the hard disk registry.
4618 *
4619 * @param aHardDisk Hard disk object to remove.
4620 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4621 *
4622 * When @a aSaveRegistry is @c true, this operation may fail because of the
4623 * failed #saveSettings() method it calls. In this case, the hard disk object
4624 * will NOT be removed from the registry when this method returns. It is
4625 * therefore the responsibility of the caller to call this method as the first
4626 * step of some action that requires unregistration, before calling uninit() on
4627 * @a aHardDisk.
4628 *
4629 * @note Locks this object for writing and @a aHardDisk for reading.
4630 */
4631HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,
4632 bool aSaveRegistry /*= true*/)
4633{
4634 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4635
4636 AutoCaller autoCaller(this);
4637 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4638
4639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4640
4641 AutoCaller dhcpServerCaller(aDHCPServer);
4642 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4643
4644 m->ollDHCPServers.removeChild(aDHCPServer);
4645
4646 HRESULT rc = S_OK;
4647
4648 if (aSaveRegistry)
4649 {
4650 rc = saveSettings();
4651 if (FAILED(rc))
4652 registerDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4653 }
4654
4655 return rc;
4656}
4657
4658/* 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