VirtualBox

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

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

windows build fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 155.5 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 30717 2010-07-07 16:53:36Z vboxsync $ */
2
3/** @file
4 * Implementation of IVirtualBox in VBoxSVC.
5 */
6
7/*
8 * Copyright (C) 2006-2010 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include <iprt/asm.h>
20#include <iprt/buildconfig.h>
21#include <iprt/cpp/utils.h>
22#include <iprt/dir.h>
23#include <iprt/env.h>
24#include <iprt/file.h>
25#include <iprt/path.h>
26#include <iprt/process.h>
27#include <iprt/string.h>
28#include <iprt/stream.h>
29#include <iprt/thread.h>
30#include <iprt/uuid.h>
31#include <iprt/cpp/xml.h>
32
33#include <VBox/com/com.h>
34#include <VBox/com/array.h>
35#include "VBox/com/EventQueue.h"
36
37#include <VBox/err.h>
38#include <VBox/param.h>
39#include <VBox/VBoxHDD.h>
40#include <VBox/settings.h>
41#include <VBox/version.h>
42
43#include <package-generated.h>
44
45#include <algorithm>
46#include <set>
47#include <vector>
48#include <memory> // for auto_ptr
49
50#include <typeinfo>
51
52#include "VirtualBoxImpl.h"
53
54#include "Global.h"
55#include "MachineImpl.h"
56#include "MediumImpl.h"
57#include "SharedFolderImpl.h"
58#include "ProgressImpl.h"
59#include "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 = d->that->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 if (!exePath)
2436 {
2437 rc = d->that->setError(E_FAIL, tr("Cannot get executable name"));
2438 break;
2439 }
2440
2441 Utf8Str argsStr = Utf8StrFmt("/Helper %s", client.name().raw());
2442
2443 LogFlowFunc(("Starting '\"%s\" %s'...\n", exePath, argsStr.raw()));
2444
2445 RTPROCESS pid = NIL_RTPROCESS;
2446
2447 if (d->privileged)
2448 {
2449 /* Attempt to start a privileged process using the Run As dialog */
2450
2451 Bstr file = exePath;
2452 Bstr parameters = argsStr;
2453
2454 SHELLEXECUTEINFO shExecInfo;
2455
2456 shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
2457
2458 shExecInfo.fMask = NULL;
2459 shExecInfo.hwnd = NULL;
2460 shExecInfo.lpVerb = L"runas";
2461 shExecInfo.lpFile = file;
2462 shExecInfo.lpParameters = parameters;
2463 shExecInfo.lpDirectory = NULL;
2464 shExecInfo.nShow = SW_NORMAL;
2465 shExecInfo.hInstApp = NULL;
2466
2467 if (!ShellExecuteEx(&shExecInfo))
2468 {
2469 int vrc2 = RTErrConvertFromWin32(GetLastError());
2470 /* hide excessive details in case of a frequent error
2471 * (pressing the Cancel button to close the Run As dialog) */
2472 if (vrc2 == VERR_CANCELLED)
2473 rc = d->that->setError(E_FAIL,
2474 tr("Operation cancelled by the user"));
2475 else
2476 rc = d->that->setError(E_FAIL,
2477 tr("Could not launch a privileged process '%s' (%Rrc)"),
2478 exePath, vrc2);
2479 break;
2480 }
2481 }
2482 else
2483 {
2484 const char *args[] = { exePath, "/Helper", client.name().c_str(), 0 };
2485 vrc = RTProcCreate(exePath, args, RTENV_DEFAULT, 0, &pid);
2486 if (RT_FAILURE(vrc))
2487 {
2488 rc = d->that->setError(E_FAIL,
2489 tr("Could not launch a process '%s' (%Rrc)"), exePath, vrc);
2490 break;
2491 }
2492 }
2493
2494 /* wait for the client to connect */
2495 vrc = client.connect();
2496 if (RT_SUCCESS(vrc))
2497 {
2498 /* start the user supplied function */
2499 rc = d->func(&client, d->progress, d->user, &vrc);
2500 userFuncCalled = true;
2501 }
2502
2503 /* send the termination signal to the process anyway */
2504 {
2505 int vrc2 = client.write(SVCHlpMsg::Null);
2506 if (RT_SUCCESS(vrc))
2507 vrc = vrc2;
2508 }
2509
2510 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
2511 {
2512 rc = d->that->setError(E_FAIL,
2513 tr("Could not operate the communication channel (%Rrc)"), vrc);
2514 break;
2515 }
2516 }
2517 while (0);
2518
2519 if (FAILED(rc) && !userFuncCalled)
2520 {
2521 /* call the user function in the "cleanup only" mode
2522 * to let it free resources passed to in aUser */
2523 d->func(NULL, NULL, d->user, NULL);
2524 }
2525
2526 d->progress->notifyComplete(rc);
2527
2528 LogFlowFuncLeave();
2529 return 0;
2530}
2531
2532#endif /* RT_OS_WINDOWS */
2533
2534/**
2535 * Sends a signal to the client watcher thread to rescan the set of machines
2536 * that have open sessions.
2537 *
2538 * @note Doesn't lock anything.
2539 */
2540void VirtualBox::updateClientWatcher()
2541{
2542 AutoCaller autoCaller(this);
2543 AssertComRCReturnVoid(autoCaller.rc());
2544
2545 AssertReturnVoid(m->threadClientWatcher != NIL_RTTHREAD);
2546
2547 /* sent an update request */
2548#if defined(RT_OS_WINDOWS)
2549 ::SetEvent(m->updateReq);
2550#elif defined(RT_OS_OS2)
2551 RTSemEventSignal(m->updateReq);
2552#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
2553 RTSemEventSignal(m->updateReq);
2554#else
2555# error "Port me!"
2556#endif
2557}
2558
2559/**
2560 * Adds the given child process ID to the list of processes to be reaped.
2561 * This call should be followed by #updateClientWatcher() to take the effect.
2562 */
2563void VirtualBox::addProcessToReap(RTPROCESS pid)
2564{
2565 AutoCaller autoCaller(this);
2566 AssertComRCReturnVoid(autoCaller.rc());
2567
2568 /// @todo (dmik) Win32?
2569#ifndef RT_OS_WINDOWS
2570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2571 m->llProcesses.push_back(pid);
2572#endif
2573}
2574
2575/**
2576 * Removes a dead callback.
2577 * @param aCallback The reference to the registered callback interface.
2578 */
2579void VirtualBox::removeDeadCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2580{
2581 /* find and delete */
2582 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2583 CallbackList::iterator it = std::find(m->llCallbacks.begin(),
2584 m->llCallbacks.end(),
2585 CallbackList::value_type(aCallback));
2586 if (it != m->llCallbacks.end())
2587 {
2588 LogRel(("Removing dead callback: %p\n", &*it));
2589 m->llCallbacks.erase(it);
2590 }
2591}
2592
2593/** Event for onMachineStateChange(), onMachineDataChange(), onMachineRegistered() */
2594struct MachineEvent : public VirtualBox::CallbackEvent
2595{
2596 MachineEvent(VirtualBox *aVB, const Guid &aId)
2597 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineDataChange), id(aId.toUtf16())
2598 {}
2599
2600 MachineEvent(VirtualBox *aVB, const Guid &aId, MachineState_T aState)
2601 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineStateChange), id(aId.toUtf16())
2602 , state(aState)
2603 {}
2604
2605 MachineEvent(VirtualBox *aVB, const Guid &aId, BOOL aRegistered)
2606 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineRegistered), id(aId.toUtf16())
2607 , registered(aRegistered)
2608 {}
2609
2610 HRESULT handleCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2611 {
2612 switch (mWhat)
2613 {
2614 case VirtualBoxCallbackRegistration::kOnMachineDataChange:
2615 LogFlow(("OnMachineDataChange: id={%ls}\n", id.raw()));
2616 return aCallback->OnMachineDataChange(id);
2617
2618 case VirtualBoxCallbackRegistration::kOnMachineStateChange:
2619 LogFlow(("OnMachineStateChange: id={%ls}, state=%d\n",
2620 id.raw(), state));
2621 return aCallback->OnMachineStateChange(id, state);
2622
2623 case VirtualBoxCallbackRegistration::kOnMachineRegistered:
2624 LogFlow(("OnMachineRegistered: id={%ls}, registered=%d\n",
2625 id.raw(), registered));
2626 return aCallback->OnMachineRegistered(id, registered);
2627
2628 default:
2629 AssertFailedReturn(S_OK);
2630 }
2631 }
2632#ifdef RT_OS_WINDOWS
2633 HRESULT prepareComEventDesc(ComEventDesc& aEvDesc)
2634 {
2635 switch (mWhat)
2636 {
2637 case VirtualBoxCallbackRegistration::kOnMachineDataChange:
2638 aEvDesc.init("OnMachineDataChange", 1);
2639 aEvDesc.add(id);
2640 break;
2641
2642 case VirtualBoxCallbackRegistration::kOnMachineStateChange:
2643 aEvDesc.init("OnMachineStateChange", 2);
2644 aEvDesc.add(id).add((int)state);
2645 break;
2646
2647 case VirtualBoxCallbackRegistration::kOnMachineRegistered:
2648 aEvDesc.init("OnMachineRegistered", 2);
2649 aEvDesc.add(id).add(registered);
2650 break;
2651
2652 default:
2653 AssertFailedReturn(S_OK);
2654 }
2655 return S_OK;
2656 }
2657#endif
2658 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2659 {
2660 switch (mWhat)
2661 {
2662 case VirtualBoxCallbackRegistration::kOnMachineDataChange:
2663 aEvDesc.init(aSource, VBoxEventType_OnMachineDataChange, id.raw());
2664 break;
2665
2666 case VirtualBoxCallbackRegistration::kOnMachineStateChange:
2667 aEvDesc.init(aSource, VBoxEventType_OnMachineStateChange, id.raw(), state);
2668 break;
2669
2670 case VirtualBoxCallbackRegistration::kOnMachineRegistered:
2671 aEvDesc.init(aSource, VBoxEventType_OnMachineRegistered, id.raw(), registered);
2672 break;
2673
2674 default:
2675 AssertFailedReturn(S_OK);
2676 }
2677 return S_OK;
2678 }
2679
2680 Bstr id;
2681 MachineState_T state;
2682 BOOL registered;
2683};
2684
2685/**
2686 * @note Doesn't lock any object.
2687 */
2688void VirtualBox::onMachineStateChange(const Guid &aId, MachineState_T aState)
2689{
2690 postEvent(new MachineEvent(this, aId, aState));
2691}
2692
2693/**
2694 * @note Doesn't lock any object.
2695 */
2696void VirtualBox::onMachineDataChange(const Guid &aId)
2697{
2698 postEvent(new MachineEvent(this, aId));
2699}
2700
2701/**
2702 * @note Locks this object for reading.
2703 */
2704BOOL VirtualBox::onExtraDataCanChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue,
2705 Bstr &aError)
2706{
2707 LogFlowThisFunc(("machine={%s} aKey={%ls} aValue={%ls}\n",
2708 aId.toString().raw(), aKey, aValue));
2709
2710 AutoCaller autoCaller(this);
2711 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2712
2713 CallbackList list;
2714 {
2715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2716 list = m->llCallbacks;
2717 }
2718
2719 BOOL allowChange = TRUE;
2720 CallbackList::iterator it = list.begin();
2721 Bstr id = aId.toUtf16();
2722 while ((it != list.end()) && allowChange)
2723 {
2724 if (it->isWanted(VirtualBoxCallbackRegistration::kOnExtraDataCanChange))
2725 {
2726 HRESULT rc = it->ptrICb->OnExtraDataCanChange(id, aKey, aValue,
2727 aError.asOutParam(),
2728 &allowChange);
2729 if (FAILED(rc))
2730 {
2731 if (rc == VBOX_E_DONT_CALL_AGAIN)
2732 {
2733 /* Have to update the original. */
2734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2735 CallbackList::iterator itOrg;
2736 itOrg = std::find(m->llCallbacks.begin(),
2737 m->llCallbacks.end(),
2738 CallbackList::value_type(it->ptrICb));
2739 if (itOrg != m->llCallbacks.end())
2740 itOrg->setDontCallAgain(VirtualBoxCallbackRegistration::kOnExtraDataCanChange);
2741 }
2742 else if (FAILED_DEAD_INTERFACE(rc))
2743 removeDeadCallback(it->ptrICb);
2744
2745 /* if a call to this method fails for some reason (for ex., because
2746 * the other side is dead), we ensure allowChange stays true
2747 * (MS COM RPC implementation seems to zero all output vars before
2748 * issuing an IPC call or after a failure, so it's essential
2749 * there) */
2750 allowChange = TRUE;
2751 }
2752 }
2753 ++it;
2754 }
2755
2756 VBoxEventDesc evDesc;
2757 evDesc.init(m->pEventSource, VBoxEventType_OnExtraDataCanChange, id.raw(), aKey, aValue);
2758 BOOL fDelivered = evDesc.fire(3000); /* Wait up to 3 secs for delivery */
2759 //Assert(fDelivered);
2760 if (fDelivered)
2761 {
2762 ComPtr<IEvent> aEvent;
2763 evDesc.getEvent(aEvent.asOutParam());
2764 ComPtr<IExtraDataCanChangeEvent> aCanChangeEvent = aEvent;
2765 Assert(aCanChangeEvent);
2766 BOOL fVetoed = FALSE;
2767 aCanChangeEvent->IsVetoed(&fVetoed);
2768 allowChange = !fVetoed;
2769
2770 if (!allowChange)
2771 {
2772 SafeArray<BSTR> aVetos;
2773 aCanChangeEvent->GetVetos(ComSafeArrayAsOutParam(aVetos));
2774 if (aVetos.size() > 0)
2775 aError = aVetos[0];
2776 }
2777 }
2778 else
2779 allowChange = TRUE;
2780
2781 LogFlowThisFunc(("allowChange=%RTbool\n", allowChange));
2782 return allowChange;
2783}
2784
2785/** Event for onExtraDataChange() */
2786struct ExtraDataEvent : public VirtualBox::CallbackEvent
2787{
2788 ExtraDataEvent(VirtualBox *aVB, const Guid &aMachineId,
2789 IN_BSTR aKey, IN_BSTR aVal)
2790 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnExtraDataChange)
2791 , machineId(aMachineId.toUtf16()), key(aKey), val(aVal)
2792 {}
2793
2794 HRESULT handleCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2795 {
2796 LogFlow(("OnExtraDataChange: machineId={%ls}, key='%ls', val='%ls'\n",
2797 machineId.raw(), key.raw(), val.raw()));
2798 return aCallback->OnExtraDataChange(machineId, key, val);
2799 }
2800#ifdef RT_OS_WINDOWS
2801 HRESULT prepareComEventDesc(ComEventDesc& aEvDesc)
2802 {
2803 aEvDesc.init("OnExtraDataChange", 3);
2804 aEvDesc.add(machineId).add(key).add(val);
2805 return S_OK;
2806 }
2807#endif
2808 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2809 {
2810 return aEvDesc.init(aSource, VBoxEventType_OnExtraDataChange, machineId.raw(), key.raw(), val.raw());
2811 }
2812
2813 Bstr machineId, key, val;
2814};
2815
2816/**
2817 * @note Doesn't lock any object.
2818 */
2819void VirtualBox::onExtraDataChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue)
2820{
2821 postEvent(new ExtraDataEvent(this, aId, aKey, aValue));
2822}
2823
2824/**
2825 * @note Doesn't lock any object.
2826 */
2827void VirtualBox::onMachineRegistered(const Guid &aId, BOOL aRegistered)
2828{
2829 postEvent(new MachineEvent(this, aId, aRegistered));
2830}
2831
2832/** Event for onSessionStateChange() */
2833struct SessionEvent : public VirtualBox::CallbackEvent
2834{
2835 SessionEvent(VirtualBox *aVB, const Guid &aMachineId, SessionState_T aState)
2836 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnSessionStateChange)
2837 , machineId(aMachineId.toUtf16()), sessionState(aState)
2838 {}
2839
2840 HRESULT handleCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2841 {
2842 LogFlow(("OnSessionStateChange: machineId={%ls}, sessionState=%d\n",
2843 machineId.raw(), sessionState));
2844 return aCallback->OnSessionStateChange(machineId, sessionState);
2845 }
2846
2847#ifdef RT_OS_WINDOWS
2848 HRESULT prepareComEventDesc(ComEventDesc& aEvDesc)
2849 {
2850 aEvDesc.init("OnSessionStateChange", 2);
2851 aEvDesc.add(machineId).add((int)sessionState);
2852 return S_OK;
2853 }
2854#endif
2855 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2856 {
2857 return aEvDesc.init(aSource, VBoxEventType_OnSessionStateChange, machineId.raw(), sessionState);
2858 }
2859 Bstr machineId;
2860 SessionState_T sessionState;
2861};
2862
2863/**
2864 * @note Doesn't lock any object.
2865 */
2866void VirtualBox::onSessionStateChange(const Guid &aId, SessionState_T aState)
2867{
2868 postEvent(new SessionEvent(this, aId, aState));
2869}
2870
2871/** Event for onSnapshotTaken(), onSnapshotDeleted() and onSnapshotChange() */
2872struct SnapshotEvent : public VirtualBox::CallbackEvent
2873{
2874 SnapshotEvent(VirtualBox *aVB, const Guid &aMachineId, const Guid &aSnapshotId,
2875 VirtualBoxCallbackRegistration::CallbackBit aWhat)
2876 : CallbackEvent(aVB, aWhat)
2877 , machineId(aMachineId), snapshotId(aSnapshotId)
2878 {}
2879
2880 HRESULT handleCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2881 {
2882 Bstr mid = machineId.toUtf16();
2883 Bstr sid = snapshotId.toUtf16();
2884
2885 switch (mWhat)
2886 {
2887 case VirtualBoxCallbackRegistration::kOnSnapshotTaken:
2888 LogFlow(("OnSnapshotTaken: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2889 machineId.ptr(), snapshotId.ptr()));
2890 return aCallback->OnSnapshotTaken(mid, sid);
2891
2892 case VirtualBoxCallbackRegistration::kOnSnapshotDeleted:
2893 LogFlow(("OnSnapshotDeleted: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2894 machineId.ptr(), snapshotId.ptr()));
2895 return aCallback->OnSnapshotDeleted(mid, sid);
2896
2897 case VirtualBoxCallbackRegistration::kOnSnapshotChange:
2898 LogFlow(("OnSnapshotChange: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2899 machineId.ptr(), snapshotId.ptr()));
2900 return aCallback->OnSnapshotChange(mid, sid);
2901
2902 default:
2903 AssertFailedReturn(S_OK);
2904 }
2905 }
2906
2907#ifdef RT_OS_WINDOWS
2908 HRESULT prepareComEventDesc(ComEventDesc& aEvDesc)
2909 {
2910 aEvDesc.init("OnSnapshotTaken", 2);
2911 aEvDesc.add(machineId.toUtf16()).add(snapshotId.toUtf16());
2912 return S_OK;
2913 }
2914#endif
2915 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2916 {
2917 return aEvDesc.init(aSource, VBoxEventType_OnSnapshotTaken,
2918 machineId.toUtf16().raw(), snapshotId.toUtf16().raw());
2919 }
2920
2921 Guid machineId;
2922 Guid snapshotId;
2923};
2924
2925/**
2926 * @note Doesn't lock any object.
2927 */
2928void VirtualBox::onSnapshotTaken(const Guid &aMachineId, const Guid &aSnapshotId)
2929{
2930 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2931 VirtualBoxCallbackRegistration::kOnSnapshotTaken));
2932}
2933
2934/**
2935 * @note Doesn't lock any object.
2936 */
2937void VirtualBox::onSnapshotDeleted(const Guid &aMachineId, const Guid &aSnapshotId)
2938{
2939 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2940 VirtualBoxCallbackRegistration::kOnSnapshotDeleted));
2941}
2942
2943/**
2944 * @note Doesn't lock any object.
2945 */
2946void VirtualBox::onSnapshotChange(const Guid &aMachineId, const Guid &aSnapshotId)
2947{
2948 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2949 VirtualBoxCallbackRegistration::kOnSnapshotChange));
2950}
2951
2952/** Event for onGuestPropertyChange() */
2953struct GuestPropertyEvent : public VirtualBox::CallbackEvent
2954{
2955 GuestPropertyEvent(VirtualBox *aVBox, const Guid &aMachineId,
2956 IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
2957 : CallbackEvent(aVBox, VirtualBoxCallbackRegistration::kOnGuestPropertyChange),
2958 machineId(aMachineId),
2959 name(aName),
2960 value(aValue),
2961 flags(aFlags)
2962 {}
2963
2964 HRESULT handleCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2965 {
2966 LogFlow(("OnGuestPropertyChange: machineId={%RTuuid}, name='%ls', value='%ls', flags='%ls'\n",
2967 machineId.ptr(), name.raw(), value.raw(), flags.raw()));
2968 return aCallback->OnGuestPropertyChange(machineId.toUtf16(), name, value, flags);
2969 }
2970
2971#ifdef RT_OS_WINDOWS
2972 HRESULT prepareComEventDesc(ComEventDesc& aEvDesc)
2973 {
2974 aEvDesc.init("OnGuestPropertyChange", 4);
2975 aEvDesc.add(machineId.toUtf16()).add(name).add(value).add(flags);
2976 return S_OK;
2977 }
2978#endif
2979 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2980 {
2981 return aEvDesc.init(aSource, VBoxEventType_OnGuestPropertyChange,
2982 machineId.toUtf16().raw(), name.raw(), value.raw(), flags.raw());
2983 }
2984
2985 Guid machineId;
2986 Bstr name, value, flags;
2987};
2988
2989/**
2990 * @note Doesn't lock any object.
2991 */
2992void VirtualBox::onGuestPropertyChange(const Guid &aMachineId, IN_BSTR aName,
2993 IN_BSTR aValue, IN_BSTR aFlags)
2994{
2995 postEvent(new GuestPropertyEvent(this, aMachineId, aName, aValue, aFlags));
2996}
2997
2998/**
2999 * @note Locks this object for reading.
3000 */
3001ComObjPtr<GuestOSType> VirtualBox::getUnknownOSType()
3002{
3003 ComObjPtr<GuestOSType> type;
3004 AutoCaller autoCaller(this);
3005 AssertComRCReturn(autoCaller.rc(), type);
3006
3007 /* unknown type must always be the first */
3008 ComAssertRet(m->allGuestOSTypes.size() > 0, type);
3009
3010 return m->allGuestOSTypes.front();
3011}
3012
3013/**
3014 * Returns the list of opened machines (machines having direct sessions opened
3015 * by client processes) and optionally the list of direct session controls.
3016 *
3017 * @param aMachines Where to put opened machines (will be empty if none).
3018 * @param aControls Where to put direct session controls (optional).
3019 *
3020 * @note The returned lists contain smart pointers. So, clear it as soon as
3021 * it becomes no more necessary to release instances.
3022 *
3023 * @note It can be possible that a session machine from the list has been
3024 * already uninitialized, so do a usual AutoCaller/AutoReadLock sequence
3025 * when accessing unprotected data directly.
3026 *
3027 * @note Locks objects for reading.
3028 */
3029void VirtualBox::getOpenedMachines(SessionMachinesList &aMachines,
3030 InternalControlList *aControls /*= NULL*/)
3031{
3032 AutoCaller autoCaller(this);
3033 AssertComRCReturnVoid(autoCaller.rc());
3034
3035 aMachines.clear();
3036 if (aControls)
3037 aControls->clear();
3038
3039 AutoReadLock alock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3040
3041 for (MachinesOList::iterator it = m->allMachines.begin();
3042 it != m->allMachines.end();
3043 ++it)
3044 {
3045 ComObjPtr<SessionMachine> sm;
3046 ComPtr<IInternalSessionControl> ctl;
3047 if ((*it)->isSessionOpen(sm, &ctl))
3048 {
3049 aMachines.push_back(sm);
3050 if (aControls)
3051 aControls->push_back(ctl);
3052 }
3053 }
3054}
3055
3056/**
3057 * Searches for a Machine object with the given ID in the collection
3058 * of registered machines.
3059 *
3060 * @param id
3061 * ID of the machine
3062 * @param doSetError
3063 * if TRUE, the appropriate error info is set in case when the machine
3064 * is not found
3065 * @param machine
3066 * where to store the found machine object (can be NULL)
3067 *
3068 * @return
3069 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
3070 *
3071 * @note Locks this object for reading.
3072 */
3073HRESULT VirtualBox::findMachine(const Guid &aId,
3074 bool aSetError,
3075 ComObjPtr<Machine> *aMachine /* = NULL */)
3076{
3077 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
3078
3079 AutoCaller autoCaller(this);
3080 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3081
3082 {
3083 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3084
3085 for (MachinesOList::iterator it = m->allMachines.begin();
3086 it != m->allMachines.end();
3087 ++it)
3088 {
3089 ComObjPtr<Machine> pMachine2 = *it;
3090 AutoCaller machCaller(pMachine2);
3091 /* skip inaccessible machines */
3092 if (FAILED(machCaller.rc()))
3093 continue;
3094
3095 if (pMachine2->getId() == aId)
3096 {
3097 rc = S_OK;
3098 if (aMachine)
3099 *aMachine = pMachine2;
3100 break;
3101 }
3102 }
3103 }
3104
3105 if (aSetError && FAILED(rc))
3106 rc = setError(rc,
3107 tr("Could not find a registered machine with UUID {%RTuuid}"),
3108 aId.raw());
3109
3110 return rc;
3111}
3112
3113/**
3114 * Searches for a Medium object with the given ID or location in the list of
3115 * registered hard disks. If both ID and location are specified, the first
3116 * object that matches either of them (not necessarily both) is returned.
3117 *
3118 * @param aId ID of the hard disk (unused when NULL).
3119 * @param aLocation Full location specification (unused NULL).
3120 * @param aSetError If @c true , the appropriate error info is set in case
3121 * when the hard disk is not found.
3122 * @param aHardDisk Where to store the found hard disk object (can be NULL).
3123 *
3124 * @return S_OK when found or E_INVALIDARG when not found.
3125 *
3126 * @note Locks the media tree for reading.
3127 */
3128HRESULT VirtualBox::findHardDisk(const Guid *aId,
3129 CBSTR aLocation,
3130 bool aSetError,
3131 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
3132{
3133 AssertReturn(aId || aLocation, E_INVALIDARG);
3134
3135 // we use the hard disks map, but it is protected by the
3136 // hard disk _list_ lock handle
3137 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3138
3139 /* first, look up by UUID in the map if UUID is provided */
3140 if (aId)
3141 {
3142 HardDiskMap::const_iterator it = m->mapHardDisks.find(*aId);
3143 if (it != m->mapHardDisks.end())
3144 {
3145 if (aHardDisk)
3146 *aHardDisk = (*it).second;
3147 return S_OK;
3148 }
3149 }
3150
3151 /* then iterate and search by location */
3152 int result = -1;
3153 if (aLocation)
3154 {
3155 Utf8Str location = aLocation;
3156
3157 for (HardDiskMap::const_iterator it = m->mapHardDisks.begin();
3158 it != m->mapHardDisks.end();
3159 ++ it)
3160 {
3161 const ComObjPtr<Medium> &hd = (*it).second;
3162
3163 HRESULT rc = hd->compareLocationTo(location.c_str(), result);
3164 if (FAILED(rc)) return rc;
3165
3166 if (result == 0)
3167 {
3168 if (aHardDisk)
3169 *aHardDisk = hd;
3170 break;
3171 }
3172 }
3173 }
3174
3175 HRESULT rc = result == 0 ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
3176
3177 if (aSetError && result != 0)
3178 {
3179 if (aId)
3180 setError(rc,
3181 tr("Could not find a hard disk with UUID {%RTuuid} in the media registry ('%s')"),
3182 aId->raw(),
3183 m->strSettingsFilePath.raw());
3184 else
3185 setError(rc,
3186 tr("Could not find a hard disk with location '%ls' in the media registry ('%s')"),
3187 aLocation,
3188 m->strSettingsFilePath.raw());
3189 }
3190
3191 return rc;
3192}
3193
3194/**
3195 * Searches for a Medium object with the given ID or location in the list of
3196 * registered DVD images. If both ID and file path are specified, the first
3197 * object that matches either of them (not necessarily both) is returned.
3198 *
3199 * @param aId ID of the DVD image (unused when NULL).
3200 * @param aLocation Full path to the image file (unused when NULL).
3201 * @param aSetError If @c true, the appropriate error info is set in case when
3202 * the image is not found.
3203 * @param aImage Where to store the found image object (can be NULL).
3204 *
3205 * @return S_OK when found or E_INVALIDARG when not found.
3206 *
3207 * @note Locks the media tree for reading.
3208 */
3209HRESULT VirtualBox::findDVDImage(const Guid *aId,
3210 CBSTR aLocation,
3211 bool aSetError,
3212 ComObjPtr<Medium> *aImage /* = NULL */)
3213{
3214 AssertReturn(aId || aLocation, E_INVALIDARG);
3215
3216 Utf8Str location;
3217
3218 if (aLocation != NULL)
3219 {
3220 int vrc = calculateFullPath(Utf8Str(aLocation), location);
3221 if (RT_FAILURE(vrc))
3222 return setError(VBOX_E_FILE_ERROR,
3223 tr("Invalid image file location '%ls' (%Rrc)"),
3224 aLocation,
3225 vrc);
3226 }
3227
3228 AutoReadLock alock(m->allDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3229
3230 bool found = false;
3231
3232 for (MediaList::const_iterator it = m->allDVDImages.begin();
3233 it != m->allDVDImages.end();
3234 ++ it)
3235 {
3236 /* no AutoCaller, registered image life time is bound to this */
3237 AutoReadLock imageLock(*it COMMA_LOCKVAL_SRC_POS);
3238
3239 found = (aId && (*it)->getId() == *aId) ||
3240 (aLocation != NULL &&
3241 RTPathCompare(location.c_str(),
3242 (*it)->getLocationFull().c_str()
3243 ) == 0);
3244 if (found)
3245 {
3246 if (aImage)
3247 *aImage = *it;
3248 break;
3249 }
3250 }
3251
3252 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
3253
3254 if (aSetError && !found)
3255 {
3256 if (aId)
3257 setError(rc,
3258 tr("Could not find a CD/DVD image with UUID {%RTuuid} in the media registry ('%s')"),
3259 aId->raw(),
3260 m->strSettingsFilePath.raw());
3261 else
3262 setError(rc,
3263 tr("Could not find a CD/DVD image with location '%ls' in the media registry ('%s')"),
3264 aLocation,
3265 m->strSettingsFilePath.raw());
3266 }
3267
3268 return rc;
3269}
3270
3271/**
3272 * Searches for a Medium object with the given ID or location in the
3273 * collection of registered DVD images. If both ID and file path are specified,
3274 * the first object that matches either of them (not necessarily both) is
3275 * returned.
3276 *
3277 * @param aId ID of the DVD image (unused when NULL).
3278 * @param aLocation Full path to the image file (unused when NULL).
3279 * @param aSetError If @c true, the appropriate error info is set in case when
3280 * the image is not found.
3281 * @param aImage Where to store the found image object (can be NULL).
3282 *
3283 * @return S_OK when found or E_INVALIDARG when not found.
3284 *
3285 * @note Locks the media tree for reading.
3286 */
3287HRESULT VirtualBox::findFloppyImage(const Guid *aId,
3288 CBSTR aLocation,
3289 bool aSetError,
3290 ComObjPtr<Medium> *aImage /* = NULL */)
3291{
3292 AssertReturn(aId || aLocation, E_INVALIDARG);
3293
3294 Utf8Str location;
3295
3296 if (aLocation != NULL)
3297 {
3298 int vrc = calculateFullPath(Utf8Str(aLocation), location);
3299 if (RT_FAILURE(vrc))
3300 return setError(VBOX_E_FILE_ERROR,
3301 tr("Invalid image file location '%ls' (%Rrc)"),
3302 aLocation, vrc);
3303 }
3304
3305 AutoReadLock alock(m->allFloppyImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3306
3307 bool found = false;
3308
3309 for (MediaList::const_iterator it = m->allFloppyImages.begin();
3310 it != m->allFloppyImages.end();
3311 ++ it)
3312 {
3313 /* no AutoCaller, registered image life time is bound to this */
3314 AutoReadLock imageLock(*it COMMA_LOCKVAL_SRC_POS);
3315
3316 found = (aId && (*it)->getId() == *aId) ||
3317 (aLocation != NULL &&
3318 RTPathCompare(location.c_str(),
3319 (*it)->getLocationFull().c_str()
3320 ) == 0);
3321 if (found)
3322 {
3323 if (aImage)
3324 *aImage = *it;
3325 break;
3326 }
3327 }
3328
3329 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
3330
3331 if (aSetError && !found)
3332 {
3333 if (aId)
3334 setError(rc,
3335 tr("Could not find a floppy image with UUID {%RTuuid} in the media registry ('%s')"),
3336 aId->raw(),
3337 m->strSettingsFilePath.raw());
3338 else
3339 setError(rc,
3340 tr("Could not find a floppy image with location '%ls' in the media registry ('%s')"),
3341 aLocation,
3342 m->strSettingsFilePath.raw());
3343 }
3344
3345 return rc;
3346}
3347
3348HRESULT VirtualBox::findGuestOSType(const Bstr &bstrOSType,
3349 GuestOSType*& pGuestOSType)
3350{
3351 /* Look for a GuestOSType object */
3352 AssertMsg(m->allGuestOSTypes.size() != 0,
3353 ("Guest OS types array must be filled"));
3354
3355 if (bstrOSType.isEmpty())
3356 {
3357 pGuestOSType = NULL;
3358 return S_OK;
3359 }
3360
3361 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3362 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
3363 it != m->allGuestOSTypes.end();
3364 ++it)
3365 {
3366 if ((*it)->id() == bstrOSType)
3367 {
3368 pGuestOSType = *it;
3369 return S_OK;
3370 }
3371 }
3372
3373 return setError(VBOX_E_OBJECT_NOT_FOUND,
3374 tr("Guest OS type '%ls' is invalid"),
3375 bstrOSType.raw());
3376}
3377
3378const ComObjPtr<Host>& VirtualBox::host() const
3379{
3380 return m->pHost;
3381}
3382
3383const ComObjPtr<SystemProperties>& VirtualBox::systemProperties() const
3384{
3385 return m->pSystemProperties;
3386}
3387
3388#ifdef VBOX_WITH_RESOURCE_USAGE_API
3389const ComObjPtr<PerformanceCollector>& VirtualBox::performanceCollector() const
3390{
3391 return m->pPerformanceCollector;
3392}
3393#endif /* VBOX_WITH_RESOURCE_USAGE_API */
3394
3395/**
3396 * Returns the default machine folder from the system properties
3397 * with proper locking.
3398 * @return
3399 */
3400const Utf8Str& VirtualBox::getDefaultMachineFolder() const
3401{
3402 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
3403 return m->pSystemProperties->m_strDefaultMachineFolder;
3404}
3405
3406/**
3407 * Returns the default hard disk folder from the system properties
3408 * with proper locking.
3409 * @return
3410 */
3411const Utf8Str& VirtualBox::getDefaultHardDiskFolder() const
3412{
3413 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
3414 return m->pSystemProperties->m_strDefaultHardDiskFolder;
3415}
3416
3417/**
3418 * Returns the default hard disk format from the system properties
3419 * with proper locking.
3420 * @return
3421 */
3422const Utf8Str& VirtualBox::getDefaultHardDiskFormat() const
3423{
3424 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
3425 return m->pSystemProperties->m_strDefaultHardDiskFormat;
3426}
3427
3428const Utf8Str& VirtualBox::homeDir() const
3429{
3430 return m->strHomeDir;
3431}
3432
3433/**
3434 * Calculates the absolute path of the given path taking the VirtualBox home
3435 * directory as the current directory.
3436 *
3437 * @param aPath Path to calculate the absolute path for.
3438 * @param aResult Where to put the result (used only on success, can be the
3439 * same Utf8Str instance as passed in @a aPath).
3440 * @return IPRT result.
3441 *
3442 * @note Doesn't lock any object.
3443 */
3444int VirtualBox::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
3445{
3446 AutoCaller autoCaller(this);
3447 AssertComRCReturn(autoCaller.rc(), VERR_GENERAL_FAILURE);
3448
3449 /* no need to lock since mHomeDir is const */
3450
3451 char folder[RTPATH_MAX];
3452 int vrc = RTPathAbsEx(m->strHomeDir.c_str(), strPath.c_str(), folder, sizeof(folder));
3453 if (RT_SUCCESS(vrc))
3454 aResult = folder;
3455
3456 return vrc;
3457}
3458
3459/**
3460 * Copies strSource to strTarget, making it relative to the VirtualBox config folder
3461 * if it is a subdirectory thereof, or simply copying it otherwise.
3462 *
3463 * @param strSource Path to evalue and copy.
3464 * @param strTarget Buffer to receive target path.
3465 */
3466void VirtualBox::copyPathRelativeToConfig(const Utf8Str &strSource,
3467 Utf8Str &strTarget)
3468{
3469 AutoCaller autoCaller(this);
3470 AssertComRCReturnVoid(autoCaller.rc());
3471
3472 // no need to lock since mHomeDir is const
3473
3474 // use strTarget as a temporary buffer to hold the machine settings dir
3475 strTarget = m->strHomeDir;
3476 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
3477 // is relative: then append what's left
3478 strTarget.append(strSource.c_str() + strTarget.length()); // include '/'
3479 else
3480 // is not relative: then overwrite
3481 strTarget = strSource;
3482}
3483
3484// private methods
3485/////////////////////////////////////////////////////////////////////////////
3486
3487/**
3488 * Checks if there is a hard disk, DVD or floppy image with the given ID or
3489 * location already registered.
3490 *
3491 * On return, sets @a aConflict to the string describing the conflicting medium,
3492 * or sets it to @c Null if no conflicting media is found. Returns S_OK in
3493 * either case. A failure is unexpected.
3494 *
3495 * @param aId UUID to check.
3496 * @param aLocation Location to check.
3497 * @param aConflict Where to return parameters of the conflicting medium.
3498 *
3499 * @note Locks the media tree and media objects for reading.
3500 */
3501HRESULT VirtualBox::checkMediaForConflicts2(const Guid &aId,
3502 const Utf8Str &aLocation,
3503 Utf8Str &aConflict)
3504{
3505 aConflict.setNull();
3506
3507 AssertReturn(!aId.isEmpty() && !aLocation.isEmpty(), E_FAIL);
3508
3509 AutoReadLock alock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3510
3511 HRESULT rc = S_OK;
3512
3513 Bstr bstrLocation(aLocation);
3514
3515 {
3516 ComObjPtr<Medium> hardDisk;
3517 rc = findHardDisk(&aId, bstrLocation, false /* aSetError */, &hardDisk);
3518 if (SUCCEEDED(rc))
3519 {
3520 /* Note: no AutoCaller since bound to this */
3521 AutoReadLock mediaLock(hardDisk COMMA_LOCKVAL_SRC_POS);
3522 aConflict = Utf8StrFmt(tr("hard disk '%s' with UUID {%RTuuid}"),
3523 hardDisk->getLocationFull().raw(),
3524 hardDisk->getId().raw());
3525 return S_OK;
3526 }
3527 }
3528
3529 {
3530 ComObjPtr<Medium> image;
3531 rc = findDVDImage(&aId, bstrLocation, false /* aSetError */, &image);
3532 if (SUCCEEDED(rc))
3533 {
3534 /* Note: no AutoCaller since bound to this */
3535 AutoReadLock mediaLock(image COMMA_LOCKVAL_SRC_POS);
3536 aConflict = Utf8StrFmt(tr("CD/DVD image '%s' with UUID {%RTuuid}"),
3537 image->getLocationFull().raw(),
3538 image->getId().raw());
3539 return S_OK;
3540 }
3541 }
3542
3543 {
3544 ComObjPtr<Medium> image;
3545 rc = findFloppyImage(&aId, bstrLocation, false /* aSetError */, &image);
3546 if (SUCCEEDED(rc))
3547 {
3548 /* Note: no AutoCaller since bound to this */
3549 AutoReadLock mediaLock(image COMMA_LOCKVAL_SRC_POS);
3550 aConflict = Utf8StrFmt(tr("floppy image '%s' with UUID {%RTuuid}"),
3551 image->getLocationFull().raw(),
3552 image->getId().raw());
3553 return S_OK;
3554 }
3555 }
3556
3557 return S_OK;
3558}
3559
3560/**
3561 * Called from Machine::prepareSaveSettings() when it has detected
3562 * that a machine has been renamed. Such renames will require
3563 * updating the global media registry during the
3564 * VirtualBox::saveSettings() that follows later.
3565*
3566 * When a machine is renamed, there may well be media (in particular,
3567 * diff images for snapshots) in the global registry that will need
3568 * to have their paths updated. Before 3.2, Machine::saveSettings
3569 * used to call VirtualBox::saveSettings implicitly, which was both
3570 * unintuitive and caused locking order problems. Now, we remeber
3571 * such pending name changes with this method so that
3572 * VirtualBox::saveSettings() can process them properly.
3573 */
3574void VirtualBox::rememberMachineNameChangeForMedia(const Utf8Str &strOldConfigDir,
3575 const Utf8Str &strNewConfigDir)
3576{
3577 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3578
3579 Data::PendingMachineRename pmr;
3580 pmr.strConfigDirOld = strOldConfigDir;
3581 pmr.strConfigDirNew = strNewConfigDir;
3582 m->llPendingMachineRenames.push_back(pmr);
3583}
3584
3585/**
3586 * Helper function which actually writes out VirtualBox.xml, the main configuration file.
3587 * Gets called from the public VirtualBox::SaveSettings() as well as from various other
3588 * places internally when settings need saving.
3589 *
3590 * @note Caller must have locked the VirtualBox object for writing and must not hold any
3591 * other locks since this locks all kinds of member objects and trees temporarily,
3592 * which could cause conflicts.
3593 */
3594HRESULT VirtualBox::saveSettings()
3595{
3596 AutoCaller autoCaller(this);
3597 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3598
3599 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
3600 AssertReturn(!m->strSettingsFilePath.isEmpty(), E_FAIL);
3601
3602 HRESULT rc = S_OK;
3603
3604 try
3605 {
3606 // lock the lists while we're here
3607 AutoReadLock machinesLock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3608
3609 // machines
3610 settings::MachinesRegistry machinesTemp;
3611 {
3612 for (MachinesOList::iterator it = m->allMachines.begin();
3613 it != m->allMachines.end();
3614 ++it)
3615 {
3616 Machine *pMachine = *it;
3617 // save actual machine registry entry
3618 settings::MachineRegistryEntry mre;
3619 rc = pMachine->saveRegistryEntry(mre);
3620 machinesTemp.push_back(mre);
3621 }
3622 }
3623
3624 // lock all media for the following; use a write lock because we're
3625 // modifying the PendingMachineRenamesList, which is protected by this
3626 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3627
3628 // if a machine was renamed, then we'll need to refresh media paths
3629 if (m->llPendingMachineRenames.size())
3630 {
3631 // make a single list from the three media lists so we don't need three loops
3632 MediaList llAllMedia;
3633 // with hard disks, we must use the map, not the list, because the list only has base images
3634 for (HardDiskMap::iterator it = m->mapHardDisks.begin(); it != m->mapHardDisks.end(); ++it)
3635 llAllMedia.push_back(it->second);
3636 for (MediaList::iterator it = m->allDVDImages.begin(); it != m->allDVDImages.end(); ++it)
3637 llAllMedia.push_back(*it);
3638 for (MediaList::iterator it = m->allFloppyImages.begin(); it != m->allFloppyImages.end(); ++it)
3639 llAllMedia.push_back(*it);
3640
3641 for (MediaList::iterator it = llAllMedia.begin();
3642 it != llAllMedia.end();
3643 ++it)
3644 {
3645 Medium *pMedium = *it;
3646 for (Data::PendingMachineRenamesList::iterator it2 = m->llPendingMachineRenames.begin();
3647 it2 != m->llPendingMachineRenames.end();
3648 ++it2)
3649 {
3650 const Data::PendingMachineRename &pmr = *it2;
3651 const char *pcszOld = pmr.strConfigDirOld.c_str();
3652 const char *pcszNew = pmr.strConfigDirNew.c_str();
3653 pMedium->updatePath(pcszOld, pcszNew);
3654 }
3655 }
3656 // done, don't do it again until we have more machine renames
3657 m->llPendingMachineRenames.clear();
3658 }
3659
3660 // hard disks
3661 settings::MediaList hardDisksTemp;
3662 for (MediaList::const_iterator it = m->allHardDisks.begin();
3663 it != m->allHardDisks.end();
3664 ++it)
3665 {
3666 settings::Medium med;
3667 rc = (*it)->saveSettings(med); // this recurses into its children
3668 if (FAILED(rc)) throw rc;
3669 hardDisksTemp.push_back(med);
3670 }
3671
3672 /* CD/DVD images */
3673 settings::MediaList dvdsTemp;
3674 for (MediaList::const_iterator it = m->allDVDImages.begin();
3675 it != m->allDVDImages.end();
3676 ++it)
3677 {
3678 settings::Medium med;
3679 rc = (*it)->saveSettings(med);
3680 if (FAILED(rc)) throw rc;
3681 dvdsTemp.push_back(med);
3682 }
3683
3684 /* floppy images */
3685 settings::MediaList floppiesTemp;
3686 for (MediaList::const_iterator it = m->allFloppyImages.begin();
3687 it != m->allFloppyImages.end();
3688 ++it)
3689 {
3690 settings::Medium med;
3691 rc = (*it)->saveSettings(med);
3692 if (FAILED(rc)) throw rc;
3693 floppiesTemp.push_back(med);
3694 }
3695
3696 settings::DHCPServersList dhcpServersTemp;
3697 {
3698 AutoReadLock dhcpLock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3699 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
3700 it != m->allDHCPServers.end();
3701 ++it)
3702 {
3703 settings::DHCPServer d;
3704 rc = (*it)->saveSettings(d);
3705 if (FAILED(rc)) throw rc;
3706 dhcpServersTemp.push_back(d);
3707 }
3708 }
3709
3710 /* now copy the temp data to the config file under the VirtualBox lock */
3711 m->pMainConfigFile->llMachines = machinesTemp;
3712 m->pMainConfigFile->llHardDisks = hardDisksTemp;
3713 m->pMainConfigFile->llDvdImages = dvdsTemp;
3714 m->pMainConfigFile->llFloppyImages = floppiesTemp;
3715 m->pMainConfigFile->llDhcpServers = dhcpServersTemp;
3716
3717 // leave extra data alone, it's still in the config file
3718
3719 /* host data (USB filters) */
3720 rc = m->pHost->saveSettings(m->pMainConfigFile->host);
3721 if (FAILED(rc)) throw rc;
3722
3723 rc = m->pSystemProperties->saveSettings(m->pMainConfigFile->systemProperties);
3724 if (FAILED(rc)) throw rc;
3725
3726 // and write out the XML, still under the lock
3727 m->pMainConfigFile->write(m->strSettingsFilePath);
3728 }
3729 catch (HRESULT err)
3730 {
3731 /* we assume that error info is set by the thrower */
3732 rc = err;
3733 }
3734 catch (...)
3735 {
3736 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
3737 }
3738
3739 return rc;
3740}
3741
3742/**
3743 * Helper to register the machine.
3744 *
3745 * When called during VirtualBox startup, adds the given machine to the
3746 * collection of registered machines. Otherwise tries to mark the machine
3747 * as registered, and, if succeeded, adds it to the collection and
3748 * saves global settings.
3749 *
3750 * @note The caller must have added itself as a caller of the @a aMachine
3751 * object if calls this method not on VirtualBox startup.
3752 *
3753 * @param aMachine machine to register
3754 *
3755 * @note Locks objects!
3756 */
3757HRESULT VirtualBox::registerMachine(Machine *aMachine)
3758{
3759 ComAssertRet(aMachine, E_INVALIDARG);
3760
3761 AutoCaller autoCaller(this);
3762 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3763
3764 HRESULT rc = S_OK;
3765
3766 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3767
3768 {
3769 ComObjPtr<Machine> pMachine;
3770 rc = findMachine(aMachine->getId(), false /* aDoSetError */, &pMachine);
3771 if (SUCCEEDED(rc))
3772 {
3773 /* sanity */
3774 AutoLimitedCaller machCaller(pMachine);
3775 AssertComRC(machCaller.rc());
3776
3777 return setError(E_INVALIDARG,
3778 tr("Registered machine with UUID {%RTuuid} ('%ls') already exists"),
3779 aMachine->getId().raw(),
3780 pMachine->getSettingsFileFull().raw());
3781 }
3782
3783 ComAssertRet(rc == VBOX_E_OBJECT_NOT_FOUND, rc);
3784 rc = S_OK;
3785 }
3786
3787 if (autoCaller.state() != InInit)
3788 {
3789 rc = aMachine->trySetRegistered(TRUE);
3790 if (FAILED(rc)) return rc;
3791 }
3792
3793 /* add to the collection of registered machines */
3794 m->allMachines.addChild(aMachine);
3795
3796 if (autoCaller.state() != InInit)
3797 rc = saveSettings();
3798
3799 return rc;
3800}
3801
3802/**
3803 * Remembers the given hard disk by storing it in the hard disk registry.
3804 *
3805 * @param aHardDisk Hard disk object to remember.
3806 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3807 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3808 *
3809 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3810 */
3811HRESULT VirtualBox::registerHardDisk(Medium *aHardDisk,
3812 bool *pfNeedsSaveSettings)
3813{
3814 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3815
3816 AutoCaller autoCaller(this);
3817 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3818
3819 AutoCaller hardDiskCaller(aHardDisk);
3820 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3821
3822 // caller must hold the media tree write lock
3823 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3824
3825 Guid id;
3826 Utf8Str strLocationFull;
3827 ComObjPtr<Medium> pParent;
3828 {
3829 AutoReadLock hardDiskLock(aHardDisk COMMA_LOCKVAL_SRC_POS);
3830 id = aHardDisk->getId();
3831 strLocationFull = aHardDisk->getLocationFull();
3832 pParent = aHardDisk->getParent();
3833 }
3834
3835 HRESULT rc;
3836
3837 Utf8Str strConflict;
3838 rc = checkMediaForConflicts2(id,
3839 strLocationFull,
3840 strConflict);
3841 if (FAILED(rc)) return rc;
3842
3843 if (strConflict.length())
3844 return setError(E_INVALIDARG,
3845 tr("Cannot register the hard disk '%s' with UUID {%RTuuid} because a %s already exists in the media registry ('%s')"),
3846 strLocationFull.raw(),
3847 id.raw(),
3848 strConflict.raw(),
3849 m->strSettingsFilePath.raw());
3850
3851 // store base (root) hard disks in the list
3852 if (pParent.isNull())
3853 m->allHardDisks.getList().push_back(aHardDisk);
3854 // access the list directly because we already locked the list above
3855
3856 // store all hard disks (even differencing images) in the map
3857 m->mapHardDisks[id] = aHardDisk;
3858
3859 if (pfNeedsSaveSettings)
3860 *pfNeedsSaveSettings = true;
3861
3862 return rc;
3863}
3864
3865/**
3866 * Removes the given hard disk from the hard disk registry.
3867 *
3868 * @param aHardDisk Hard disk object to remove.
3869 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3870 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3871 *
3872 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3873 */
3874HRESULT VirtualBox::unregisterHardDisk(Medium *aHardDisk,
3875 bool *pfNeedsSaveSettings)
3876{
3877 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3878
3879 AutoCaller autoCaller(this);
3880 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3881
3882 AutoCaller hardDiskCaller(aHardDisk);
3883 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3884
3885 // caller must hold the media tree write lock
3886 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3887
3888 Guid id;
3889 ComObjPtr<Medium> pParent;
3890 {
3891 AutoReadLock hardDiskLock(aHardDisk COMMA_LOCKVAL_SRC_POS);
3892 id = aHardDisk->getId();
3893 pParent = aHardDisk->getParent();
3894 }
3895
3896 // remove base (root) hard disks from the list
3897 if (pParent.isNull())
3898 m->allHardDisks.getList().remove(aHardDisk);
3899 // access the list directly because caller must have locked the list
3900
3901 // remove all hard disks (even differencing images) from map
3902 size_t cnt = m->mapHardDisks.erase(id);
3903 Assert(cnt == 1);
3904 NOREF(cnt);
3905
3906 if (pfNeedsSaveSettings)
3907 *pfNeedsSaveSettings = true;
3908
3909 return S_OK;
3910}
3911
3912/**
3913 * Remembers the given image by storing it in the CD/DVD or floppy image registry.
3914 *
3915 * @param argImage Image object to remember.
3916 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3917 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3918 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3919 *
3920 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3921 */
3922HRESULT VirtualBox::registerImage(Medium *argImage,
3923 DeviceType_T argType,
3924 bool *pfNeedsSaveSettings)
3925{
3926 AssertReturn(argImage != NULL, E_INVALIDARG);
3927
3928 AutoCaller autoCaller(this);
3929 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3930
3931 AutoCaller imageCaller(argImage);
3932 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3933
3934 // caller must hold the media tree write lock
3935 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3936
3937 Guid id;
3938 Utf8Str strLocationFull;
3939 ComObjPtr<Medium> pParent;
3940 {
3941 AutoReadLock al(argImage COMMA_LOCKVAL_SRC_POS);
3942 id = argImage->getId();
3943 strLocationFull = argImage->getLocationFull();
3944 pParent = argImage->getParent();
3945 }
3946
3947 // work on DVDs or floppies list?
3948 ObjectsList<Medium> &all = (argType == DeviceType_DVD) ? m->allDVDImages : m->allFloppyImages;
3949
3950 HRESULT rc;
3951 // lock the images lists (list + map) while checking for conflicts
3952 AutoWriteLock al(all.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3953
3954 Utf8Str strConflict;
3955 rc = checkMediaForConflicts2(id,
3956 strLocationFull,
3957 strConflict);
3958 if (FAILED(rc)) return rc;
3959
3960 if (strConflict.length())
3961 return setError(VBOX_E_INVALID_OBJECT_STATE,
3962 tr("Cannot register the image '%s' with UUID {%RTuuid} because a %s already exists in the media registry ('%s')"),
3963 strLocationFull.raw(),
3964 id.raw(),
3965 strConflict.raw(),
3966 m->strSettingsFilePath.raw());
3967
3968 // add to the collection
3969 all.getList().push_back(argImage);
3970 // access the list directly because we already locked the list above
3971
3972 if (pfNeedsSaveSettings)
3973 *pfNeedsSaveSettings = true;
3974
3975 return rc;
3976}
3977
3978/**
3979 * Removes the given image from the CD/DVD or floppy image registry.
3980 *
3981 * @param argImage Image object to remove.
3982 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3983 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3984 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3985 *
3986 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3987 */
3988HRESULT VirtualBox::unregisterImage(Medium *argImage,
3989 DeviceType_T argType,
3990 bool *pfNeedsSaveSettings)
3991{
3992 AssertReturn(argImage != NULL, E_INVALIDARG);
3993
3994 AutoCaller autoCaller(this);
3995 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3996
3997 AutoCaller imageCaller(argImage);
3998 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3999
4000 // caller must hold the media tree write lock
4001 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
4002
4003 Guid id;
4004 ComObjPtr<Medium> pParent;
4005 {
4006 AutoReadLock al(argImage COMMA_LOCKVAL_SRC_POS);
4007 id = argImage->getId();
4008 pParent = argImage->getParent();
4009 }
4010
4011 // work on DVDs or floppies list?
4012 ObjectsList<Medium> &all = (argType == DeviceType_DVD) ? m->allDVDImages : m->allFloppyImages;
4013
4014 // access the list directly because the caller must have requested the lock
4015 all.getList().remove(argImage);
4016
4017 HRESULT rc = S_OK;
4018
4019 if (pfNeedsSaveSettings)
4020 *pfNeedsSaveSettings = true;
4021
4022 return rc;
4023}
4024
4025/**
4026 * Creates the path to the specified file according to the path information
4027 * present in the file name.
4028 *
4029 * Note that the given file name must contain the full path otherwise the
4030 * extracted relative path will be created based on the current working
4031 * directory which is normally unknown.
4032 *
4033 * @param aFileName Full file name which path needs to be created.
4034 *
4035 * @return Extended error information on failure to create the path.
4036 */
4037/* static */
4038HRESULT VirtualBox::ensureFilePathExists(const Utf8Str &strFileName)
4039{
4040 Utf8Str strDir(strFileName);
4041 strDir.stripFilename();
4042 if (!RTDirExists(strDir.c_str()))
4043 {
4044 int vrc = RTDirCreateFullPath(strDir.c_str(), 0777);
4045 if (RT_FAILURE(vrc))
4046 return setErrorStatic(E_FAIL,
4047 Utf8StrFmt(tr("Could not create the directory '%s' (%Rrc)"),
4048 strDir.c_str(),
4049 vrc));
4050 }
4051
4052 return S_OK;
4053}
4054
4055/**
4056 * Handles unexpected exceptions by turning them into COM errors in release
4057 * builds or by hitting a breakpoint in the release builds.
4058 *
4059 * Usage pattern:
4060 * @code
4061 try
4062 {
4063 // ...
4064 }
4065 catch (LaLalA)
4066 {
4067 // ...
4068 }
4069 catch (...)
4070 {
4071 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
4072 }
4073 * @endcode
4074 *
4075 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
4076 */
4077/* static */
4078HRESULT VirtualBox::handleUnexpectedExceptions(RT_SRC_POS_DECL)
4079{
4080 try
4081 {
4082 /* re-throw the current exception */
4083 throw;
4084 }
4085 catch (const xml::Error &err)
4086 {
4087 return setErrorStatic(E_FAIL,
4088 Utf8StrFmt(tr("%s.\n%s[%d] (%s)"),
4089 err.what(),
4090 pszFile, iLine, pszFunction).c_str());
4091 }
4092 catch (const std::exception &err)
4093 {
4094 return setErrorStatic(E_FAIL,
4095 Utf8StrFmt(tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
4096 err.what(), typeid(err).name(),
4097 pszFile, iLine, pszFunction).c_str());
4098 }
4099 catch (...)
4100 {
4101 return setErrorStatic(E_FAIL,
4102 Utf8StrFmt(tr("Unknown exception\n%s[%d] (%s)"),
4103 pszFile, iLine, pszFunction).c_str());
4104 }
4105
4106 /* should not get here */
4107 AssertFailed();
4108 return E_FAIL;
4109}
4110
4111const Utf8Str& VirtualBox::settingsFilePath()
4112{
4113 return m->strSettingsFilePath;
4114}
4115
4116/**
4117 * Returns the lock handle which protects the media trees (hard disks,
4118 * DVDs, floppies). As opposed to version 3.1 and earlier, these lists
4119 * are no longer protected by the VirtualBox lock, but by this more
4120 * specialized lock. Mind the locking order: always request this lock
4121 * after the VirtualBox object lock but before the locks of the media
4122 * objects contained in these lists. See AutoLock.h.
4123 */
4124RWLockHandle& VirtualBox::getMediaTreeLockHandle()
4125{
4126 return m->lockMedia;
4127}
4128
4129/**
4130 * Thread function that watches the termination of all client processes
4131 * that have opened sessions using IVirtualBox::OpenSession()
4132 */
4133// static
4134DECLCALLBACK(int) VirtualBox::ClientWatcher(RTTHREAD /* thread */, void *pvUser)
4135{
4136 LogFlowFuncEnter();
4137
4138 VirtualBox *that = (VirtualBox*)pvUser;
4139 Assert(that);
4140
4141 typedef std::vector< ComObjPtr<Machine> > MachineVector;
4142 typedef std::vector< ComObjPtr<SessionMachine> > SessionMachineVector;
4143
4144 SessionMachineVector machines;
4145 MachineVector spawnedMachines;
4146
4147 size_t cnt = 0;
4148 size_t cntSpawned = 0;
4149
4150#if defined(RT_OS_WINDOWS)
4151
4152 HRESULT hrc = CoInitializeEx(NULL,
4153 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
4154 COINIT_SPEED_OVER_MEMORY);
4155 AssertComRC(hrc);
4156
4157 /// @todo (dmik) processes reaping!
4158
4159 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
4160 handles[0] = that->m->updateReq;
4161
4162 do
4163 {
4164 AutoCaller autoCaller(that);
4165 /* VirtualBox has been early uninitialized, terminate */
4166 if (!autoCaller.isOk())
4167 break;
4168
4169 do
4170 {
4171 /* release the caller to let uninit() ever proceed */
4172 autoCaller.release();
4173
4174 DWORD rc = ::WaitForMultipleObjects((DWORD)(1 + cnt + cntSpawned),
4175 handles,
4176 FALSE,
4177 INFINITE);
4178
4179 /* Restore the caller before using VirtualBox. If it fails, this
4180 * means VirtualBox is being uninitialized and we must terminate. */
4181 autoCaller.add();
4182 if (!autoCaller.isOk())
4183 break;
4184
4185 bool update = false;
4186
4187 if (rc == WAIT_OBJECT_0)
4188 {
4189 /* update event is signaled */
4190 update = true;
4191 }
4192 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
4193 {
4194 /* machine mutex is released */
4195 (machines[rc - WAIT_OBJECT_0 - 1])->checkForDeath();
4196 update = true;
4197 }
4198 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
4199 {
4200 /* machine mutex is abandoned due to client process termination */
4201 (machines[rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
4202 update = true;
4203 }
4204 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
4205 {
4206 /* spawned VM process has terminated (normally or abnormally) */
4207 (spawnedMachines[rc - WAIT_OBJECT_0 - cnt - 1])->
4208 checkForSpawnFailure();
4209 update = true;
4210 }
4211
4212 if (update)
4213 {
4214 /* close old process handles */
4215 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i)
4216 CloseHandle(handles[i]);
4217
4218 // lock the machines list for reading
4219 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4220
4221 /* obtain a new set of opened machines */
4222 cnt = 0;
4223 machines.clear();
4224
4225 for (MachinesOList::iterator it = that->m->allMachines.begin();
4226 it != that->m->allMachines.end();
4227 ++it)
4228 {
4229 /// @todo handle situations with more than 64 objects
4230 AssertMsgBreak((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
4231 ("MAXIMUM_WAIT_OBJECTS reached"));
4232
4233 ComObjPtr<SessionMachine> sm;
4234 HANDLE ipcSem;
4235 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
4236 {
4237 machines.push_back(sm);
4238 handles[1 + cnt] = ipcSem;
4239 ++cnt;
4240 }
4241 }
4242
4243 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4244
4245 /* obtain a new set of spawned machines */
4246 cntSpawned = 0;
4247 spawnedMachines.clear();
4248
4249 for (MachinesOList::iterator it = that->m->allMachines.begin();
4250 it != that->m->allMachines.end();
4251 ++it)
4252 {
4253 /// @todo handle situations with more than 64 objects
4254 AssertMsgBreak((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
4255 ("MAXIMUM_WAIT_OBJECTS reached"));
4256
4257 RTPROCESS pid;
4258 if ((*it)->isSessionSpawning(&pid))
4259 {
4260 HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid);
4261 AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
4262 pid, GetLastError()));
4263 if (rc == 0)
4264 {
4265 spawnedMachines.push_back(*it);
4266 handles[1 + cnt + cntSpawned] = ph;
4267 ++cntSpawned;
4268 }
4269 }
4270 }
4271
4272 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4273
4274 // machines lock unwinds here
4275 }
4276 }
4277 while (true);
4278 }
4279 while (0);
4280
4281 /* close old process handles */
4282 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
4283 CloseHandle(handles[i]);
4284
4285 /* release sets of machines if any */
4286 machines.clear();
4287 spawnedMachines.clear();
4288
4289 ::CoUninitialize();
4290
4291#elif defined(RT_OS_OS2)
4292
4293 /// @todo (dmik) processes reaping!
4294
4295 /* according to PMREF, 64 is the maximum for the muxwait list */
4296 SEMRECORD handles[64];
4297
4298 HMUX muxSem = NULLHANDLE;
4299
4300 do
4301 {
4302 AutoCaller autoCaller(that);
4303 /* VirtualBox has been early uninitialized, terminate */
4304 if (!autoCaller.isOk())
4305 break;
4306
4307 do
4308 {
4309 /* release the caller to let uninit() ever proceed */
4310 autoCaller.release();
4311
4312 int vrc = RTSemEventWait(that->m->updateReq, 500);
4313
4314 /* Restore the caller before using VirtualBox. If it fails, this
4315 * means VirtualBox is being uninitialized and we must terminate. */
4316 autoCaller.add();
4317 if (!autoCaller.isOk())
4318 break;
4319
4320 bool update = false;
4321 bool updateSpawned = false;
4322
4323 if (RT_SUCCESS(vrc))
4324 {
4325 /* update event is signaled */
4326 update = true;
4327 updateSpawned = true;
4328 }
4329 else
4330 {
4331 AssertMsg(vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
4332 ("RTSemEventWait returned %Rrc\n", vrc));
4333
4334 /* are there any mutexes? */
4335 if (cnt > 0)
4336 {
4337 /* figure out what's going on with machines */
4338
4339 unsigned long semId = 0;
4340 APIRET arc = ::DosWaitMuxWaitSem(muxSem,
4341 SEM_IMMEDIATE_RETURN, &semId);
4342
4343 if (arc == NO_ERROR)
4344 {
4345 /* machine mutex is normally released */
4346 Assert(semId >= 0 && semId < cnt);
4347 if (semId >= 0 && semId < cnt)
4348 {
4349#ifdef DEBUG
4350 {
4351 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
4352 LogFlowFunc(("released mutex: machine='%ls'\n",
4353 machines[semId]->name().raw()));
4354 }
4355#endif
4356 machines[semId]->checkForDeath();
4357 }
4358 update = true;
4359 }
4360 else if (arc == ERROR_SEM_OWNER_DIED)
4361 {
4362 /* machine mutex is abandoned due to client process
4363 * termination; find which mutex is in the Owner Died
4364 * state */
4365 for (size_t i = 0; i < cnt; ++ i)
4366 {
4367 PID pid; TID tid;
4368 unsigned long reqCnt;
4369 arc = DosQueryMutexSem((HMTX)handles[i].hsemCur, &pid, &tid, &reqCnt);
4370 if (arc == ERROR_SEM_OWNER_DIED)
4371 {
4372 /* close the dead mutex as asked by PMREF */
4373 ::DosCloseMutexSem((HMTX)handles[i].hsemCur);
4374
4375 Assert(i >= 0 && i < cnt);
4376 if (i >= 0 && i < cnt)
4377 {
4378#ifdef DEBUG
4379 {
4380 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
4381 LogFlowFunc(("mutex owner dead: machine='%ls'\n",
4382 machines[i]->name().raw()));
4383 }
4384#endif
4385 machines[i]->checkForDeath();
4386 }
4387 }
4388 }
4389 update = true;
4390 }
4391 else
4392 AssertMsg(arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
4393 ("DosWaitMuxWaitSem returned %d\n", arc));
4394 }
4395
4396 /* are there any spawning sessions? */
4397 if (cntSpawned > 0)
4398 {
4399 for (size_t i = 0; i < cntSpawned; ++ i)
4400 updateSpawned |= (spawnedMachines[i])->
4401 checkForSpawnFailure();
4402 }
4403 }
4404
4405 if (update || updateSpawned)
4406 {
4407 AutoReadLock thatLock(that COMMA_LOCKVAL_SRC_POS);
4408
4409 if (update)
4410 {
4411 /* close the old muxsem */
4412 if (muxSem != NULLHANDLE)
4413 ::DosCloseMuxWaitSem(muxSem);
4414
4415 /* obtain a new set of opened machines */
4416 cnt = 0;
4417 machines.clear();
4418
4419 for (MachinesOList::iterator it = that->m->llMachines.begin();
4420 it != that->m->llMachines.end(); ++ it)
4421 {
4422 /// @todo handle situations with more than 64 objects
4423 AssertMsg(cnt <= 64 /* according to PMREF */,
4424 ("maximum of 64 mutex semaphores reached (%d)",
4425 cnt));
4426
4427 ComObjPtr<SessionMachine> sm;
4428 HMTX ipcSem;
4429 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
4430 {
4431 machines.push_back(sm);
4432 handles[cnt].hsemCur = (HSEM)ipcSem;
4433 handles[cnt].ulUser = cnt;
4434 ++ cnt;
4435 }
4436 }
4437
4438 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4439
4440 if (cnt > 0)
4441 {
4442 /* create a new muxsem */
4443 APIRET arc = ::DosCreateMuxWaitSem(NULL, &muxSem, cnt,
4444 handles,
4445 DCMW_WAIT_ANY);
4446 AssertMsg(arc == NO_ERROR,
4447 ("DosCreateMuxWaitSem returned %d\n", arc));
4448 NOREF(arc);
4449 }
4450 }
4451
4452 if (updateSpawned)
4453 {
4454 /* obtain a new set of spawned machines */
4455 spawnedMachines.clear();
4456
4457 for (MachinesOList::iterator it = that->m->llMachines.begin();
4458 it != that->m->llMachines.end(); ++ it)
4459 {
4460 if ((*it)->isSessionSpawning())
4461 spawnedMachines.push_back(*it);
4462 }
4463
4464 cntSpawned = spawnedMachines.size();
4465 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4466 }
4467 }
4468 }
4469 while (true);
4470 }
4471 while (0);
4472
4473 /* close the muxsem */
4474 if (muxSem != NULLHANDLE)
4475 ::DosCloseMuxWaitSem(muxSem);
4476
4477 /* release sets of machines if any */
4478 machines.clear();
4479 spawnedMachines.clear();
4480
4481#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
4482
4483 bool update = false;
4484 bool updateSpawned = false;
4485
4486 do
4487 {
4488 AutoCaller autoCaller(that);
4489 if (!autoCaller.isOk())
4490 break;
4491
4492 do
4493 {
4494 /* release the caller to let uninit() ever proceed */
4495 autoCaller.release();
4496
4497 int rc = RTSemEventWait(that->m->updateReq, 500);
4498
4499 /*
4500 * Restore the caller before using VirtualBox. If it fails, this
4501 * means VirtualBox is being uninitialized and we must terminate.
4502 */
4503 autoCaller.add();
4504 if (!autoCaller.isOk())
4505 break;
4506
4507 if (RT_SUCCESS(rc) || update || updateSpawned)
4508 {
4509 /* RT_SUCCESS(rc) means an update event is signaled */
4510
4511 // lock the machines list for reading
4512 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4513
4514 if (RT_SUCCESS(rc) || update)
4515 {
4516 /* obtain a new set of opened machines */
4517 machines.clear();
4518
4519 for (MachinesOList::iterator it = that->m->allMachines.begin();
4520 it != that->m->allMachines.end();
4521 ++it)
4522 {
4523 ComObjPtr<SessionMachine> sm;
4524 if ((*it)->isSessionOpenOrClosing(sm))
4525 machines.push_back(sm);
4526 }
4527
4528 cnt = machines.size();
4529 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4530 }
4531
4532 if (RT_SUCCESS(rc) || updateSpawned)
4533 {
4534 /* obtain a new set of spawned machines */
4535 spawnedMachines.clear();
4536
4537 for (MachinesOList::iterator it = that->m->allMachines.begin();
4538 it != that->m->allMachines.end();
4539 ++it)
4540 {
4541 if ((*it)->isSessionSpawning())
4542 spawnedMachines.push_back(*it);
4543 }
4544
4545 cntSpawned = spawnedMachines.size();
4546 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4547 }
4548
4549 // machines lock unwinds here
4550 }
4551
4552 update = false;
4553 for (size_t i = 0; i < cnt; ++ i)
4554 update |= (machines[i])->checkForDeath();
4555
4556 updateSpawned = false;
4557 for (size_t i = 0; i < cntSpawned; ++ i)
4558 updateSpawned |= (spawnedMachines[i])->checkForSpawnFailure();
4559
4560 /* reap child processes */
4561 {
4562 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
4563 if (that->m->llProcesses.size())
4564 {
4565 LogFlowFunc(("UPDATE: child process count = %d\n",
4566 that->m->llProcesses.size()));
4567 VirtualBox::Data::ProcessList::iterator it = that->m->llProcesses.begin();
4568 while (it != that->m->llProcesses.end())
4569 {
4570 RTPROCESS pid = *it;
4571 RTPROCSTATUS status;
4572 int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status);
4573 if (vrc == VINF_SUCCESS)
4574 {
4575 LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n",
4576 pid, pid, status.iStatus,
4577 status.enmReason));
4578 it = that->m->llProcesses.erase(it);
4579 }
4580 else
4581 {
4582 LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
4583 pid, pid, vrc));
4584 if (vrc != VERR_PROCESS_RUNNING)
4585 {
4586 /* remove the process if it is not already running */
4587 it = that->m->llProcesses.erase(it);
4588 }
4589 else
4590 ++ it;
4591 }
4592 }
4593 }
4594 }
4595 }
4596 while (true);
4597 }
4598 while (0);
4599
4600 /* release sets of machines if any */
4601 machines.clear();
4602 spawnedMachines.clear();
4603
4604#else
4605# error "Port me!"
4606#endif
4607
4608 LogFlowFuncLeave();
4609 return 0;
4610}
4611
4612/**
4613 * Thread function that handles custom events posted using #postEvent().
4614 */
4615// static
4616DECLCALLBACK(int) VirtualBox::AsyncEventHandler(RTTHREAD thread, void *pvUser)
4617{
4618 LogFlowFuncEnter();
4619
4620 AssertReturn(pvUser, VERR_INVALID_POINTER);
4621
4622 // create an event queue for the current thread
4623 EventQueue *eventQ = new EventQueue();
4624 AssertReturn(eventQ, VERR_NO_MEMORY);
4625
4626 // return the queue to the one who created this thread
4627 *(static_cast <EventQueue **>(pvUser)) = eventQ;
4628 // signal that we're ready
4629 RTThreadUserSignal(thread);
4630
4631 BOOL ok = TRUE;
4632 Event *event = NULL;
4633
4634 while ((ok = eventQ->waitForEvent(&event)) && event)
4635 eventQ->handleEvent(event);
4636
4637 AssertReturn(ok, VERR_GENERAL_FAILURE);
4638
4639 delete eventQ;
4640
4641 LogFlowFuncLeave();
4642
4643 return 0;
4644}
4645
4646
4647////////////////////////////////////////////////////////////////////////////////
4648
4649/**
4650 * Takes the current list of registered callbacks of the managed VirtualBox
4651 * instance, and calls #handleCallback() for every callback item from the
4652 * list, passing the item as an argument.
4653 *
4654 * @note Locks the managed VirtualBox object for reading but leaves the lock
4655 * before iterating over callbacks and calling their methods.
4656 */
4657void *VirtualBox::CallbackEvent::handler()
4658{
4659 if (!mVirtualBox)
4660 return NULL;
4661
4662 AutoCaller autoCaller(mVirtualBox);
4663 if (!autoCaller.isOk())
4664 {
4665 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",
4666 autoCaller.state()));
4667 /* We don't need mVirtualBox any more, so release it */
4668 mVirtualBox = NULL;
4669 return NULL;
4670 }
4671
4672 CallbackList callbacks;
4673#ifdef RT_OS_WINDOWS
4674 EventListenersList listeners;
4675#endif
4676 {
4677 /* Make a copy to release the lock before iterating */
4678 AutoReadLock alock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
4679 callbacks = mVirtualBox->m->llCallbacks;
4680#ifdef RT_OS_WINDOWS
4681 IUnknown** pp;
4682 for (pp = mVirtualBox->m_vec.begin(); pp < mVirtualBox->m_vec.end(); pp++)
4683 {
4684 listeners.Add(*pp);
4685 }
4686#endif
4687 }
4688
4689
4690#ifdef RT_OS_WINDOWS
4691 {
4692 ComEventDesc evDesc;
4693
4694 int nConnections = listeners.GetSize();
4695 /* Only prepare args if someone really needs them */
4696 if (nConnections)
4697 prepareComEventDesc(evDesc);
4698
4699 for (int i=0; i<nConnections; i++)
4700 {
4701 ComPtr<IUnknown> sp = listeners.GetAt(i);
4702 ComPtr<IVirtualBoxCallback> cbI;
4703 ComPtr<IDispatch> cbD;
4704
4705 cbI = sp;
4706 cbD = sp;
4707
4708 /**
4709 * Would be just handleCallback(cbI) in an ideal world, unfortunately our
4710 * consumers want to be invoked via IDispatch, thus going the hard way.
4711 */
4712 if (cbI != NULL && cbD != NULL)
4713 {
4714 CComVariant varResult;
4715 mVirtualBox->m->mComEvHelper.fire(cbD, evDesc, &varResult);
4716 // what we gonna do with the result?
4717 }
4718 }
4719 }
4720#endif
4721
4722 {
4723 VBoxEventDesc evDesc;
4724 prepareEventDesc(mVirtualBox->m->pEventSource, evDesc);
4725
4726 evDesc.fire(/* don't wait for delivery */0);
4727 }
4728
4729 for (CallbackList::const_iterator it = callbacks.begin();
4730 it != callbacks.end();
4731 ++it)
4732 {
4733 if (it->isWanted(mWhat))
4734 {
4735 HRESULT hrc = handleCallback(it->ptrICb);
4736 if (hrc == VBOX_E_DONT_CALL_AGAIN)
4737 {
4738 /* Have to update the original. */
4739 AutoReadLock alock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
4740 CallbackList::iterator itOrg;
4741 itOrg = std::find(mVirtualBox->m->llCallbacks.begin(),
4742 mVirtualBox->m->llCallbacks.end(),
4743 CallbackList::value_type(it->ptrICb));
4744 if (itOrg != mVirtualBox->m->llCallbacks.end())
4745 itOrg->setDontCallAgain(mWhat);
4746 }
4747 else if (FAILED_DEAD_INTERFACE(hrc))
4748 mVirtualBox->removeDeadCallback(it->ptrICb);
4749 }
4750 }
4751
4752 mVirtualBox = NULL; /* Not needed any longer. Still make sense to do this? */
4753 return NULL;
4754}
4755
4756//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface(/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
4757//{
4758// return E_NOTIMPL;
4759//}
4760
4761STDMETHODIMP VirtualBox::CreateDHCPServer(IN_BSTR aName, IDHCPServer ** aServer)
4762{
4763 CheckComArgStrNotEmptyOrNull(aName);
4764 CheckComArgNotNull(aServer);
4765
4766 AutoCaller autoCaller(this);
4767 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4768
4769 ComObjPtr<DHCPServer> dhcpServer;
4770 dhcpServer.createObject();
4771 HRESULT rc = dhcpServer->init(this, aName);
4772 if (FAILED(rc)) return rc;
4773
4774 rc = registerDHCPServer(dhcpServer, true);
4775 if (FAILED(rc)) return rc;
4776
4777 dhcpServer.queryInterfaceTo(aServer);
4778
4779 return rc;
4780}
4781
4782STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName(IN_BSTR aName, IDHCPServer ** aServer)
4783{
4784 CheckComArgStrNotEmptyOrNull(aName);
4785 CheckComArgNotNull(aServer);
4786
4787 AutoCaller autoCaller(this);
4788 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4789
4790 HRESULT rc;
4791 Bstr bstr;
4792 ComPtr<DHCPServer> found;
4793
4794 AutoReadLock alock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4795
4796 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
4797 it != m->allDHCPServers.end();
4798 ++it)
4799 {
4800 rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());
4801 if (FAILED(rc)) throw rc;
4802
4803 if (bstr == aName)
4804 {
4805 found = *it;
4806 break;
4807 }
4808 }
4809
4810 if (!found)
4811 return E_INVALIDARG;
4812
4813 return found.queryInterfaceTo(aServer);
4814}
4815
4816STDMETHODIMP VirtualBox::RemoveDHCPServer(IDHCPServer * aServer)
4817{
4818 CheckComArgNotNull(aServer);
4819
4820 AutoCaller autoCaller(this);
4821 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4822
4823 HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);
4824
4825 return rc;
4826}
4827
4828/**
4829 * Remembers the given dhcp server by storing it in the hard disk registry.
4830 *
4831 * @param aDHCPServer Dhcp Server object to remember.
4832 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4833 *
4834 * When @a aSaveRegistry is @c true, this operation may fail because of the
4835 * failed #saveSettings() method it calls. In this case, the dhcp server object
4836 * will not be remembered. It is therefore the responsibility of the caller to
4837 * call this method as the last step of some action that requires registration
4838 * in order to make sure that only fully functional dhcp server objects get
4839 * registered.
4840 *
4841 * @note Locks this object for writing and @a aDHCPServer for reading.
4842 */
4843HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,
4844 bool aSaveRegistry /*= true*/)
4845{
4846 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4847
4848 AutoCaller autoCaller(this);
4849 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4850
4851 AutoCaller dhcpServerCaller(aDHCPServer);
4852 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4853
4854 Bstr name;
4855 HRESULT rc;
4856 rc = aDHCPServer->COMGETTER(NetworkName)(name.asOutParam());
4857 if (FAILED(rc)) return rc;
4858
4859 ComPtr<IDHCPServer> existing;
4860 rc = FindDHCPServerByNetworkName(name, existing.asOutParam());
4861 if (SUCCEEDED(rc))
4862 return E_INVALIDARG;
4863
4864 rc = S_OK;
4865
4866 m->allDHCPServers.addChild(aDHCPServer);
4867
4868 if (aSaveRegistry)
4869 {
4870 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4871 rc = saveSettings();
4872 vboxLock.release();
4873
4874 if (FAILED(rc))
4875 unregisterDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4876 }
4877
4878 return rc;
4879}
4880
4881/**
4882 * Removes the given hard disk from the hard disk registry.
4883 *
4884 * @param aHardDisk Hard disk object to remove.
4885 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4886 *
4887 * When @a aSaveRegistry is @c true, this operation may fail because of the
4888 * failed #saveSettings() method it calls. In this case, the hard disk object
4889 * will NOT be removed from the registry when this method returns. It is
4890 * therefore the responsibility of the caller to call this method as the first
4891 * step of some action that requires unregistration, before calling uninit() on
4892 * @a aHardDisk.
4893 *
4894 * @note Locks this object for writing and @a aHardDisk for reading.
4895 */
4896HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,
4897 bool aSaveRegistry /*= true*/)
4898{
4899 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4900
4901 AutoCaller autoCaller(this);
4902 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4903
4904 AutoCaller dhcpServerCaller(aDHCPServer);
4905 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4906
4907 m->allDHCPServers.removeChild(aDHCPServer);
4908
4909 HRESULT rc = S_OK;
4910
4911 if (aSaveRegistry)
4912 {
4913 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4914 rc = saveSettings();
4915 vboxLock.release();
4916
4917 if (FAILED(rc))
4918 registerDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4919 }
4920
4921 return rc;
4922}
4923
4924/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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