VirtualBox

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

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

back out r63543, r63544 until windows build problems can be solved properly

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