VirtualBox

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

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

Main: make calculateRelativePath methods a bit smarter and rename them to VirtualBox::copyPathRelativeToConfig() and Machine::copyPathRelativeToMachine()

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

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