VirtualBox

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

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

Main: more events

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