VirtualBox

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

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

Main: events bits

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