VirtualBox

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

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

Main: generic Java glue works (including callbacks on Win)

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