VirtualBox

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

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

Main: generic events (disabled)

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