3
* VirtualBox COM class implementation
7
* Copyright (C) 2006-2007 innotek GmbH
9
* This file is part of VirtualBox Open Source Edition (OSE), as
10
* available from http://www.virtualbox.org. This file is free software;
11
* you can redistribute it and/or modify it under the terms of the GNU
12
* General Public License as published by the Free Software Foundation,
13
* in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14
* distribution. VirtualBox OSE is distributed in the hope that it will
15
* be useful, but WITHOUT ANY WARRANTY of any kind.
18
#include "VirtualBoxImpl.h"
19
#include "MachineImpl.h"
20
#include "HardDiskImpl.h"
21
#include "DVDImageImpl.h"
22
#include "FloppyImageImpl.h"
23
#include "SharedFolderImpl.h"
24
#include "ProgressImpl.h"
26
#include "USBControllerImpl.h"
27
#include "SystemPropertiesImpl.h"
30
#include "GuestOSTypeImpl.h"
33
#include "win32/svchlp.h"
39
#include <iprt/path.h>
41
#include <iprt/file.h>
42
#include <iprt/string.h>
43
#include <iprt/uuid.h>
44
#include <iprt/thread.h>
45
#include <iprt/process.h>
46
#include <iprt/cpputils.h>
49
#include <VBox/param.h>
50
#include <VBox/VBoxHDD.h>
51
#include <VBox/VBoxHDD-new.h>
52
#include <VBox/ostypes.h>
53
#include <VBox/version.h>
55
#include <VBox/com/com.h>
59
#include <memory> // for auto_ptr
62
/////////////////////////////////////////////////////////////////////////////
64
#define VBOX_GLOBAL_SETTINGS_FILE "VirtualBox.xml"
67
/////////////////////////////////////////////////////////////////////////////
69
static const char DefaultGlobalConfig [] =
71
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" RTFILE_LINEFEED
72
"<!-- innotek VirtualBox Global Configuration -->" RTFILE_LINEFEED
73
"<VirtualBox xmlns=\"" VBOX_XML_NAMESPACE "\" "
74
"version=\"" VBOX_XML_VERSION "-" VBOX_XML_PLATFORM "\">" RTFILE_LINEFEED
75
" <Global>"RTFILE_LINEFEED
76
" <MachineRegistry/>"RTFILE_LINEFEED
77
" <DiskRegistry/>"RTFILE_LINEFEED
78
" <USBDeviceFilters/>"RTFILE_LINEFEED
79
" <SystemProperties/>"RTFILE_LINEFEED
80
" </Global>"RTFILE_LINEFEED
81
"</VirtualBox>"RTFILE_LINEFEED
85
Bstr VirtualBox::sVersion;
87
// constructor / destructor
88
/////////////////////////////////////////////////////////////////////////////
90
VirtualBox::VirtualBox()
91
: mAsyncEventThread (NIL_RTTHREAD)
95
VirtualBox::~VirtualBox() {}
97
HRESULT VirtualBox::FinalConstruct()
99
LogFlowThisFunc (("\n"));
104
void VirtualBox::FinalRelease()
106
LogFlowThisFunc (("\n"));
111
VirtualBox::Data::Data()
115
// public initializer/uninitializer for internal purposes only
116
/////////////////////////////////////////////////////////////////////////////
119
* Initializes the VirtualBox object.
121
* @return COM result code
123
HRESULT VirtualBox::init()
125
/* Enclose the state transition NotReady->InInit->Ready */
126
AutoInitSpan autoInitSpan (this);
127
AssertReturn (autoInitSpan.isOk(), E_UNEXPECTED);
129
LogFlow (("===========================================================\n"));
130
LogFlowThisFuncEnter();
132
if (sVersion.isNull())
133
sVersion = VBOX_VERSION_STRING;
134
LogFlowThisFunc (("Version: %ls\n", sVersion.raw()));
136
/* Get the VirtualBox home directory. */
138
char homeDir [RTPATH_MAX];
139
int vrc = com::GetVBoxUserHomeDirectory (homeDir, sizeof (homeDir));
140
if (VBOX_FAILURE (vrc))
141
return setError (E_FAIL,
142
tr ("Could not create the VirtualBox home directory '%s'"
146
unconst (mData.mHomeDir) = homeDir;
149
/* compose the global config file name (always full path) */
150
Utf8StrFmt vboxConfigFile ("%s%c%s", mData.mHomeDir.raw(),
151
RTPATH_DELIMITER, VBOX_GLOBAL_SETTINGS_FILE);
153
/* store the config file name */
154
unconst (mData.mCfgFile.mName) = vboxConfigFile;
156
/* lock the config file */
157
HRESULT rc = lockConfig();
160
if (!isConfigLocked())
163
* This means the config file not found. This is not fatal --
164
* we just create an empty one.
166
RTFILE handle = NIL_RTFILE;
167
int vrc = RTFileOpen (&handle, vboxConfigFile,
168
RTFILE_O_READWRITE | RTFILE_O_CREATE |
169
RTFILE_O_DENY_WRITE);
170
if (VBOX_SUCCESS (vrc))
171
vrc = RTFileWrite (handle,
172
(void *) DefaultGlobalConfig,
173
sizeof (DefaultGlobalConfig), NULL);
174
if (VBOX_FAILURE (vrc))
176
rc = setError (E_FAIL, tr ("Could not create the default settings file "
178
vboxConfigFile.raw(), vrc);
182
mData.mCfgFile.mHandle = handle;
183
/* we do not close the file to simulate lockConfig() */
188
/* initialize our Xerces XML subsystem */
191
int vrc = CFGLDRInitialize();
192
if (VBOX_FAILURE (vrc))
193
rc = setError (E_FAIL, tr ("Could not initialize the XML parser (%Vrc)"),
199
CFGHANDLE configLoader = NULL;
200
char *loaderError = NULL;
202
/* load the config file */
203
int vrc = CFGLDRLoad (&configLoader, vboxConfigFile, mData.mCfgFile.mHandle,
204
XmlSchemaNS, true, cfgLdrEntityResolver,
206
if (VBOX_SUCCESS (vrc))
208
CFGNODE global = NULL;
209
CFGLDRGetNode (configLoader, "VirtualBox/Global", 0, &global);
214
/* create the host object early, machines will need it */
215
unconst (mData.mHost).createObject();
216
rc = mData.mHost->init (this);
217
ComAssertComRCBreak (rc, rc = rc);
219
unconst (mData.mSystemProperties).createObject();
220
rc = mData.mSystemProperties->init (this);
221
ComAssertComRCBreak (rc, rc = rc);
223
rc = loadDisks (global);
224
CheckComRCBreakRC ((rc));
226
/* guest OS type objects, needed by the machines */
227
rc = registerGuestOSTypes();
228
ComAssertComRCBreak (rc, rc = rc);
231
rc = loadMachines (global);
232
CheckComRCBreakRC ((rc));
234
/* host data (USB filters) */
235
rc = mData.mHost->loadSettings (global);
236
CheckComRCBreakRC ((rc));
238
rc = mData.mSystemProperties->loadSettings (global);
239
CheckComRCBreakRC ((rc));
241
/* check hard disk consistency */
242
/// @todo (r=dmik) add IVirtualBox::cleanupHardDisks() instead or similar
243
// for (HardDiskList::const_iterator it = mData.mHardDisks.begin();
244
// it != mData.mHardDisks.end() && SUCCEEDED (rc);
247
// rc = (*it)->checkConsistency();
249
// CheckComRCBreakRC ((rc));
253
/// @todo (dmik) if successful, check for orphan (unused) diffs
254
// that might be left because of the server crash, and remove them.
256
CFGLDRReleaseNode (global);
257
CFGLDRFree(configLoader);
261
rc = setError (E_FAIL,
262
tr ("Could not load the settings file '%ls' (%Vrc)%s%s"),
263
mData.mCfgFile.mName.raw(), vrc,
264
loaderError ? ".\n" : "", loaderError ? loaderError : "");
268
RTMemTmpFree (loaderError);
273
/* start the client watcher thread */
274
#if defined(RT_OS_WINDOWS)
275
unconst (mWatcherData.mUpdateReq) = ::CreateEvent (NULL, FALSE, FALSE, NULL);
276
#elif defined(RT_OS_OS2)
277
RTSemEventCreate (&unconst (mWatcherData.mUpdateReq));
278
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
279
RTSemEventCreate (&unconst (mWatcherData.mUpdateReq));
283
int vrc = RTThreadCreate (&unconst (mWatcherData.mThread),
284
ClientWatcher, (void *) this,
285
0, RTTHREADTYPE_MAIN_WORKER,
286
RTTHREADFLAGS_WAITABLE, "Watcher");
288
if (VBOX_FAILURE (vrc))
292
if (SUCCEEDED (rc)) do
294
/* start the async event handler thread */
295
int vrc = RTThreadCreate (&unconst (mAsyncEventThread), AsyncEventHandler,
296
&unconst (mAsyncEventQ),
297
0, RTTHREADTYPE_MAIN_WORKER,
298
RTTHREADFLAGS_WAITABLE, "EventHandler");
299
ComAssertRCBreak (vrc, rc = E_FAIL);
301
/* wait until the thread sets mAsyncEventQ */
302
RTThreadUserWait (mAsyncEventThread, RT_INDEFINITE_WAIT);
303
ComAssertBreak (mAsyncEventQ, rc = E_FAIL);
307
/* Confirm a successful initialization when it's the case */
309
autoInitSpan.setSucceeded();
311
LogFlowThisFunc (("rc=%08X\n", rc));
312
LogFlowThisFuncLeave();
313
LogFlow (("===========================================================\n"));
317
void VirtualBox::uninit()
319
/* Enclose the state transition Ready->InUninit->NotReady */
320
AutoUninitSpan autoUninitSpan (this);
321
if (autoUninitSpan.uninitDone())
324
LogFlow (("===========================================================\n"));
325
LogFlowThisFuncEnter();
326
LogFlowThisFunc (("initFailed()=%d\n", autoUninitSpan.initFailed()));
328
/* tell all our child objects we've been uninitialized */
330
LogFlowThisFunc (("Uninitializing machines (%d)...\n", mData.mMachines.size()));
331
if (mData.mMachines.size())
333
MachineList::iterator it = mData.mMachines.begin();
334
while (it != mData.mMachines.end())
336
mData.mMachines.clear();
339
if (mData.mSystemProperties)
341
mData.mSystemProperties->uninit();
342
unconst (mData.mSystemProperties).setNull();
347
mData.mHost->uninit();
348
unconst (mData.mHost).setNull();
352
* Uninit all other children still referenced by clients
353
* (unregistered machines, hard disks, DVD/floppy images,
354
* server-side progress operations).
356
uninitDependentChildren();
358
mData.mFloppyImages.clear();
359
mData.mDVDImages.clear();
360
mData.mHardDisks.clear();
362
mData.mHardDiskMap.clear();
364
mData.mProgressOperations.clear();
366
mData.mGuestOSTypes.clear();
368
/* unlock the config file */
371
LogFlowThisFunc (("Releasing callbacks...\n"));
372
if (mData.mCallbacks.size())
374
/* release all callbacks */
375
LogWarningFunc (("%d unregistered callbacks!\n",
376
mData.mCallbacks.size()));
377
mData.mCallbacks.clear();
380
LogFlowThisFunc (("Terminating the async event handler...\n"));
381
if (mAsyncEventThread != NIL_RTTHREAD)
383
/* signal to exit the event loop */
384
if (mAsyncEventQ->postEvent (NULL))
387
* Wait for thread termination (only if we've successfully posted
390
int vrc = RTThreadWait (mAsyncEventThread, 60000, NULL);
391
if (VBOX_FAILURE (vrc))
392
LogWarningFunc (("RTThreadWait(%RTthrd) -> %Vrc\n",
393
mAsyncEventThread, vrc));
397
AssertMsgFailed (("postEvent(NULL) failed\n"));
398
RTThreadWait (mAsyncEventThread, 0, NULL);
401
unconst (mAsyncEventThread) = NIL_RTTHREAD;
402
unconst (mAsyncEventQ) = NULL;
405
LogFlowThisFunc (("Terminating the client watcher...\n"));
406
if (mWatcherData.mThread != NIL_RTTHREAD)
408
/* signal the client watcher thread */
409
updateClientWatcher();
410
/* wait for the termination */
411
RTThreadWait (mWatcherData.mThread, RT_INDEFINITE_WAIT, NULL);
412
unconst (mWatcherData.mThread) = NIL_RTTHREAD;
414
mWatcherData.mProcesses.clear();
415
#if defined(RT_OS_WINDOWS)
416
if (mWatcherData.mUpdateReq != NULL)
418
::CloseHandle (mWatcherData.mUpdateReq);
419
unconst (mWatcherData.mUpdateReq) = NULL;
421
#elif defined(RT_OS_OS2)
422
if (mWatcherData.mUpdateReq != NIL_RTSEMEVENT)
424
RTSemEventDestroy (mWatcherData.mUpdateReq);
425
unconst (mWatcherData.mUpdateReq) = NIL_RTSEMEVENT;
427
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
428
if (mWatcherData.mUpdateReq != NIL_RTSEMEVENT)
430
RTSemEventDestroy (mWatcherData.mUpdateReq);
431
unconst (mWatcherData.mUpdateReq) = NIL_RTSEMEVENT;
437
/* uninitialize the Xerces XML subsystem */
440
LogFlowThisFuncLeave();
441
LogFlow (("===========================================================\n"));
444
// IVirtualBox properties
445
/////////////////////////////////////////////////////////////////////////////
447
STDMETHODIMP VirtualBox::COMGETTER(Version) (BSTR *aVersion)
452
AutoCaller autoCaller (this);
453
CheckComRCReturnRC (autoCaller.rc());
455
sVersion.cloneTo (aVersion);
459
STDMETHODIMP VirtualBox::COMGETTER(HomeFolder) (BSTR *aHomeFolder)
464
AutoCaller autoCaller (this);
465
CheckComRCReturnRC (autoCaller.rc());
467
mData.mHomeDir.cloneTo (aHomeFolder);
471
STDMETHODIMP VirtualBox::COMGETTER(Host) (IHost **aHost)
476
AutoCaller autoCaller (this);
477
CheckComRCReturnRC (autoCaller.rc());
479
mData.mHost.queryInterfaceTo (aHost);
483
STDMETHODIMP VirtualBox::COMGETTER(SystemProperties) (ISystemProperties **aSystemProperties)
485
if (!aSystemProperties)
488
AutoCaller autoCaller (this);
489
CheckComRCReturnRC (autoCaller.rc());
491
mData.mSystemProperties.queryInterfaceTo (aSystemProperties);
495
/** @note Locks this object for reading. */
496
STDMETHODIMP VirtualBox::COMGETTER(Machines) (IMachineCollection **aMachines)
501
AutoCaller autoCaller (this);
502
CheckComRCReturnRC (autoCaller.rc());
504
ComObjPtr <MachineCollection> collection;
505
collection.createObject();
507
AutoReaderLock alock (this);
508
collection->init (mData.mMachines);
509
collection.queryInterfaceTo (aMachines);
514
/** @note Locks this object for reading. */
515
STDMETHODIMP VirtualBox::COMGETTER(HardDisks) (IHardDiskCollection **aHardDisks)
520
AutoCaller autoCaller (this);
521
CheckComRCReturnRC (autoCaller.rc());
523
ComObjPtr <HardDiskCollection> collection;
524
collection.createObject();
526
AutoReaderLock alock (this);
527
collection->init (mData.mHardDisks);
528
collection.queryInterfaceTo (aHardDisks);
533
/** @note Locks this object for reading. */
534
STDMETHODIMP VirtualBox::COMGETTER(DVDImages) (IDVDImageCollection **aDVDImages)
539
AutoCaller autoCaller (this);
540
CheckComRCReturnRC (autoCaller.rc());
542
ComObjPtr <DVDImageCollection> collection;
543
collection.createObject();
545
AutoReaderLock alock (this);
546
collection->init (mData.mDVDImages);
547
collection.queryInterfaceTo (aDVDImages);
552
/** @note Locks this object for reading. */
553
STDMETHODIMP VirtualBox::COMGETTER(FloppyImages) (IFloppyImageCollection **aFloppyImages)
558
AutoCaller autoCaller (this);
559
CheckComRCReturnRC (autoCaller.rc());
561
ComObjPtr <FloppyImageCollection> collection;
562
collection.createObject();
564
AutoReaderLock alock (this);
565
collection->init (mData.mFloppyImages);
566
collection.queryInterfaceTo (aFloppyImages);
571
/** @note Locks this object for reading. */
572
STDMETHODIMP VirtualBox::COMGETTER(ProgressOperations) (IProgressCollection **aOperations)
577
AutoCaller autoCaller (this);
578
CheckComRCReturnRC (autoCaller.rc());
580
ComObjPtr <ProgressCollection> collection;
581
collection.createObject();
583
AutoReaderLock alock (this);
584
collection->init (mData.mProgressOperations);
585
collection.queryInterfaceTo (aOperations);
590
/** @note Locks this object for reading. */
591
STDMETHODIMP VirtualBox::COMGETTER(GuestOSTypes) (IGuestOSTypeCollection **aGuestOSTypes)
596
AutoCaller autoCaller (this);
597
CheckComRCReturnRC (autoCaller.rc());
599
ComObjPtr <GuestOSTypeCollection> collection;
600
collection.createObject();
602
AutoReaderLock alock (this);
603
collection->init (mData.mGuestOSTypes);
604
collection.queryInterfaceTo (aGuestOSTypes);
610
VirtualBox::COMGETTER(SharedFolders) (ISharedFolderCollection **aSharedFolders)
615
AutoCaller autoCaller (this);
616
CheckComRCReturnRC (autoCaller.rc());
618
return setError (E_NOTIMPL, "Not yet implemented");
621
// IVirtualBox methods
622
/////////////////////////////////////////////////////////////////////////////
624
/** @note Locks mSystemProperties object for reading. */
625
STDMETHODIMP VirtualBox::CreateMachine (INPTR BSTR aBaseFolder,
629
LogFlowThisFuncEnter();
630
LogFlowThisFunc (("aBaseFolder='%ls', aName='%ls' aMachine={%p}\n",
631
aBaseFolder, aName, aMachine));
639
return setError (E_INVALIDARG,
640
tr ("Machine name cannot be empty"));
642
AutoCaller autoCaller (this);
643
CheckComRCReturnRC (autoCaller.rc());
645
/* Compose the settings file name using the following scheme:
647
* <base_folder>/<machine_name>/<machine_name>.xml
649
* If a non-null and non-empty base folder is specified, the default
650
* machine folder will be used as a base folder.
652
Bstr settingsFile = aBaseFolder;
653
if (settingsFile.isEmpty())
655
AutoReaderLock propsLock (systemProperties());
656
/* we use the non-full folder value below to keep the path relative */
657
settingsFile = systemProperties()->defaultMachineFolder();
659
settingsFile = Utf8StrFmt ("%ls%c%ls%c%ls.xml",
660
settingsFile.raw(), RTPATH_DELIMITER,
661
aName, RTPATH_DELIMITER, aName);
665
/* create a new object */
666
ComObjPtr <Machine> machine;
667
rc = machine.createObject();
670
/* initialize the machine object */
671
rc = machine->init (this, settingsFile, Machine::Init_New, aName);
674
/* set the return value */
675
rc = machine.queryInterfaceTo (aMachine);
680
LogFlowThisFunc (("rc=%08X\n", rc));
681
LogFlowThisFuncLeave();
686
STDMETHODIMP VirtualBox::CreateLegacyMachine (INPTR BSTR aSettingsFile,
690
/* null and empty strings are not allowed as path names */
691
if (!aSettingsFile || !(*aSettingsFile))
700
return setError (E_INVALIDARG,
701
tr ("Machine name cannot be empty"));
703
AutoCaller autoCaller (this);
704
CheckComRCReturnRC (autoCaller.rc());
708
Utf8Str settingsFile = aSettingsFile;
709
/* append the default extension if none */
710
if (!RTPathHaveExt (settingsFile))
711
settingsFile = Utf8StrFmt ("%s.xml", settingsFile.raw());
713
/* create a new object */
714
ComObjPtr<Machine> machine;
715
rc = machine.createObject();
718
/* initialize the machine object */
719
rc = machine->init (this, Bstr (settingsFile), Machine::Init_New,
720
aName, FALSE /* aNameSync */);
723
/* set the return value */
724
rc = machine.queryInterfaceTo (aMachine);
731
STDMETHODIMP VirtualBox::OpenMachine (INPTR BSTR aSettingsFile,
734
/* null and empty strings are not allowed as path names */
735
if (!aSettingsFile || !(*aSettingsFile))
741
AutoCaller autoCaller (this);
742
CheckComRCReturnRC (autoCaller.rc());
746
/* create a new object */
747
ComObjPtr<Machine> machine;
748
rc = machine.createObject();
751
/* initialize the machine object */
752
rc = machine->init (this, aSettingsFile, Machine::Init_Existing);
755
/* set the return value */
756
rc = machine.queryInterfaceTo (aMachine);
764
/** @note Locks objects! */
765
STDMETHODIMP VirtualBox::RegisterMachine (IMachine *aMachine)
770
AutoCaller autoCaller (this);
771
CheckComRCReturnRC (autoCaller.rc());
776
rc = aMachine->COMGETTER(Name) (name.asOutParam());
777
CheckComRCReturnRC (rc);
780
* we can safely cast child to Machine * here because only Machine
781
* implementations of IMachine can be among our children
783
Machine *machine = static_cast <Machine *> (getDependentChild (aMachine));
787
* this machine was not created by CreateMachine()
788
* or opened by OpenMachine() or loaded during startup
790
return setError (E_FAIL,
791
tr ("The machine named '%ls' is not created within this "
792
"VirtualBox instance"), name.raw());
796
* Machine::trySetRegistered() will commit and save machine settings, and
797
* will also call #registerMachine() on success.
799
rc = registerMachine (machine);
803
onMachineRegistered (machine->data()->mUuid, TRUE);
808
/** @note Locks objects! */
809
STDMETHODIMP VirtualBox::GetMachine (INPTR GUIDPARAM aId, IMachine **aMachine)
814
AutoCaller autoCaller (this);
815
CheckComRCReturnRC (autoCaller.rc());
817
ComObjPtr <Machine> machine;
818
HRESULT rc = findMachine (Guid (aId), true /* setError */, &machine);
820
/* the below will set *aMachine to NULL if machine is null */
821
machine.queryInterfaceTo (aMachine);
826
/** @note Locks this object for reading, then some machine objects for reading. */
827
STDMETHODIMP VirtualBox::FindMachine (INPTR BSTR aName, IMachine **aMachine)
829
LogFlowThisFuncEnter();
830
LogFlowThisFunc (("aName=\"%ls\", aMachine={%p}\n", aName, aMachine));
837
AutoCaller autoCaller (this);
838
CheckComRCReturnRC (autoCaller.rc());
840
/* start with not found */
841
ComObjPtr <Machine> machine;
842
MachineList machines;
844
/* take a copy for safe iteration outside the lock */
845
AutoReaderLock alock (this);
846
machines = mData.mMachines;
849
for (MachineList::iterator it = machines.begin();
850
!machine && it != machines.end();
853
AutoReaderLock machineLock (*it);
854
if ((*it)->userData()->mName == aName)
858
/* this will set (*machine) to NULL if machineObj is null */
859
machine.queryInterfaceTo (aMachine);
863
: setError (E_INVALIDARG,
864
tr ("Could not find a registered machine named '%ls'"), aName);
866
LogFlowThisFunc (("rc=%08X\n", rc));
867
LogFlowThisFuncLeave();
872
/** @note Locks objects! */
873
STDMETHODIMP VirtualBox::UnregisterMachine (INPTR GUIDPARAM aId,
880
AutoCaller autoCaller (this);
881
CheckComRCReturnRC (autoCaller.rc());
883
AutoLock alock (this);
885
ComObjPtr <Machine> machine;
887
HRESULT rc = findMachine (id, true /* setError */, &machine);
888
CheckComRCReturnRC (rc);
890
rc = machine->trySetRegistered (FALSE);
891
CheckComRCReturnRC (rc);
893
/* remove from the collection of registered machines */
894
mData.mMachines.remove (machine);
896
/* save the global registry */
899
/* return the unregistered machine to the caller */
900
machine.queryInterfaceTo (aMachine);
903
onMachineRegistered (id, FALSE);
908
STDMETHODIMP VirtualBox::CreateHardDisk (HardDiskStorageType_T aStorageType,
909
IHardDisk **aHardDisk)
914
AutoCaller autoCaller (this);
915
CheckComRCReturnRC (autoCaller.rc());
919
ComObjPtr <HardDisk> hardDisk;
921
switch (aStorageType)
923
case HardDiskStorageType_VirtualDiskImage:
925
ComObjPtr <HVirtualDiskImage> vdi;
927
rc = vdi->init (this, NULL, NULL);
932
case HardDiskStorageType_ISCSIHardDisk:
934
ComObjPtr <HISCSIHardDisk> iscsi;
935
iscsi.createObject();
936
rc = iscsi->init (this);
941
case HardDiskStorageType_VMDKImage:
943
ComObjPtr <HVMDKImage> vmdk;
945
rc = vmdk->init (this, NULL, NULL);
954
hardDisk.queryInterfaceTo (aHardDisk);
959
/** @note Locks mSystemProperties object for reading. */
960
STDMETHODIMP VirtualBox::OpenHardDisk (INPTR BSTR aLocation, IHardDisk **aHardDisk)
962
/* null and empty strings are not allowed locations */
963
if (!aLocation || !(*aLocation))
969
AutoCaller autoCaller (this);
970
CheckComRCReturnRC (autoCaller.rc());
972
/* Currently, the location is always a path. So, append the
973
* default path if only a name is given. */
974
Bstr location = aLocation;
976
Utf8Str loc = aLocation;
977
if (!RTPathHavePath (loc))
979
AutoLock propsLock (mData.mSystemProperties);
980
location = Utf8StrFmt ("%ls%c%s",
981
mData.mSystemProperties->defaultVDIFolder().raw(),
987
ComObjPtr <HardDisk> hardDisk;
988
HRESULT rc = HardDisk::openHardDisk (this, location, hardDisk);
990
hardDisk.queryInterfaceTo (aHardDisk);
995
/** @note Locks mSystemProperties object for reading. */
996
STDMETHODIMP VirtualBox::OpenVirtualDiskImage (INPTR BSTR aFilePath,
997
IVirtualDiskImage **aImage)
999
/* null and empty strings are not allowed as path names here */
1000
if (!aFilePath || !(*aFilePath))
1001
return E_INVALIDARG;
1006
AutoCaller autoCaller (this);
1007
CheckComRCReturnRC (autoCaller.rc());
1009
/* append the default path if only a name is given */
1010
Bstr path = aFilePath;
1012
Utf8Str fp = aFilePath;
1013
if (!RTPathHavePath (fp))
1015
AutoLock propsLock (mData.mSystemProperties);
1016
path = Utf8StrFmt ("%ls%c%s",
1017
mData.mSystemProperties->defaultVDIFolder().raw(),
1023
ComObjPtr <HVirtualDiskImage> vdi;
1025
HRESULT rc = vdi->init (this, NULL, path);
1028
vdi.queryInterfaceTo (aImage);
1033
/** @note Locks objects! */
1034
STDMETHODIMP VirtualBox::RegisterHardDisk (IHardDisk *aHardDisk)
1039
AutoCaller autoCaller (this);
1040
CheckComRCReturnRC (autoCaller.rc());
1042
VirtualBoxBase *child = getDependentChild (aHardDisk);
1044
return setError (E_FAIL, tr ("The given hard disk is not created within "
1045
"this VirtualBox instance"));
1048
* we can safely cast child to HardDisk * here because only HardDisk
1049
* implementations of IHardDisk can be among our children
1052
return registerHardDisk (static_cast <HardDisk *> (child), RHD_External);
1055
/** @note Locks objects! */
1056
STDMETHODIMP VirtualBox::GetHardDisk (INPTR GUIDPARAM aId, IHardDisk **aHardDisk)
1061
AutoCaller autoCaller (this);
1062
CheckComRCReturnRC (autoCaller.rc());
1065
ComObjPtr <HardDisk> hd;
1066
HRESULT rc = findHardDisk (&id, NULL, true /* setError */, &hd);
1068
/* the below will set *aHardDisk to NULL if hd is null */
1069
hd.queryInterfaceTo (aHardDisk);
1074
/** @note Locks objects! */
1075
STDMETHODIMP VirtualBox::FindHardDisk (INPTR BSTR aLocation,
1076
IHardDisk **aHardDisk)
1079
return E_INVALIDARG;
1083
AutoCaller autoCaller (this);
1084
CheckComRCReturnRC (autoCaller.rc());
1086
Utf8Str location = aLocation;
1087
if (strncmp (location, "iscsi:", 6) == 0)
1089
/* nothing special */
1093
/* For locations represented by file paths, append the default path if
1094
* only a name is given, and then get the full path. */
1095
if (!RTPathHavePath (location))
1097
AutoLock propsLock (mData.mSystemProperties);
1098
location = Utf8StrFmt ("%ls%c%s",
1099
mData.mSystemProperties->defaultVDIFolder().raw(),
1104
/* get the full file name */
1105
char buf [RTPATH_MAX];
1106
int vrc = RTPathAbsEx (mData.mHomeDir, location, buf, sizeof (buf));
1107
if (VBOX_FAILURE (vrc))
1108
return setError (E_FAIL, tr ("Invalid hard disk location '%ls' (%Vrc)"),
1113
ComObjPtr <HardDisk> hardDisk;
1114
HRESULT rc = findHardDisk (NULL, Bstr (location), true /* setError */,
1117
/* the below will set *aHardDisk to NULL if hardDisk is null */
1118
hardDisk.queryInterfaceTo (aHardDisk);
1123
/** @note Locks objects! */
1124
STDMETHODIMP VirtualBox::FindVirtualDiskImage (INPTR BSTR aFilePath,
1125
IVirtualDiskImage **aImage)
1128
return E_INVALIDARG;
1132
AutoCaller autoCaller (this);
1133
CheckComRCReturnRC (autoCaller.rc());
1135
/* append the default path if only a name is given */
1136
Utf8Str path = aFilePath;
1139
if (!RTPathHavePath (fp))
1141
AutoLock propsLock (mData.mSystemProperties);
1142
path = Utf8StrFmt ("%ls%c%s",
1143
mData.mSystemProperties->defaultVDIFolder().raw(),
1149
/* get the full file name */
1150
char buf [RTPATH_MAX];
1151
int vrc = RTPathAbsEx (mData.mHomeDir, path, buf, sizeof (buf));
1152
if (VBOX_FAILURE (vrc))
1153
return setError (E_FAIL, tr ("Invalid image file path '%ls' (%Vrc)"),
1156
ComObjPtr <HVirtualDiskImage> vdi;
1157
HRESULT rc = findVirtualDiskImage (NULL, Bstr (buf), true /* setError */,
1160
/* the below will set *aImage to NULL if vdi is null */
1161
vdi.queryInterfaceTo (aImage);
1166
/** @note Locks objects! */
1167
STDMETHODIMP VirtualBox::UnregisterHardDisk (INPTR GUIDPARAM aId, IHardDisk **aHardDisk)
1172
AutoCaller autoCaller (this);
1173
CheckComRCReturnRC (autoCaller.rc());
1178
ComObjPtr <HardDisk> hd;
1179
HRESULT rc = findHardDisk (&id, NULL, true /* setError */, &hd);
1180
CheckComRCReturnRC (rc);
1182
rc = unregisterHardDisk (hd);
1184
hd.queryInterfaceTo (aHardDisk);
1189
/** @note Doesn't lock anything. */
1190
STDMETHODIMP VirtualBox::OpenDVDImage (INPTR BSTR aFilePath, INPTR GUIDPARAM aId,
1191
IDVDImage **aDVDImage)
1193
/* null and empty strings are not allowed as path names */
1194
if (!aFilePath || !(*aFilePath))
1195
return E_INVALIDARG;
1200
AutoCaller autoCaller (this);
1201
CheckComRCReturnRC (autoCaller.rc());
1203
HRESULT rc = E_FAIL;
1206
/* generate an UUID if not specified */
1210
ComObjPtr <DVDImage> dvdImage;
1211
dvdImage.createObject();
1212
rc = dvdImage->init (this, aFilePath, FALSE /* !isRegistered */, uuid);
1214
dvdImage.queryInterfaceTo (aDVDImage);
1219
/** @note Locks objects! */
1220
STDMETHODIMP VirtualBox::RegisterDVDImage (IDVDImage *aDVDImage)
1225
AutoCaller autoCaller (this);
1226
CheckComRCReturnRC (autoCaller.rc());
1228
VirtualBoxBase *child = getDependentChild (aDVDImage);
1230
return setError (E_FAIL, tr ("The given CD/DVD image is not created within "
1231
"this VirtualBox instance"));
1234
* we can safely cast child to DVDImage * here because only DVDImage
1235
* implementations of IDVDImage can be among our children
1238
return registerDVDImage (static_cast <DVDImage *> (child),
1239
FALSE /* aOnStartUp */);
1242
/** @note Locks objects! */
1243
STDMETHODIMP VirtualBox::GetDVDImage (INPTR GUIDPARAM aId, IDVDImage **aDVDImage)
1248
AutoCaller autoCaller (this);
1249
CheckComRCReturnRC (autoCaller.rc());
1252
ComObjPtr <DVDImage> dvd;
1253
HRESULT rc = findDVDImage (&uuid, NULL, true /* setError */, &dvd);
1255
/* the below will set *aDVDImage to NULL if dvd is null */
1256
dvd.queryInterfaceTo (aDVDImage);
1261
/** @note Locks objects! */
1262
STDMETHODIMP VirtualBox::FindDVDImage (INPTR BSTR aFilePath, IDVDImage **aDVDImage)
1265
return E_INVALIDARG;
1269
AutoCaller autoCaller (this);
1270
CheckComRCReturnRC (autoCaller.rc());
1272
/* get the full file name */
1273
char buf [RTPATH_MAX];
1274
int vrc = RTPathAbsEx (mData.mHomeDir, Utf8Str (aFilePath), buf, sizeof (buf));
1275
if (VBOX_FAILURE (vrc))
1276
return setError (E_FAIL, tr ("Invalid image file path: '%ls' (%Vrc)"),
1279
ComObjPtr <DVDImage> dvd;
1280
HRESULT rc = findDVDImage (NULL, Bstr (buf), true /* setError */, &dvd);
1282
/* the below will set *dvdImage to NULL if dvd is null */
1283
dvd.queryInterfaceTo (aDVDImage);
1288
/** @note Locks objects! */
1289
STDMETHODIMP VirtualBox::GetDVDImageUsage (INPTR GUIDPARAM aId,
1290
ResourceUsage_T aUsage,
1295
if (aUsage == ResourceUsage_InvalidUsage)
1296
return E_INVALIDARG;
1298
AutoCaller autoCaller (this);
1299
CheckComRCReturnRC (autoCaller.rc());
1301
AutoReaderLock alock (this);
1303
Guid uuid = Guid (aId);
1304
HRESULT rc = findDVDImage (&uuid, NULL, true /* setError */, NULL);
1309
getDVDImageUsage (uuid, aUsage, &ids);
1310
ids.cloneTo (aMachineIDs);
1315
/** @note Locks objects! */
1316
STDMETHODIMP VirtualBox::UnregisterDVDImage (INPTR GUIDPARAM aId,
1317
IDVDImage **aDVDImage)
1322
AutoCaller autoCaller (this);
1323
CheckComRCReturnRC (autoCaller.rc());
1325
AutoLock alock (this);
1330
ComObjPtr <DVDImage> dvd;
1331
HRESULT rc = findDVDImage (&uuid, NULL, true /* setError */, &dvd);
1332
CheckComRCReturnRC (rc);
1334
if (!getDVDImageUsage (aId, ResourceUsage_AllUsage))
1336
/* remove from the collection */
1337
mData.mDVDImages.remove (dvd);
1339
/* save the global config file */
1344
rc = dvd.queryInterfaceTo (aDVDImage);
1345
ComAssertComRC (rc);
1349
rc = setError(E_FAIL,
1350
tr ("The CD/DVD image with the UUID {%s} is currently in use"),
1351
uuid.toString().raw());
1356
/** @note Doesn't lock anything. */
1357
STDMETHODIMP VirtualBox::OpenFloppyImage (INPTR BSTR aFilePath, INPTR GUIDPARAM aId,
1358
IFloppyImage **aFloppyImage)
1360
/* null and empty strings are not allowed as path names */
1361
if (!aFilePath || !(*aFilePath))
1362
return E_INVALIDARG;
1367
AutoCaller autoCaller (this);
1368
CheckComRCReturnRC (autoCaller.rc());
1370
HRESULT rc = E_FAIL;
1373
/* generate an UUID if not specified */
1374
if (Guid::isEmpty (aId))
1377
ComObjPtr <FloppyImage> floppyImage;
1378
floppyImage.createObject();
1379
rc = floppyImage->init (this, aFilePath, FALSE /* !isRegistered */, uuid);
1381
floppyImage.queryInterfaceTo (aFloppyImage);
1386
/** @note Locks objects! */
1387
STDMETHODIMP VirtualBox::RegisterFloppyImage (IFloppyImage *aFloppyImage)
1392
AutoCaller autoCaller (this);
1393
CheckComRCReturnRC (autoCaller.rc());
1395
VirtualBoxBase *child = getDependentChild (aFloppyImage);
1397
return setError (E_FAIL, tr ("The given floppy image is not created within "
1398
"this VirtualBox instance"));
1401
* we can safely cast child to FloppyImage * here because only FloppyImage
1402
* implementations of IFloppyImage can be among our children
1405
return registerFloppyImage (static_cast <FloppyImage *> (child),
1406
FALSE /* aOnStartUp */);
1409
/** @note Locks objects! */
1410
STDMETHODIMP VirtualBox::GetFloppyImage (INPTR GUIDPARAM aId,
1411
IFloppyImage **aFloppyImage)
1416
AutoCaller autoCaller (this);
1417
CheckComRCReturnRC (autoCaller.rc());
1420
ComObjPtr <FloppyImage> floppy;
1421
HRESULT rc = findFloppyImage (&uuid, NULL, true /* setError */, &floppy);
1423
/* the below will set *aFloppyImage to NULL if dvd is null */
1424
floppy.queryInterfaceTo (aFloppyImage);
1429
/** @note Locks objects! */
1430
STDMETHODIMP VirtualBox::FindFloppyImage (INPTR BSTR aFilePath,
1431
IFloppyImage **aFloppyImage)
1434
return E_INVALIDARG;
1438
AutoCaller autoCaller (this);
1439
CheckComRCReturnRC (autoCaller.rc());
1441
/* get the full file name */
1442
char buf [RTPATH_MAX];
1443
int vrc = RTPathAbsEx (mData.mHomeDir, Utf8Str (aFilePath), buf, sizeof (buf));
1444
if (VBOX_FAILURE (vrc))
1445
return setError (E_FAIL, tr ("Invalid image file path: '%ls' (%Vrc)"),
1448
ComObjPtr <FloppyImage> floppy;
1449
HRESULT rc = findFloppyImage (NULL, Bstr (buf), true /* setError */, &floppy);
1451
/* the below will set *image to NULL if img is null */
1452
floppy.queryInterfaceTo (aFloppyImage);
1457
/** @note Locks objects! */
1458
STDMETHODIMP VirtualBox::GetFloppyImageUsage (INPTR GUIDPARAM aId,
1459
ResourceUsage_T aUsage,
1464
if (aUsage == ResourceUsage_InvalidUsage)
1465
return E_INVALIDARG;
1467
AutoCaller autoCaller (this);
1468
CheckComRCReturnRC (autoCaller.rc());
1470
AutoReaderLock alock (this);
1472
Guid uuid = Guid (aId);
1473
HRESULT rc = findFloppyImage (&uuid, NULL, true /* setError */, NULL);
1478
getFloppyImageUsage (uuid, aUsage, &ids);
1479
ids.cloneTo (aMachineIDs);
1484
/** @note Locks objects! */
1485
STDMETHODIMP VirtualBox::UnregisterFloppyImage (INPTR GUIDPARAM aId,
1486
IFloppyImage **aFloppyImage)
1489
return E_INVALIDARG;
1491
AutoCaller autoCaller (this);
1492
CheckComRCReturnRC (autoCaller.rc());
1494
AutoLock alock (this);
1496
*aFloppyImage = NULL;
1499
ComObjPtr <FloppyImage> floppy;
1500
HRESULT rc = findFloppyImage (&uuid, NULL, true /* setError */, &floppy);
1501
CheckComRCReturnRC (rc);
1503
if (!getFloppyImageUsage (aId, ResourceUsage_AllUsage))
1505
/* remove from the collection */
1506
mData.mFloppyImages.remove (floppy);
1508
/* save the global config file */
1512
rc = floppy.queryInterfaceTo (aFloppyImage);
1513
ComAssertComRC (rc);
1517
rc = setError(E_FAIL,
1518
tr ("A floppy image with UUID {%s} is currently in use"),
1519
uuid.toString().raw());
1524
/** @note Locks this object for reading. */
1525
STDMETHODIMP VirtualBox::GetGuestOSType (INPTR BSTR aId, IGuestOSType **aType)
1528
return E_INVALIDARG;
1530
AutoCaller autoCaller (this);
1531
CheckComRCReturnRC (autoCaller.rc());
1535
AutoReaderLock alock (this);
1537
for (GuestOSTypeList::iterator it = mData.mGuestOSTypes.begin();
1538
it != mData.mGuestOSTypes.end();
1541
const Bstr &typeId = (*it)->id();
1542
AssertMsg (!!typeId, ("ID must not be NULL"));
1545
(*it).queryInterfaceTo (aType);
1550
return (*aType) ? S_OK :
1551
setError (E_INVALIDARG,
1552
tr ("'%ls' is not a valid Guest OS type"),
1557
VirtualBox::CreateSharedFolder (INPTR BSTR aName, INPTR BSTR aHostPath)
1559
if (!aName || !aHostPath)
1560
return E_INVALIDARG;
1562
AutoCaller autoCaller (this);
1563
CheckComRCReturnRC (autoCaller.rc());
1565
return setError (E_NOTIMPL, "Not yet implemented");
1568
STDMETHODIMP VirtualBox::RemoveSharedFolder (INPTR BSTR aName)
1571
return E_INVALIDARG;
1573
AutoCaller autoCaller (this);
1574
CheckComRCReturnRC (autoCaller.rc());
1576
return setError (E_NOTIMPL, "Not yet implemented");
1579
/** @note Locks this object for reading. */
1580
STDMETHODIMP VirtualBox::
1581
GetNextExtraDataKey (INPTR BSTR aKey, BSTR *aNextKey, BSTR *aNextValue)
1586
AutoCaller autoCaller (this);
1587
CheckComRCReturnRC (autoCaller.rc());
1589
/* start with nothing found */
1594
/* serialize file access */
1595
AutoReaderLock alock (this);
1597
CFGHANDLE configLoader;
1599
/* load the config file */
1600
int vrc = CFGLDRLoad (&configLoader, Utf8Str (mData.mCfgFile.mName),
1601
mData.mCfgFile.mHandle,
1602
XmlSchemaNS, true, cfgLdrEntityResolver, NULL);
1603
ComAssertRCRet (vrc, E_FAIL);
1605
CFGNODE extraDataNode;
1607
/* navigate to the right position */
1608
if (VBOX_SUCCESS (CFGLDRGetNode (configLoader,
1609
"VirtualBox/Global/ExtraData", 0,
1612
/* check if it exists */
1615
CFGNODE extraDataItemNode;
1616
CFGLDRCountChildren (extraDataNode, "ExtraDataItem", &count);
1617
for (unsigned i = 0; (i < count) && (found == false); i++)
1620
CFGLDRGetChildNode (extraDataNode, "ExtraDataItem", i, &extraDataItemNode);
1621
CFGLDRQueryBSTR (extraDataItemNode, "name", name.asOutParam());
1623
/* if we're supposed to return the first one */
1626
name.cloneTo (aNextKey);
1628
CFGLDRQueryBSTR (extraDataItemNode, "value", aNextValue);
1631
/* did we find the key we're looking for? */
1632
else if (name == aKey)
1635
/* is there another item? */
1638
CFGLDRGetChildNode (extraDataNode, "ExtraDataItem", i + 1,
1639
&extraDataItemNode);
1640
CFGLDRQueryBSTR (extraDataItemNode, "name", name.asOutParam());
1641
name.cloneTo (aNextKey);
1643
CFGLDRQueryBSTR (extraDataItemNode, "value", aNextValue);
1648
/* it's the last one */
1652
CFGLDRReleaseNode (extraDataItemNode);
1655
/* if we haven't found the key, it's an error */
1657
rc = setError (E_FAIL,
1658
tr ("Could not find extra data key '%ls'"), aKey);
1660
CFGLDRReleaseNode (extraDataNode);
1663
CFGLDRFree (configLoader);
1668
/** @note Locks this object for reading. */
1669
STDMETHODIMP VirtualBox::GetExtraData (INPTR BSTR aKey, BSTR *aValue)
1671
if (!aKey || !(*aKey))
1672
return E_INVALIDARG;
1676
AutoCaller autoCaller (this);
1677
CheckComRCReturnRC (autoCaller.rc());
1679
/* start with nothing found */
1684
/* serialize file access */
1685
AutoReaderLock alock (this);
1687
CFGHANDLE configLoader;
1689
/* load the config file */
1690
int vrc = CFGLDRLoad (&configLoader, Utf8Str (mData.mCfgFile.mName),
1691
mData.mCfgFile.mHandle,
1692
XmlSchemaNS, true, cfgLdrEntityResolver, NULL);
1693
ComAssertRCRet (vrc, E_FAIL);
1695
CFGNODE extraDataNode;
1697
/* navigate to the right position */
1698
if (VBOX_SUCCESS (CFGLDRGetNode (configLoader,
1699
"VirtualBox/Global/ExtraData", 0,
1702
/* check if it exists */
1705
CFGNODE extraDataItemNode;
1706
CFGLDRCountChildren (extraDataNode, "ExtraDataItem", &count);
1707
for (unsigned i = 0; (i < count) && (found == false); i++)
1710
CFGLDRGetChildNode (extraDataNode, "ExtraDataItem", i, &extraDataItemNode);
1711
CFGLDRQueryBSTR (extraDataItemNode, "name", name.asOutParam());
1715
CFGLDRQueryBSTR (extraDataItemNode, "value", aValue);
1717
CFGLDRReleaseNode (extraDataItemNode);
1720
CFGLDRReleaseNode (extraDataNode);
1723
CFGLDRFree (configLoader);
1728
/** @note Locks this object for writing. */
1729
STDMETHODIMP VirtualBox::SetExtraData(INPTR BSTR aKey, INPTR BSTR aValue)
1734
AutoCaller autoCaller (this);
1735
CheckComRCReturnRC (autoCaller.rc());
1739
bool changed = false;
1742
/* serialize file access */
1743
AutoLock alock (this);
1745
CFGHANDLE configLoader;
1747
/* load the config file */
1748
int vrc = CFGLDRLoad (&configLoader, Utf8Str (mData.mCfgFile.mName),
1749
mData.mCfgFile.mHandle,
1750
XmlSchemaNS, true, cfgLdrEntityResolver, NULL);
1751
ComAssertRCRet (vrc, E_FAIL);
1753
CFGNODE extraDataNode = 0;
1755
vrc = CFGLDRGetNode (configLoader, "VirtualBox/Global/ExtraData", 0,
1757
if (VBOX_FAILURE (vrc) && aValue)
1758
vrc = CFGLDRCreateNode (configLoader, "VirtualBox/Global/ExtraData",
1763
CFGNODE extraDataItemNode = 0;
1767
CFGLDRCountChildren (extraDataNode, "ExtraDataItem", &count);
1769
for (unsigned i = 0; i < count; i++)
1771
CFGLDRGetChildNode (extraDataNode, "ExtraDataItem", i, &extraDataItemNode);
1773
CFGLDRQueryBSTR (extraDataItemNode, "name", name.asOutParam());
1776
CFGLDRQueryBSTR (extraDataItemNode, "value", oldVal.asOutParam());
1779
CFGLDRReleaseNode (extraDataItemNode);
1780
extraDataItemNode = 0;
1784
* When no key is found, oldVal is null. Note:
1785
* 1. when oldVal is null, |oldVal == (BSTR) NULL| is true
1786
* 2. we cannot do |oldVal != value| because it will compare
1787
* BSTR pointers instead of strings (due to type conversion ops)
1789
changed = !(oldVal == aValue);
1793
/* ask for permission from all listeners */
1795
if (!onExtraDataCanChange (emptyGuid, aKey, aValue, error))
1797
const char *sep = error.isEmpty() ? "" : ": ";
1798
const BSTR err = error.isNull() ? (const BSTR) L"" : error.raw();
1799
LogWarningFunc (("Someone vetoed! Change refused%s%ls\n",
1801
rc = setError (E_ACCESSDENIED,
1802
tr ("Could not set extra data because someone refused "
1803
"the requested change of '%ls' to '%ls'%s%ls"),
1804
aKey, aValue, sep, err);
1810
if (!extraDataItemNode)
1812
/* create a new item */
1813
CFGLDRAppendChildNode (extraDataNode, "ExtraDataItem",
1814
&extraDataItemNode);
1815
CFGLDRSetBSTR (extraDataItemNode, "name", aKey);
1817
CFGLDRSetBSTR (extraDataItemNode, "value", aValue);
1821
/* an old value does for sure exist here */
1822
CFGLDRDeleteNode (extraDataItemNode);
1823
extraDataItemNode = 0;
1828
if (extraDataItemNode)
1829
CFGLDRReleaseNode (extraDataItemNode);
1831
CFGLDRReleaseNode (extraDataNode);
1833
if (SUCCEEDED (rc) && changed)
1835
char *loaderError = NULL;
1836
vrc = CFGLDRSave (configLoader, &loaderError);
1837
if (VBOX_FAILURE (vrc))
1839
rc = setError (E_FAIL,
1840
tr ("Could not save the settings file '%ls' (%Vrc)%s%s"),
1841
mData.mCfgFile.mName.raw(), vrc,
1842
loaderError ? ".\n" : "", loaderError ? loaderError : "");
1844
RTMemTmpFree (loaderError);
1849
CFGLDRFree (configLoader);
1851
/* notification handling */
1852
if (SUCCEEDED (rc) && changed)
1853
onExtraDataChange (emptyGuid, aKey, aValue);
1859
* @note Locks objects!
1861
STDMETHODIMP VirtualBox::OpenSession (ISession *aSession, INPTR GUIDPARAM aMachineId)
1864
return E_INVALIDARG;
1866
AutoCaller autoCaller (this);
1867
CheckComRCReturnRC (autoCaller.rc());
1869
Guid id = aMachineId;
1870
ComObjPtr <Machine> machine;
1872
HRESULT rc = findMachine (id, true /* setError */, &machine);
1873
CheckComRCReturnRC (rc);
1875
/* check the session state */
1876
SessionState_T state;
1877
rc = aSession->COMGETTER(State) (&state);
1878
CheckComRCReturnRC (rc);
1880
if (state != SessionState_SessionClosed)
1881
return setError (E_INVALIDARG,
1882
tr ("The given session is already open or being opened"));
1884
/* get the IInternalSessionControl interface */
1885
ComPtr <IInternalSessionControl> control = aSession;
1886
ComAssertMsgRet (!!control, ("No IInternalSessionControl interface"),
1889
rc = machine->openSession (control);
1894
* tell the client watcher thread to update the set of
1895
* machines that have open sessions
1897
updateClientWatcher();
1900
onSessionStateChange (aMachineId, SessionState_SessionOpen);
1907
* @note Locks objects!
1909
STDMETHODIMP VirtualBox::OpenRemoteSession (ISession *aSession,
1910
INPTR GUIDPARAM aMachineId,
1912
INPTR BSTR aEnvironment,
1913
IProgress **aProgress)
1915
if (!aSession || !aType)
1916
return E_INVALIDARG;
1920
AutoCaller autoCaller (this);
1921
CheckComRCReturnRC (autoCaller.rc());
1923
Guid id = aMachineId;
1924
ComObjPtr <Machine> machine;
1926
HRESULT rc = findMachine (id, true /* setError */, &machine);
1927
CheckComRCReturnRC (rc);
1929
/* check the session state */
1930
SessionState_T state;
1931
rc = aSession->COMGETTER(State) (&state);
1932
CheckComRCReturnRC (rc);
1934
if (state != SessionState_SessionClosed)
1935
return setError (E_INVALIDARG,
1936
tr ("The given session is already open or being opened"));
1938
/* get the IInternalSessionControl interface */
1939
ComPtr <IInternalSessionControl> control = aSession;
1940
ComAssertMsgRet (!!control, ("No IInternalSessionControl interface"),
1943
/* create a progress object */
1944
ComObjPtr <Progress> progress;
1945
progress.createObject();
1946
progress->init (this, (IMachine *) machine,
1947
Bstr (tr ("Spawning session")),
1948
FALSE /* aCancelable */);
1950
rc = machine->openRemoteSession (control, aType, aEnvironment, progress);
1954
progress.queryInterfaceTo (aProgress);
1957
onSessionStateChange (aMachineId, SessionState_SessionSpawning);
1964
* @note Locks objects!
1966
STDMETHODIMP VirtualBox::OpenExistingSession (ISession *aSession,
1967
INPTR GUIDPARAM aMachineId)
1972
AutoCaller autoCaller (this);
1973
CheckComRCReturnRC (autoCaller.rc());
1975
Guid id = aMachineId;
1976
ComObjPtr <Machine> machine;
1978
HRESULT rc = findMachine (id, true /* setError */, &machine);
1979
CheckComRCReturnRC (rc);
1981
/* check the session state */
1982
SessionState_T state;
1983
rc = aSession->COMGETTER(State) (&state);
1984
CheckComRCReturnRC (rc);
1986
if (state != SessionState_SessionClosed)
1987
return setError (E_INVALIDARG,
1988
tr ("The given session is already open or being opened"));
1990
/* get the IInternalSessionControl interface */
1991
ComPtr <IInternalSessionControl> control = aSession;
1992
ComAssertMsgRet (!!control, ("No IInternalSessionControl interface"),
1995
rc = machine->openExistingSession (control);
2001
* Registers a new client callback on this instance. The methods of the
2002
* callback interface will be called by this instance when the appropriate
2005
* @note Locks this object for writing.
2007
STDMETHODIMP VirtualBox::RegisterCallback (IVirtualBoxCallback *callback)
2009
LogFlowMember (("VirtualBox::RegisterCallback(): callback=%p\n", callback));
2012
return E_INVALIDARG;
2014
AutoCaller autoCaller (this);
2015
CheckComRCReturnRC (autoCaller.rc());
2017
AutoLock alock (this);
2018
mData.mCallbacks.push_back (CallbackList::value_type (callback));
2024
* Unregisters the previously registered client callback.
2026
* @note Locks this object for writing.
2028
STDMETHODIMP VirtualBox::UnregisterCallback (IVirtualBoxCallback *callback)
2031
return E_INVALIDARG;
2033
AutoCaller autoCaller (this);
2034
CheckComRCReturnRC (autoCaller.rc());
2038
AutoLock alock (this);
2040
CallbackList::iterator it;
2041
it = std::find (mData.mCallbacks.begin(),
2042
mData.mCallbacks.end(),
2043
CallbackList::value_type (callback));
2044
if (it == mData.mCallbacks.end())
2047
mData.mCallbacks.erase (it);
2049
LogFlowMember (("VirtualBox::UnregisterCallback(): callback=%p, rc=%08X\n",
2054
// public methods only for internal purposes
2055
/////////////////////////////////////////////////////////////////////////////
2058
* Posts an event to the event queue that is processed asynchronously
2059
* on a dedicated thread.
2061
* Posting events to the dedicated event queue is useful to perform secondary
2062
* actions outside any object locks -- for example, to iterate over a list
2063
* of callbacks and inform them about some change caused by some object's
2066
* @param event event to post
2067
* (must be allocated using |new|, will be deleted automatically
2068
* by the event thread after processing)
2070
* @note Doesn't lock any object.
2072
HRESULT VirtualBox::postEvent (Event *event)
2074
AutoCaller autoCaller (this);
2075
AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
2077
if (autoCaller.state() != Ready)
2079
LogWarningFunc (("VirtualBox has been uninitialized (state=%d), "
2080
"the event is discarded!\n",
2081
autoCaller.state()));
2085
AssertReturn (event, E_FAIL);
2086
AssertReturn (mAsyncEventQ, E_FAIL);
2088
AutoLock alock (mAsyncEventQLock);
2089
if (mAsyncEventQ->postEvent (event))
2096
* Helper method to add a progress to the global collection of pending
2099
* @param aProgress operation to add to the collection
2100
* @return COM status code
2102
* @note Locks this object for writing.
2104
HRESULT VirtualBox::addProgress (IProgress *aProgress)
2107
return E_INVALIDARG;
2109
AutoCaller autoCaller (this);
2110
CheckComRCReturnRC (autoCaller.rc());
2112
AutoLock alock (this);
2113
mData.mProgressOperations.push_back (aProgress);
2118
* Helper method to remove the progress from the global collection of pending
2119
* operations. Usualy gets called upon progress completion.
2121
* @param aId UUID of the progress operation to remove
2122
* @return COM status code
2124
* @note Locks this object for writing.
2126
HRESULT VirtualBox::removeProgress (INPTR GUIDPARAM aId)
2128
AutoCaller autoCaller (this);
2129
CheckComRCReturnRC (autoCaller.rc());
2131
ComPtr <IProgress> progress;
2133
AutoLock alock (this);
2135
for (ProgressList::iterator it = mData.mProgressOperations.begin();
2136
it != mData.mProgressOperations.end();
2140
(*it)->COMGETTER(Id) (id.asOutParam());
2143
mData.mProgressOperations.erase (it);
2148
AssertFailed(); /* should never happen */
2153
#ifdef RT_OS_WINDOWS
2155
struct StartSVCHelperClientData
2157
ComObjPtr <VirtualBox> that;
2158
ComObjPtr <Progress> progress;
2160
VirtualBox::SVCHelperClientFunc func;
2165
* Helper method to that starts a worker thread that:
2166
* - creates a pipe communication channel using SVCHlpClient;
2167
* - starts a SVC Helper process that will inherit this channel;
2168
* - executes the supplied function by passing it the created SVCHlpClient
2169
* and opened instance to communicate to the Helper process and the given
2172
* The user function is supposed to communicate to the helper process
2173
* using the \a aClient argument to do the requested job and optionally expose
2174
* the prgress through the \a aProgress object. The user function should never
2175
* call notifyComplete() on it: this will be done automatically using the
2176
* result code returned by the function.
2178
* Before the user function is stared, the communication channel passed to in
2179
* the \a aClient argument, is fully set up, the function should start using
2180
* it's write() and read() methods directly.
2182
* The \a aVrc parameter of the user function may be used to return an error
2183
* code if it is related to communication errors (for example, returned by
2184
* the SVCHlpClient members when they fail). In this case, the correct error
2185
* message using this value will be reported to the caller. Note that the
2186
* value of \a aVrc is inspected only if the user function itself returns
2189
* If a failure happens anywhere before the user function would be normally
2190
* called, it will be called anyway in special "cleanup only" mode indicated
2191
* by \a aClient, \a aProgress and \aVrc arguments set to NULL. In this mode,
2192
* all the function is supposed to do is to cleanup its aUser argument if
2193
* necessary (it's assumed that the ownership of this argument is passed to
2194
* the user function once #startSVCHelperClient() returns a success, thus
2195
* making it responsible for the cleanup).
2197
* After the user function returns, the thread will send the SVCHlpMsg::Null
2198
* message to indicate a process termination.
2200
* @param aPrivileged |true| to start the SVC Hepler process as a privlieged
2201
* user that can perform administrative tasks
2202
* @param aFunc user function to run
2203
* @param aUser argument to the user function
2204
* @param aProgress progress object that will track operation completion
2206
* @note aPrivileged is currently ignored (due to some unsolved problems in
2207
* Vista) and the process will be started as a normal (unprivileged)
2210
* @note Doesn't lock anything.
2212
HRESULT VirtualBox::startSVCHelperClient (bool aPrivileged,
2213
SVCHelperClientFunc aFunc,
2214
void *aUser, Progress *aProgress)
2216
AssertReturn (aFunc, E_POINTER);
2217
AssertReturn (aProgress, E_POINTER);
2219
AutoCaller autoCaller (this);
2220
CheckComRCReturnRC (autoCaller.rc());
2222
/* create the SVCHelperClientThread() argument */
2223
std::auto_ptr <StartSVCHelperClientData>
2224
d (new StartSVCHelperClientData());
2225
AssertReturn (d.get(), E_OUTOFMEMORY);
2228
d->progress = aProgress;
2229
d->privileged = aPrivileged;
2233
RTTHREAD tid = NIL_RTTHREAD;
2234
int vrc = RTThreadCreate (&tid, SVCHelperClientThread,
2235
static_cast <void *> (d.get()),
2236
0, RTTHREADTYPE_MAIN_WORKER,
2237
RTTHREADFLAGS_WAITABLE, "SVCHelper");
2239
ComAssertMsgRCRet (vrc, ("Could not create SVCHelper thread (%Vrc)\n", vrc),
2242
/* d is now owned by SVCHelperClientThread(), so release it */
2249
* Worker thread for startSVCHelperClient().
2253
VirtualBox::SVCHelperClientThread (RTTHREAD aThread, void *aUser)
2257
std::auto_ptr <StartSVCHelperClientData>
2258
d (static_cast <StartSVCHelperClientData *> (aUser));
2261
bool userFuncCalled = false;
2265
AssertBreak (d.get(), rc = E_POINTER);
2266
AssertReturn (!d->progress.isNull(), E_POINTER);
2268
/* protect VirtualBox from uninitialization */
2269
AutoCaller autoCaller (d->that);
2270
if (!autoCaller.isOk())
2273
rc = autoCaller.rc();
2277
int vrc = VINF_SUCCESS;
2281
SVCHlpClient client;
2282
vrc = client.create (Utf8StrFmt ("VirtualBox\\SVCHelper\\{%Vuuid}",
2284
if (VBOX_FAILURE (vrc))
2286
rc = setError (E_FAIL,
2287
tr ("Could not create the communication channel (%Vrc)"), vrc);
2291
/* get the path to the executable */
2292
char exePathBuf [RTPATH_MAX];
2293
char *exePath = RTProcGetExecutableName (exePathBuf, RTPATH_MAX);
2294
ComAssertBreak (exePath, E_FAIL);
2296
Utf8Str argsStr = Utf8StrFmt ("/Helper %s", client.name().raw());
2298
LogFlowFunc (("Starting '\"%s\" %s'...\n", exePath, argsStr.raw()));
2300
RTPROCESS pid = NIL_RTPROCESS;
2304
/* Attempt to start a privileged process using the Run As dialog */
2306
Bstr file = exePath;
2307
Bstr parameters = argsStr;
2309
SHELLEXECUTEINFO shExecInfo;
2311
shExecInfo.cbSize = sizeof (SHELLEXECUTEINFO);
2313
shExecInfo.fMask = NULL;
2314
shExecInfo.hwnd = NULL;
2315
shExecInfo.lpVerb = L"runas";
2316
shExecInfo.lpFile = file;
2317
shExecInfo.lpParameters = parameters;
2318
shExecInfo.lpDirectory = NULL;
2319
shExecInfo.nShow = SW_NORMAL;
2320
shExecInfo.hInstApp = NULL;
2322
if (!ShellExecuteEx (&shExecInfo))
2324
int vrc2 = RTErrConvertFromWin32 (GetLastError());
2325
/* hide excessive details in case of a frequent error
2326
* (pressing the Cancel button to close the Run As dialog) */
2327
if (vrc2 == VERR_CANCELLED)
2328
rc = setError (E_FAIL,
2329
tr ("Operatiion cancelled by the user"));
2331
rc = setError (E_FAIL,
2332
tr ("Could not launch a privileged process '%s' (%Vrc)"),
2339
const char *args[] = { exePath, "/Helper", client.name(), 0 };
2340
vrc = RTProcCreate (exePath, args, NULL, 0, &pid);
2341
if (VBOX_FAILURE (vrc))
2343
rc = setError (E_FAIL,
2344
tr ("Could not launch a process '%s' (%Vrc)"), exePath, vrc);
2349
/* wait for the client to connect */
2350
vrc = client.connect();
2351
if (VBOX_SUCCESS (vrc))
2353
/* start the user supplied function */
2354
rc = d->func (&client, d->progress, d->user, &vrc);
2355
userFuncCalled = true;
2358
/* send the termination signal to the process anyway */
2360
int vrc2 = client.write (SVCHlpMsg::Null);
2361
if (VBOX_SUCCESS (vrc))
2365
if (SUCCEEDED (rc) && VBOX_FAILURE (vrc))
2367
rc = setError (E_FAIL,
2368
tr ("Could not operate the communication channel (%Vrc)"), vrc);
2374
if (FAILED (rc) && !userFuncCalled)
2376
/* call the user function in the "cleanup only" mode
2377
* to let it free resources passed to in aUser */
2378
d->func (NULL, NULL, d->user, NULL);
2381
d->progress->notifyComplete (rc);
2387
#endif /* RT_OS_WINDOWS */
2390
* Sends a signal to the client watcher thread to rescan the set of machines
2391
* that have open sessions.
2393
* @note Doesn't lock anything.
2395
void VirtualBox::updateClientWatcher()
2397
AutoCaller autoCaller (this);
2398
AssertComRCReturn (autoCaller.rc(), (void) 0);
2400
AssertReturn (mWatcherData.mThread != NIL_RTTHREAD, (void) 0);
2402
/* sent an update request */
2403
#if defined(RT_OS_WINDOWS)
2404
::SetEvent (mWatcherData.mUpdateReq);
2405
#elif defined(RT_OS_OS2)
2406
RTSemEventSignal (mWatcherData.mUpdateReq);
2407
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
2408
RTSemEventSignal (mWatcherData.mUpdateReq);
2415
* Adds the given child process ID to the list of processes to be reaped.
2416
* This call should be followed by #updateClientWatcher() to take the effect.
2418
void VirtualBox::addProcessToReap (RTPROCESS pid)
2420
AutoCaller autoCaller (this);
2421
AssertComRCReturn (autoCaller.rc(), (void) 0);
2423
/// @todo (dmik) Win32?
2424
#ifndef RT_OS_WINDOWS
2425
AutoLock alock (this);
2426
mWatcherData.mProcesses.push_back (pid);
2430
/** Event for onMachineStateChange(), onMachineDataChange(), onMachineRegistered() */
2431
struct MachineEvent : public VirtualBox::CallbackEvent
2433
enum What { DataChanged, StateChanged, Registered };
2435
MachineEvent (VirtualBox *aVB, const Guid &aId)
2436
: CallbackEvent (aVB), what (DataChanged), id (aId)
2439
MachineEvent (VirtualBox *aVB, const Guid &aId, MachineState_T aState)
2440
: CallbackEvent (aVB), what (StateChanged), id (aId)
2444
MachineEvent (VirtualBox *aVB, const Guid &aId, BOOL aRegistered)
2445
: CallbackEvent (aVB), what (Registered), id (aId)
2446
, registered (aRegistered)
2449
void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
2454
LogFlow (("OnMachineDataChange: id={%Vuuid}\n", id.ptr()));
2455
aCallback->OnMachineDataChange (id);
2459
LogFlow (("OnMachineStateChange: id={%Vuuid}, state=%d\n",
2461
aCallback->OnMachineStateChange (id, state);
2465
LogFlow (("OnMachineRegistered: id={%Vuuid}, registered=%d\n",
2466
id.ptr(), registered));
2467
aCallback->OnMachineRegistered (id, registered);
2475
MachineState_T state;
2480
* @note Doesn't lock any object.
2482
void VirtualBox::onMachineStateChange (const Guid &aId, MachineState_T aState)
2484
postEvent (new MachineEvent (this, aId, aState));
2488
* @note Doesn't lock any object.
2490
void VirtualBox::onMachineDataChange (const Guid &aId)
2492
postEvent (new MachineEvent (this, aId));
2496
* @note Locks this object for reading.
2498
BOOL VirtualBox::onExtraDataCanChange (const Guid &aId, INPTR BSTR aKey, INPTR BSTR aValue,
2501
LogFlowThisFunc (("machine={%s} aKey={%ls} aValue={%ls}\n",
2502
aId.toString().raw(), aKey, aValue));
2504
AutoCaller autoCaller (this);
2505
AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
2509
AutoReaderLock alock (this);
2510
list = mData.mCallbacks;
2513
BOOL allowChange = TRUE;
2514
CallbackList::iterator it = list.begin();
2515
while ((it != list.end()) && allowChange)
2517
HRESULT rc = (*it++)->OnExtraDataCanChange (aId, aKey, aValue,
2518
aError.asOutParam(), &allowChange);
2521
/* if a call to this method fails for some reason (for ex., because
2522
* the other side is dead), we ensure allowChange stays true
2523
* (MS COM RPC implementation seems to zero all output vars before
2524
* issuing an IPC call or after a failure, so it's essential
2530
LogFlowThisFunc (("allowChange=%RTbool\n", allowChange));
2534
/** Event for onExtraDataChange() */
2535
struct ExtraDataEvent : public VirtualBox::CallbackEvent
2537
ExtraDataEvent (VirtualBox *aVB, const Guid &aMachineId,
2538
INPTR BSTR aKey, INPTR BSTR aVal)
2539
: CallbackEvent (aVB), machineId (aMachineId)
2540
, key (aKey), val (aVal)
2543
void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
2545
LogFlow (("OnExtraDataChange: machineId={%Vuuid}, key='%ls', val='%ls'\n",
2546
machineId.ptr(), key.raw(), val.raw()));
2547
aCallback->OnExtraDataChange (machineId, key, val);
2555
* @note Doesn't lock any object.
2557
void VirtualBox::onExtraDataChange (const Guid &aId, INPTR BSTR aKey, INPTR BSTR aValue)
2559
postEvent (new ExtraDataEvent (this, aId, aKey, aValue));
2563
* @note Doesn't lock any object.
2565
void VirtualBox::onMachineRegistered (const Guid &aId, BOOL aRegistered)
2567
postEvent (new MachineEvent (this, aId, aRegistered));
2570
/** Event for onSessionStateChange() */
2571
struct SessionEvent : public VirtualBox::CallbackEvent
2573
SessionEvent (VirtualBox *aVB, const Guid &aMachineId, SessionState_T aState)
2574
: CallbackEvent (aVB), machineId (aMachineId), sessionState (aState)
2577
void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
2579
LogFlow (("OnSessionStateChange: machineId={%Vuuid}, sessionState=%d\n",
2580
machineId.ptr(), sessionState));
2581
aCallback->OnSessionStateChange (machineId, sessionState);
2585
SessionState_T sessionState;
2589
* @note Doesn't lock any object.
2591
void VirtualBox::onSessionStateChange (const Guid &aId, SessionState_T aState)
2593
postEvent (new SessionEvent (this, aId, aState));
2596
/** Event for onSnapshotTaken(), onSnapshotRemoved() and onSnapshotChange() */
2597
struct SnapshotEvent : public VirtualBox::CallbackEvent
2599
enum What { Taken, Discarded, Changed };
2601
SnapshotEvent (VirtualBox *aVB, const Guid &aMachineId, const Guid &aSnapshotId,
2603
: CallbackEvent (aVB)
2605
, machineId (aMachineId), snapshotId (aSnapshotId)
2608
void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
2613
LogFlow (("OnSnapshotTaken: machineId={%Vuuid}, snapshotId={%Vuuid}\n",
2614
machineId.ptr(), snapshotId.ptr()));
2615
aCallback->OnSnapshotTaken (machineId, snapshotId);
2619
LogFlow (("OnSnapshotDiscarded: machineId={%Vuuid}, snapshotId={%Vuuid}\n",
2620
machineId.ptr(), snapshotId.ptr()));
2621
aCallback->OnSnapshotDiscarded (machineId, snapshotId);
2625
LogFlow (("OnSnapshotChange: machineId={%Vuuid}, snapshotId={%Vuuid}\n",
2626
machineId.ptr(), snapshotId.ptr()));
2627
aCallback->OnSnapshotChange (machineId, snapshotId);
2639
* @note Doesn't lock any object.
2641
void VirtualBox::onSnapshotTaken (const Guid &aMachineId, const Guid &aSnapshotId)
2643
postEvent (new SnapshotEvent (this, aMachineId, aSnapshotId, SnapshotEvent::Taken));
2647
* @note Doesn't lock any object.
2649
void VirtualBox::onSnapshotDiscarded (const Guid &aMachineId, const Guid &aSnapshotId)
2651
postEvent (new SnapshotEvent (this, aMachineId, aSnapshotId, SnapshotEvent::Discarded));
2655
* @note Doesn't lock any object.
2657
void VirtualBox::onSnapshotChange (const Guid &aMachineId, const Guid &aSnapshotId)
2659
postEvent (new SnapshotEvent (this, aMachineId, aSnapshotId, SnapshotEvent::Changed));
2663
* @note Locks this object for reading.
2665
ComObjPtr <GuestOSType> VirtualBox::getUnknownOSType()
2667
ComObjPtr <GuestOSType> type;
2669
AutoCaller autoCaller (this);
2670
AssertComRCReturn (autoCaller.rc(), type);
2672
AutoReaderLock alock (this);
2674
/* unknown type must always be the first */
2675
ComAssertRet (mData.mGuestOSTypes.size() > 0, type);
2677
type = mData.mGuestOSTypes.front();
2682
* Returns the list of opened machines (i.e. machines having direct sessions
2683
* opened by client processes).
2685
* @note the returned list contains smart pointers. So, clear it as soon as
2686
* it becomes no more necessary to release instances.
2687
* @note it can be possible that a session machine from the list has been
2688
* already uninitialized, so a) lock the instance and b) chheck for
2689
* instance->isReady() return value before manipulating the object directly
2690
* (i.e. not through COM methods).
2692
* @note Locks objects for reading.
2694
void VirtualBox::getOpenedMachines (SessionMachineVector &aVector)
2696
AutoCaller autoCaller (this);
2697
AssertComRCReturn (autoCaller.rc(), (void) 0);
2699
std::list <ComObjPtr <SessionMachine> > list;
2702
AutoReaderLock alock (this);
2704
for (MachineList::iterator it = mData.mMachines.begin();
2705
it != mData.mMachines.end();
2708
ComObjPtr <SessionMachine> sm = (*it)->sessionMachine();
2709
/* SessionMachine is null when there are no open sessions */
2711
list.push_back (sm);
2715
aVector = SessionMachineVector (list.begin(), list.end());
2720
* Helper to find machines that use the given DVD image.
2722
* @param machineIDs string where to store the list (can be NULL)
2723
* @return TRUE if at least one machine found and false otherwise
2725
* @note For now, we just scan all the machines. We can optimize this later
2726
* if required by adding the corresponding field to DVDImage and requiring all
2727
* IDVDImage instances to be DVDImage objects.
2729
* @note Locks objects for reading.
2731
BOOL VirtualBox::getDVDImageUsage (const Guid &id,
2732
ResourceUsage_T usage,
2735
AutoCaller autoCaller (this);
2736
AssertComRCReturn (autoCaller.rc(), FALSE);
2738
typedef std::set <Guid> Set;
2742
AutoReaderLock alock (this);
2744
for (MachineList::const_iterator mit = mData.mMachines.begin();
2745
mit != mData.mMachines.end();
2748
/// @todo (dmik) move this part to Machine for better incapsulation
2750
ComObjPtr <Machine> m = *mit;
2751
AutoReaderLock malock (m);
2753
/* take the session machine when appropriate */
2754
if (!m->data()->mSession.mMachine.isNull())
2755
m = m->data()->mSession.mMachine;
2757
const ComObjPtr <DVDDrive> &dvd = m->dvdDrive();
2758
AutoReaderLock dalock (dvd);
2760
/* loop over the backed up (permanent) and current (temporary) dvd data */
2761
DVDDrive::Data *dvdData [2];
2762
if (dvd->data().isBackedUp())
2764
dvdData [0] = dvd->data().backedUpData();
2765
dvdData [1] = dvd->data().data();
2769
dvdData [0] = dvd->data().data();
2773
if (!(usage & ResourceUsage_PermanentUsage))
2775
if (!(usage & ResourceUsage_TemporaryUsage))
2778
for (unsigned i = 0; i < ELEMENTS (dvdData); i++)
2782
if (dvdData [i]->mDriveState == DriveState_ImageMounted)
2785
dvdData [i]->mDVDImage->COMGETTER(Id) (iid.asOutParam());
2787
idSet.insert (m->data()->mUuid);
2798
/* convert to a string of UUIDs */
2799
char *idList = (char *) RTMemTmpAllocZ (RTUUID_STR_LENGTH * idSet.size());
2800
char *idListPtr = idList;
2801
for (Set::iterator it = idSet.begin(); it != idSet.end(); ++ it)
2803
RTUuidToStr (*it, idListPtr, RTUUID_STR_LENGTH);
2804
idListPtr += RTUUID_STR_LENGTH - 1;
2805
/* replace EOS with a space char */
2806
*(idListPtr ++) = ' ';
2808
Assert (int (idListPtr - idList) == int (RTUUID_STR_LENGTH * idSet.size()));
2809
/* remove the trailing space */
2810
*(-- idListPtr) = 0;
2811
/* copy the string */
2812
*machineIDs = idList;
2813
RTMemTmpFree (idList);
2817
(*machineIDs).setNull();
2821
return !idSet.empty();
2825
* Helper to find machines that use the given floppy image.
2827
* @param machineIDs string where to store the list (can be NULL)
2828
* @return TRUE if at least one machine found and false otherwise
2830
* @note For now, we just scan all the machines. We can optimize this later
2831
* if required by adding the corresponding field to FloppyImage and requiring all
2832
* IFloppyImage instances to be FloppyImage objects.
2834
* @note Locks objects for reading.
2836
BOOL VirtualBox::getFloppyImageUsage (const Guid &id,
2837
ResourceUsage_T usage,
2840
AutoCaller autoCaller (this);
2841
AssertComRCReturn (autoCaller.rc(), FALSE);
2843
typedef std::set <Guid> Set;
2847
AutoReaderLock alock (this);
2849
for (MachineList::const_iterator mit = mData.mMachines.begin();
2850
mit != mData.mMachines.end();
2853
/// @todo (dmik) move this part to Machine for better incapsulation
2855
ComObjPtr <Machine> m = *mit;
2856
AutoReaderLock malock (m);
2858
/* take the session machine when appropriate */
2859
if (!m->data()->mSession.mMachine.isNull())
2860
m = m->data()->mSession.mMachine;
2862
const ComObjPtr <FloppyDrive> &drv = m->floppyDrive();
2863
AutoReaderLock dalock (drv);
2865
/* loop over the backed up (permanent) and current (temporary) floppy data */
2866
FloppyDrive::Data *data [2];
2867
if (drv->data().isBackedUp())
2869
data [0] = drv->data().backedUpData();
2870
data [1] = drv->data().data();
2874
data [0] = drv->data().data();
2878
if (!(usage & ResourceUsage_PermanentUsage))
2880
if (!(usage & ResourceUsage_TemporaryUsage))
2883
for (unsigned i = 0; i < ELEMENTS (data); i++)
2887
if (data [i]->mDriveState == DriveState_ImageMounted)
2890
data [i]->mFloppyImage->COMGETTER(Id) (iid.asOutParam());
2892
idSet.insert (m->data()->mUuid);
2903
/* convert to a string of UUIDs */
2904
char *idList = (char *) RTMemTmpAllocZ (RTUUID_STR_LENGTH * idSet.size());
2905
char *idListPtr = idList;
2906
for (Set::iterator it = idSet.begin(); it != idSet.end(); ++ it)
2908
RTUuidToStr (*it, idListPtr, RTUUID_STR_LENGTH);
2909
idListPtr += RTUUID_STR_LENGTH - 1;
2910
/* replace EOS with a space char */
2911
*(idListPtr ++) = ' ';
2913
Assert (int (idListPtr - idList) == int (RTUUID_STR_LENGTH * idSet.size()));
2914
/* remove the trailing space */
2915
*(-- idListPtr) = 0;
2916
/* copy the string */
2917
*machineIDs = idList;
2918
RTMemTmpFree (idList);
2922
(*machineIDs).setNull();
2926
return !idSet.empty();
2930
* Tries to calculate the relative path of the given absolute path using the
2931
* directory of the VirtualBox settings file as the base directory.
2933
* @param aPath absolute path to calculate the relative path for
2934
* @param aResult where to put the result (used only when it's possible to
2935
* make a relative path from the given absolute path;
2936
* otherwise left untouched)
2938
* @note Doesn't lock any object.
2940
void VirtualBox::calculateRelativePath (const char *aPath, Utf8Str &aResult)
2942
AutoCaller autoCaller (this);
2943
AssertComRCReturn (autoCaller.rc(), (void) 0);
2945
/* no need to lock since mHomeDir is const */
2947
Utf8Str settingsDir = mData.mHomeDir;
2949
if (RTPathStartsWith (aPath, settingsDir))
2951
/* when assigning, we create a separate Utf8Str instance because both
2952
* aPath and aResult can point to the same memory location when this
2953
* func is called (if we just do aResult = aPath, aResult will be freed
2954
* first, and since its the same as aPath, an attempt to copy garbage
2956
aResult = Utf8Str (aPath + settingsDir.length() + 1);
2961
/////////////////////////////////////////////////////////////////////////////
2964
* Searches for a Machine object with the given ID in the collection
2965
* of registered machines.
2970
* if TRUE, the appropriate error info is set in case when the machine
2973
* where to store the found machine object (can be NULL)
2976
* S_OK when found or E_INVALIDARG when not found
2978
* @note Locks this object for reading.
2980
HRESULT VirtualBox::findMachine (const Guid &aId, bool aSetError,
2981
ComObjPtr <Machine> *aMachine /* = NULL */)
2983
AutoCaller autoCaller (this);
2984
AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
2989
AutoReaderLock alock (this);
2991
for (MachineList::iterator it = mData.mMachines.begin();
2992
!found && it != mData.mMachines.end();
2995
/* mUuid is constant, no need to lock */
2996
found = (*it)->data()->mUuid == aId;
2997
if (found && aMachine)
3002
HRESULT rc = found ? S_OK : E_INVALIDARG;
3004
if (aSetError && !found)
3006
setError (E_INVALIDARG,
3007
tr ("Could not find a registered machine with UUID {%Vuuid}"),
3015
* Searches for a HardDisk object with the given ID or location specification
3016
* in the collection of registered hard disks. If both ID and location are
3017
* specified, the first object that matches either of them (not necessarily
3018
* both) is returned.
3020
* @param aId ID of the hard disk (NULL when unused)
3021
* @param aLocation full location specification (NULL when unused)
3022
* @param aSetError if TRUE, the appropriate error info is set in case when
3023
* the disk is not found and only one search criteria (ID
3024
* or file name) is specified.
3025
* @param aHardDisk where to store the found hard disk object (can be NULL)
3028
* S_OK when found or E_INVALIDARG when not found
3030
* @note Locks objects for reading!
3032
HRESULT VirtualBox::
3033
findHardDisk (const Guid *aId, const BSTR aLocation,
3034
bool aSetError, ComObjPtr <HardDisk> *aHardDisk /* = NULL */)
3036
ComAssertRet (aId || aLocation, E_INVALIDARG);
3038
AutoReaderLock alock (this);
3040
/* first lookup the map by UUID if UUID is provided */
3043
HardDiskMap::const_iterator it = mData.mHardDiskMap.find (*aId);
3044
if (it != mData.mHardDiskMap.end())
3047
*aHardDisk = (*it).second;
3052
/* then iterate and find by location */
3056
Utf8Str location = aLocation;
3058
for (HardDiskMap::const_iterator it = mData.mHardDiskMap.begin();
3059
!found && it != mData.mHardDiskMap.end();
3062
const ComObjPtr <HardDisk> &hd = (*it).second;
3063
AutoReaderLock hdLock (hd);
3065
if (hd->storageType() == HardDiskStorageType_VirtualDiskImage ||
3066
hd->storageType() == HardDiskStorageType_VMDKImage)
3068
/* locations of VDI and VMDK hard disks for now are just
3070
found = RTPathCompare (location,
3071
Utf8Str (hd->toString
3072
(false /* aShort */))) == 0;
3076
found = aLocation == hd->toString (false /* aShort */);
3079
if (found && aHardDisk)
3084
HRESULT rc = found ? S_OK : E_INVALIDARG;
3086
if (aSetError && !found)
3088
if (aId && !aLocation)
3089
setError (rc, tr ("Could not find a registered hard disk "
3090
"with UUID {%Vuuid}"), aId->raw());
3091
else if (aLocation && !aId)
3092
setError (rc, tr ("Could not find a registered hard disk "
3093
"with location '%ls'"), aLocation);
3100
* @deprecated Use #findHardDisk() instead.
3102
* Searches for a HVirtualDiskImage object with the given ID or file path in the
3103
* collection of registered hard disks. If both ID and file path are specified,
3104
* the first object that matches either of them (not necessarily both)
3107
* @param aId ID of the hard disk (NULL when unused)
3108
* @param filePathFull full path to the image file (NULL when unused)
3109
* @param aSetError if TRUE, the appropriate error info is set in case when
3110
* the disk is not found and only one search criteria (ID
3111
* or file name) is specified.
3112
* @param aHardDisk where to store the found hard disk object (can be NULL)
3115
* S_OK when found or E_INVALIDARG when not found
3117
* @note Locks objects for reading!
3119
HRESULT VirtualBox::
3120
findVirtualDiskImage (const Guid *aId, const BSTR aFilePathFull,
3121
bool aSetError, ComObjPtr <HVirtualDiskImage> *aImage /* = NULL */)
3123
ComAssertRet (aId || aFilePathFull, E_INVALIDARG);
3125
AutoReaderLock alock (this);
3127
/* first lookup the map by UUID if UUID is provided */
3130
HardDiskMap::const_iterator it = mData.mHardDiskMap.find (*aId);
3131
if (it != mData.mHardDiskMap.end())
3133
AutoReaderLock hdLock ((*it).second);
3134
if ((*it).second->storageType() == HardDiskStorageType_VirtualDiskImage)
3137
*aImage = (*it).second->asVDI();
3143
/* then iterate and find by name */
3147
for (HardDiskMap::const_iterator it = mData.mHardDiskMap.begin();
3148
!found && it != mData.mHardDiskMap.end();
3151
const ComObjPtr <HardDisk> &hd = (*it).second;
3152
AutoReaderLock hdLock (hd);
3153
if (hd->storageType() != HardDiskStorageType_VirtualDiskImage)
3156
found = RTPathCompare (Utf8Str (aFilePathFull),
3157
Utf8Str (hd->asVDI()->filePathFull())) == 0;
3158
if (found && aImage)
3159
*aImage = hd->asVDI();
3163
HRESULT rc = found ? S_OK : E_INVALIDARG;
3165
if (aSetError && !found)
3167
if (aId && !aFilePathFull)
3168
setError (rc, tr ("Could not find a registered VDI hard disk "
3169
"with UUID {%Vuuid}"), aId->raw());
3170
else if (aFilePathFull && !aId)
3171
setError (rc, tr ("Could not find a registered VDI hard disk "
3172
"with the file path '%ls'"), aFilePathFull);
3179
* Searches for a DVDImage object with the given ID or file path in the
3180
* collection of registered DVD images. If both ID and file path are specified,
3181
* the first object that matches either of them (not necessarily both)
3185
* ID of the DVD image (unused when NULL)
3186
* @param aFilePathFull
3187
* full path to the image file (unused when NULL)
3189
* if TRUE, the appropriate error info is set in case when the image is not
3190
* found and only one search criteria (ID or file name) is specified.
3192
* where to store the found DVD image object (can be NULL)
3195
* S_OK when found or E_INVALIDARG when not found
3197
* @note Locks this object for reading.
3199
HRESULT VirtualBox::findDVDImage (const Guid *aId, const BSTR aFilePathFull,
3201
ComObjPtr <DVDImage> *aImage /* = NULL */)
3203
ComAssertRet (aId || aFilePathFull, E_INVALIDARG);
3208
AutoReaderLock alock (this);
3210
for (DVDImageList::const_iterator it = mData.mDVDImages.begin();
3211
!found && it != mData.mDVDImages.end();
3214
/* DVDImage fields are constant, so no need to lock */
3215
found = (aId && (*it)->id() == *aId) ||
3217
RTPathCompare (Utf8Str (aFilePathFull),
3218
Utf8Str ((*it)->filePathFull())) == 0);
3219
if (found && aImage)
3224
HRESULT rc = found ? S_OK : E_INVALIDARG;
3226
if (aSetError && !found)
3228
if (aId && !aFilePathFull)
3229
setError (rc, tr ("Could not find a registered CD/DVD image "
3230
"with UUID {%s}"), aId->toString().raw());
3231
else if (aFilePathFull && !aId)
3232
setError (rc, tr ("Could not find a registered CD/DVD image "
3233
"with the file path '%ls'"), aFilePathFull);
3240
* Searches for a FloppyImage object with the given ID or file path in the
3241
* collection of registered floppy images. If both ID and file path are specified,
3242
* the first object that matches either of them (not necessarily both)
3246
* ID of the floppy image (unused when NULL)
3247
* @param aFilePathFull
3248
* full path to the image file (unused when NULL)
3250
* if TRUE, the appropriate error info is set in case when the image is not
3251
* found and only one search criteria (ID or file name) is specified.
3253
* where to store the found floppy image object (can be NULL)
3256
* S_OK when found or E_INVALIDARG when not found
3258
* @note Locks this object for reading.
3260
HRESULT VirtualBox::findFloppyImage (const Guid *aId, const BSTR aFilePathFull,
3262
ComObjPtr <FloppyImage> *aImage /* = NULL */)
3264
ComAssertRet (aId || aFilePathFull, E_INVALIDARG);
3269
AutoReaderLock alock (this);
3271
for (FloppyImageList::iterator it = mData.mFloppyImages.begin();
3272
!found && it != mData.mFloppyImages.end();
3275
/* FloppyImage fields are constant, so no need to lock */
3276
found = (aId && (*it)->id() == *aId) ||
3278
RTPathCompare (Utf8Str (aFilePathFull),
3279
Utf8Str ((*it)->filePathFull())) == 0);
3280
if (found && aImage)
3285
HRESULT rc = found ? S_OK : E_INVALIDARG;
3287
if (aSetError && !found)
3289
if (aId && !aFilePathFull)
3290
setError (rc, tr ("Could not find a registered floppy image "
3291
"with UUID {%s}"), aId->toString().raw());
3292
else if (aFilePathFull && !aId)
3293
setError (rc, tr ("Could not find a registered floppy image "
3294
"with the file path '%ls'"), aFilePathFull);
3301
* When \a aHardDisk is not NULL, searches for an object equal to the given
3302
* hard disk in the collection of registered hard disks, or, if the given hard
3303
* disk is HVirtualDiskImage, for an object with the given file path in the
3304
* collection of all registered non-hard disk images (DVDs and floppies).
3305
* Other parameters are unused.
3307
* When \a aHardDisk is NULL, searches for an object with the given ID or file
3308
* path in the collection of all registered images (VDIs, DVDs and floppies).
3309
* If both ID and file path are specified, matching either of them will satisfy
3312
* If a matching object is found, this method returns E_INVALIDARG and sets the
3313
* appropriate error info. Otherwise, S_OK is returned.
3315
* @param aHardDisk hard disk object to check against registered media
3316
* (NULL when unused)
3317
* @param aId UUID of the media to check (NULL when unused)
3318
* @param aFilePathFull full path to the image file (NULL when unused)
3320
* @note Locks objects!
3322
HRESULT VirtualBox::checkMediaForConflicts (HardDisk *aHardDisk,
3324
const BSTR aFilePathFull)
3326
AssertReturn (aHardDisk || aId || aFilePathFull, E_FAIL);
3330
AutoReaderLock alock (this);
3334
for (HardDiskMap::const_iterator it = mData.mHardDiskMap.begin();
3335
it != mData.mHardDiskMap.end();
3338
const ComObjPtr <HardDisk> &hd = (*it).second;
3339
if (hd->sameAs (aHardDisk))
3340
return setError (E_INVALIDARG,
3341
tr ("A hard disk with UUID {%Vuuid} or with the same properties "
3342
"('%ls') is already registered"),
3343
aHardDisk->id().raw(), aHardDisk->toString().raw());
3346
aId = &aHardDisk->id();
3347
if (aHardDisk->storageType() == HardDiskStorageType_VirtualDiskImage)
3348
#if !defined (VBOX_WITH_XPCOM)
3349
#if defined(RT_OS_WINDOWS)
3350
/// @todo (dmik) stupid BSTR declaration lacks the BCSTR counterpart
3351
const_cast <BSTR> (aFilePathFull) = aHardDisk->asVDI()->filePathFull();
3354
aFilePathFull = aHardDisk->asVDI()->filePathFull();
3360
if (aId || aFilePathFull) do
3364
rc = findHardDisk (aId, aFilePathFull, false /* aSetError */);
3365
found = SUCCEEDED (rc);
3370
rc = findDVDImage (aId, aFilePathFull, false /* aSetError */);
3371
found = SUCCEEDED (rc);
3375
rc = findFloppyImage (aId, aFilePathFull, false /* aSetError */);
3376
found = SUCCEEDED (rc);
3384
if (aId && !aFilePathFull)
3385
rc = setError (E_INVALIDARG,
3386
tr ("A disk image with UUID {%Vuuid} is already registered"),
3388
else if (aFilePathFull && !aId)
3389
rc = setError (E_INVALIDARG,
3390
tr ("A disk image with file path '%ls' is already registered"),
3393
rc = setError (E_INVALIDARG,
3394
tr ("A disk image with UUID {%Vuuid} or file path '%ls' "
3395
"is already registered"), aId->raw(), aFilePathFull);
3404
* Reads in the machine definitions from the configuration loader
3405
* and creates the relevant objects.
3407
* @note Can be called only from #init().
3408
* @note Doesn't lock anything.
3410
HRESULT VirtualBox::loadMachines (CFGNODE aGlobal)
3412
AutoCaller autoCaller (this);
3413
AssertReturn (autoCaller.state() == InInit, E_FAIL);
3416
CFGNODE machineRegistry = 0;
3419
CFGLDRGetChildNode (aGlobal, "MachineRegistry", 0, &machineRegistry);
3420
Assert (machineRegistry);
3422
CFGLDRCountChildren(machineRegistry, "MachineEntry", &count);
3423
for (unsigned i = 0; i < count && SUCCEEDED (rc); i++)
3426
CFGLDRGetChildNode(machineRegistry, "MachineEntry", i, &vm);
3429
CFGLDRQueryUUID(vm, "uuid", uuid.ptr());
3430
/* get the machine configuration file name */
3432
CFGLDRQueryBSTR(vm, "src", src.asOutParam());
3434
/* create a new object */
3435
ComObjPtr <Machine> machine;
3436
rc = machine.createObject();
3439
/* initialize the machine object and register it */
3440
rc = machine->init (this, src, Machine::Init_Registered,
3441
NULL, FALSE, &uuid);
3443
rc = registerMachine (machine);
3446
CFGLDRReleaseNode(vm);
3449
CFGLDRReleaseNode(machineRegistry);
3455
* Reads in the disk registration entries from the global settings file
3456
* and creates the relevant objects
3458
* @param aGlobal <Global> node
3460
* @note Can be called only from #init().
3461
* @note Doesn't lock anything.
3463
HRESULT VirtualBox::loadDisks (CFGNODE aGlobal)
3465
AutoCaller autoCaller (this);
3466
AssertReturn (autoCaller.state() == InInit, E_FAIL);
3469
CFGNODE registryNode = 0;
3471
CFGLDRGetChildNode (aGlobal, "DiskRegistry", 0, ®istryNode);
3472
ComAssertRet (registryNode, E_FAIL);
3474
const char *ImagesNodes[] = { "HardDisks", "DVDImages", "FloppyImages" };
3476
for (size_t node = 0; node < ELEMENTS (ImagesNodes) && SUCCEEDED (rc); node ++)
3478
CFGNODE imagesNode = 0;
3479
CFGLDRGetChildNode (registryNode, ImagesNodes [node], 0, &imagesNode);
3481
// all three nodes are optional
3485
if (node == 0) // HardDisks node
3486
rc = loadHardDisks (imagesNode);
3490
CFGLDRCountChildren (imagesNode, "Image", &count);
3491
for (unsigned i = 0; i < count && SUCCEEDED (rc); ++ i)
3493
CFGNODE imageNode = 0;
3494
CFGLDRGetChildNode (imagesNode, "Image", i, &imageNode);
3495
ComAssertBreak (imageNode, rc = E_FAIL);
3497
Guid uuid; // uuid (required)
3498
CFGLDRQueryUUID (imageNode, "uuid", uuid.ptr());
3499
Bstr src; // source (required)
3500
CFGLDRQueryBSTR (imageNode, "src", src.asOutParam());
3504
case 1: // DVDImages
3506
ComObjPtr <DVDImage> dvdImage;
3507
dvdImage.createObject();
3508
rc = dvdImage->init (this, src, TRUE /* isRegistered */, uuid);
3510
rc = registerDVDImage (dvdImage, TRUE /* aOnStartUp */);
3514
case 2: // FloppyImages
3516
ComObjPtr <FloppyImage> floppyImage;
3517
floppyImage.createObject();
3518
rc = floppyImage->init (this, src, TRUE /* isRegistered */, uuid);
3520
rc = registerFloppyImage (floppyImage, TRUE /* aOnStartUp */);
3528
CFGLDRReleaseNode (imageNode);
3532
CFGLDRReleaseNode (imagesNode);
3535
CFGLDRReleaseNode (registryNode);
3541
* Loads all hard disks from the given <HardDisks> node.
3542
* Note that all loaded hard disks register themselves within this VirtualBox.
3544
* @param aNode <HardDisks> node
3546
* @note Can be called only from #init().
3547
* @note Doesn't lock anything.
3549
HRESULT VirtualBox::loadHardDisks (CFGNODE aNode)
3551
AutoCaller autoCaller (this);
3552
AssertReturn (autoCaller.state() == InInit, E_FAIL);
3554
AssertReturn (aNode, E_INVALIDARG);
3559
CFGLDRCountChildren (aNode, "HardDisk", &count);
3560
for (unsigned i = 0; i < count && SUCCEEDED (rc); ++ i)
3564
CFGLDRGetChildNode (aNode, "HardDisk", i, &hdNode);
3565
ComAssertBreak (hdNode, rc = E_FAIL);
3568
CFGNODE storageNode = 0;
3570
// detect the type of the hard disk
3571
// (either one of HVirtualDiskImage, HISCSIHardDisk or HPhysicalVolume
3574
CFGLDRGetChildNode (hdNode, "VirtualDiskImage", 0, &storageNode);
3577
ComObjPtr <HVirtualDiskImage> vdi;
3579
rc = vdi->init (this, NULL, hdNode, storageNode);
3583
CFGLDRGetChildNode (hdNode, "ISCSIHardDisk", 0, &storageNode);
3586
ComObjPtr <HISCSIHardDisk> iscsi;
3587
iscsi.createObject();
3588
rc = iscsi->init (this, hdNode, storageNode);
3592
CFGLDRGetChildNode (hdNode, "VMDKImage", 0, &storageNode);
3595
ComObjPtr <HVMDKImage> vmdk;
3596
vmdk.createObject();
3597
rc = vmdk->init (this, NULL, hdNode, storageNode);
3601
/// @todo (dmik) later
3602
// CFGLDRGetChildNode (hdNode, "PhysicalVolume", 0, &storageNode);
3608
ComAssertMsgFailedBreak (("No valid hard disk storage node!\n"),
3614
CFGLDRReleaseNode (storageNode);
3617
CFGLDRReleaseNode (hdNode);
3624
* Helper function to write out the configuration to XML.
3626
* @note Locks objects!
3628
HRESULT VirtualBox::saveConfig()
3630
AutoCaller autoCaller (this);
3631
AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3633
ComAssertRet (!!mData.mCfgFile.mName, E_FAIL);
3637
AutoLock alock (this);
3639
CFGHANDLE configLoader;
3641
/* load the config file */
3642
int vrc = CFGLDRLoad (&configLoader, Utf8Str (mData.mCfgFile.mName),
3643
mData.mCfgFile.mHandle,
3644
XmlSchemaNS, true, cfgLdrEntityResolver, NULL);
3645
ComAssertRCRet (vrc, E_FAIL);
3647
const char * Global = "VirtualBox/Global";
3650
vrc = CFGLDRGetNode(configLoader, Global, 0, &global);
3651
if (VBOX_FAILURE (vrc))
3652
CFGLDRCreateNode (configLoader, Global, &global);
3656
ComAssertBreak (global, rc = E_FAIL);
3661
const char *Registry = "MachineRegistry";
3662
const char *Entry = "MachineEntry";
3663
CFGNODE registryNode = NULL;
3665
/* first, delete the entire machine registry */
3666
if (VBOX_SUCCESS (CFGLDRGetChildNode (global, Registry, 0, ®istryNode)))
3667
CFGLDRDeleteNode (registryNode);
3669
/* then, recreate it */
3670
CFGLDRCreateChildNode (global, Registry, ®istryNode);
3672
/* write out the machines */
3673
for (MachineList::iterator it = mData.mMachines.begin();
3674
it != mData.mMachines.end();
3677
/// @todo (dmik) move this part to Machine for better incapsulation
3679
ComObjPtr <Machine> m = *it;
3680
AutoReaderLock machineLock (m);
3682
AssertMsg (m->data(), ("Machine data must not be NULL"));
3684
CFGLDRAppendChildNode (registryNode, Entry, &entryNode);
3685
/* UUID + curly brackets */
3686
CFGLDRSetUUID (entryNode, "uuid", unconst (m->data()->mUuid).ptr());
3688
CFGLDRSetBSTR (entryNode, "src", m->data()->mConfigFile);
3690
CFGLDRReleaseNode (entryNode);
3693
CFGLDRReleaseNode (registryNode);
3702
CFGNODE registryNode = 0;
3703
CFGLDRGetChildNode (global, "DiskRegistry", 0, ®istryNode);
3704
/* first, delete the entire disk image registr */
3706
CFGLDRDeleteNode (registryNode);
3707
/* then, recreate it */
3708
CFGLDRCreateChildNode (global, "DiskRegistry", ®istryNode);
3709
ComAssertBreak (registryNode, rc = E_FAIL);
3711
/* write out the hard disks */
3713
CFGNODE imagesNode = 0;
3714
CFGLDRCreateChildNode (registryNode, "HardDisks", &imagesNode);
3715
rc = saveHardDisks (imagesNode);
3716
CFGLDRReleaseNode (imagesNode);
3721
/* write out the CD/DVD images */
3723
CFGNODE imagesNode = 0;
3724
CFGLDRCreateChildNode (registryNode, "DVDImages", &imagesNode);
3726
for (DVDImageList::iterator it = mData.mDVDImages.begin();
3727
it != mData.mDVDImages.end();
3730
ComObjPtr <DVDImage> dvd = *it;
3731
/* no need to lock: fields are constant */
3732
CFGNODE imageNode = 0;
3733
CFGLDRAppendChildNode (imagesNode, "Image", &imageNode);
3734
CFGLDRSetUUID (imageNode, "uuid", dvd->id());
3735
CFGLDRSetBSTR (imageNode, "src", dvd->filePath());
3736
CFGLDRReleaseNode (imageNode);
3739
CFGLDRReleaseNode (imagesNode);
3742
/* write out the floppy images */
3744
CFGNODE imagesNode = 0;
3745
CFGLDRCreateChildNode (registryNode, "FloppyImages", &imagesNode);
3747
for (FloppyImageList::iterator it = mData.mFloppyImages.begin();
3748
it != mData.mFloppyImages.end();
3751
ComObjPtr <FloppyImage> fd = *it;
3752
/* no need to lock: fields are constant */
3753
CFGNODE imageNode = 0;
3754
CFGLDRAppendChildNode (imagesNode, "Image", &imageNode);
3755
CFGLDRSetUUID (imageNode, "uuid", fd->id());
3756
CFGLDRSetBSTR (imageNode, "src", fd->filePath());
3757
CFGLDRReleaseNode (imageNode);
3760
CFGLDRReleaseNode (imagesNode);
3763
CFGLDRReleaseNode (registryNode);
3771
/* host data (USB filters) */
3772
rc = mData.mHost->saveSettings (global);
3776
rc = mData.mSystemProperties->saveSettings (global);
3785
CFGLDRReleaseNode (global);
3789
char *loaderError = NULL;
3790
vrc = CFGLDRSave (configLoader, &loaderError);
3791
if (VBOX_FAILURE (vrc))
3793
rc = setError (E_FAIL,
3794
tr ("Could not save the settings file '%ls' (%Vrc)%s%s"),
3795
mData.mCfgFile.mName.raw(), vrc,
3796
loaderError ? ".\n" : "", loaderError ? loaderError : "");
3798
RTMemTmpFree (loaderError);
3802
CFGLDRFree(configLoader);
3808
* Saves all hard disks to the given <HardDisks> node.
3810
* @param aNode <HardDisks> node
3812
* @note Locks this object for reding.
3814
HRESULT VirtualBox::saveHardDisks (CFGNODE aNode)
3816
AssertReturn (aNode, E_INVALIDARG);
3820
AutoReaderLock alock (this);
3822
for (HardDiskList::const_iterator it = mData.mHardDisks.begin();
3823
it != mData.mHardDisks.end() && SUCCEEDED (rc);
3826
ComObjPtr <HardDisk> hd = *it;
3827
AutoReaderLock hdLock (hd);
3830
CFGLDRAppendChildNode (aNode, "HardDisk", &hdNode);
3831
ComAssertBreak (hdNode, rc = E_FAIL);
3833
CFGNODE storageNode = 0;
3835
switch (hd->storageType())
3837
case HardDiskStorageType_VirtualDiskImage:
3839
CFGLDRAppendChildNode (hdNode, "VirtualDiskImage", &storageNode);
3840
ComAssertBreak (storageNode, rc = E_FAIL);
3841
rc = hd->saveSettings (hdNode, storageNode);
3845
case HardDiskStorageType_ISCSIHardDisk:
3847
CFGLDRAppendChildNode (hdNode, "ISCSIHardDisk", &storageNode);
3848
ComAssertBreak (storageNode, rc = E_FAIL);
3849
rc = hd->saveSettings (hdNode, storageNode);
3853
case HardDiskStorageType_VMDKImage:
3855
CFGLDRAppendChildNode (hdNode, "VMDKImage", &storageNode);
3856
ComAssertBreak (storageNode, rc = E_FAIL);
3857
rc = hd->saveSettings (hdNode, storageNode);
3861
/// @todo (dmik) later
3862
// case HardDiskStorageType_PhysicalVolume:
3869
CFGLDRReleaseNode (storageNode);
3871
CFGLDRReleaseNode (hdNode);
3878
* Helper to register the machine.
3880
* When called during VirtualBox startup, adds the given machine to the
3881
* collection of registered machines. Otherwise tries to mark the machine
3882
* as registered, and, if succeeded, adds it to the collection and
3883
* saves global settings.
3885
* @param aMachine machine to register
3887
* @note Locks objects!
3889
HRESULT VirtualBox::registerMachine (Machine *aMachine)
3891
ComAssertRet (aMachine, E_INVALIDARG);
3893
AutoCaller autoCaller (this);
3894
CheckComRCReturnRC (autoCaller.rc());
3896
AutoLock alock (this);
3898
ComAssertRet (findMachine (aMachine->data()->mUuid,
3899
false /* aDoSetError */, NULL) == E_INVALIDARG,
3904
if (autoCaller.state() != InInit)
3906
/* Machine::trySetRegistered() will commit and save machine settings */
3907
rc = aMachine->trySetRegistered (TRUE);
3908
CheckComRCReturnRC (rc);
3911
/* add to the collection of registered machines */
3912
mData.mMachines.push_back (aMachine);
3914
if (autoCaller.state() != InInit)
3921
* Helper to register the hard disk.
3923
* @param aHardDisk object to register
3924
* @param aFlags one of RHD_* values
3926
* @note Locks objects!
3928
HRESULT VirtualBox::registerHardDisk (HardDisk *aHardDisk, RHD_Flags aFlags)
3930
ComAssertRet (aHardDisk, E_INVALIDARG);
3932
AutoCaller autoCaller (this);
3933
AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3935
AutoLock alock (this);
3937
HRESULT rc = checkMediaForConflicts (aHardDisk, NULL, NULL);
3938
CheckComRCReturnRC (rc);
3940
/* mark the hard disk as registered only when registration is external */
3941
if (aFlags == RHD_External)
3943
rc = aHardDisk->trySetRegistered (TRUE);
3944
CheckComRCReturnRC (rc);
3947
if (!aHardDisk->parent())
3949
/* add to the collection of top-level images */
3950
mData.mHardDisks.push_back (aHardDisk);
3953
/* insert to the map of hard disks */
3955
.insert (HardDiskMap::value_type (aHardDisk->id(), aHardDisk));
3957
/* save global config file if not on startup */
3958
/// @todo (dmik) optimize later to save only the <HardDisks> node
3959
if (aFlags != RHD_OnStartUp)
3966
* Helper to unregister the hard disk.
3968
* If the hard disk is a differencing hard disk and if the unregistration
3969
* succeeds, the hard disk image is deleted and the object is uninitialized.
3971
* @param aHardDisk hard disk to unregister
3973
* @note Locks objects!
3975
HRESULT VirtualBox::unregisterHardDisk (HardDisk *aHardDisk)
3977
AssertReturn (aHardDisk, E_INVALIDARG);
3979
AutoCaller autoCaller (this);
3980
AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3982
LogFlowThisFunc (("image='%ls'\n", aHardDisk->toString().raw()));
3984
AutoLock alock (this);
3986
/* Lock the hard disk to ensure nobody registers it again before we delete
3987
* the differencing image (sanity check actually -- should never happen). */
3988
AutoLock hdLock (aHardDisk);
3990
/* try to unregister */
3991
HRESULT rc = aHardDisk->trySetRegistered (FALSE);
3992
CheckComRCReturnRC (rc);
3994
/* remove from the map of hard disks */
3995
mData.mHardDiskMap.erase (aHardDisk->id());
3997
if (!aHardDisk->parent())
3999
/* non-differencing hard disk:
4000
* remove from the collection of top-level hard disks */
4001
mData.mHardDisks.remove (aHardDisk);
4005
Assert (aHardDisk->isDifferencing());
4007
/* differencing hard disk: delete (only if the last access check
4008
* succeeded) and uninitialize */
4009
if (aHardDisk->asVDI()->lastAccessError().isNull())
4010
rc = aHardDisk->asVDI()->DeleteImage();
4011
aHardDisk->uninit();
4014
/* save the global config file anyway (already unregistered) */
4015
/// @todo (dmik) optimize later to save only the <HardDisks> node
4016
HRESULT rc2 = saveConfig();
4024
* Helper to unregister the differencing hard disk image.
4025
* Resets machine ID of the hard disk (to let the unregistration succeed)
4026
* and then calls #unregisterHardDisk().
4028
* @param aHardDisk differencing hard disk image to unregister
4030
* @note Locks objects!
4032
HRESULT VirtualBox::unregisterDiffHardDisk (HardDisk *aHardDisk)
4034
AssertReturn (aHardDisk, E_INVALIDARG);
4036
AutoCaller autoCaller (this);
4037
AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4039
AutoLock alock (this);
4042
* Note: it's safe to lock aHardDisk here because the same object
4043
* will be locked by #unregisterHardDisk().
4045
AutoLock hdLock (aHardDisk);
4047
AssertReturn (aHardDisk->isDifferencing(), E_INVALIDARG);
4050
* deassociate the machine from the hard disk
4051
* (otherwise trySetRegistered() will definitely fail)
4053
aHardDisk->setMachineId (Guid());
4055
return unregisterHardDisk (aHardDisk);
4060
* Helper to update the global settings file when the name of some machine
4061
* changes so that file and directory renaming occurs. This method ensures
4062
* that all affected paths in the disk registry are properly updated.
4064
* @param aOldPath old path (full)
4065
* @param aNewPath new path (full)
4067
* @note Locks this object + DVD, Floppy and HardDisk children for writing.
4069
HRESULT VirtualBox::updateSettings (const char *aOldPath, const char *aNewPath)
4071
LogFlowThisFunc (("aOldPath={%s} aNewPath={%s}\n", aOldPath, aNewPath));
4073
AssertReturn (aOldPath, E_INVALIDARG);
4074
AssertReturn (aNewPath, E_INVALIDARG);
4076
AutoCaller autoCaller (this);
4077
AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4079
AutoLock alock (this);
4081
size_t oldPathLen = strlen (aOldPath);
4083
/* check DVD paths */
4084
for (DVDImageList::iterator it = mData.mDVDImages.begin();
4085
it != mData.mDVDImages.end();
4088
ComObjPtr <DVDImage> image = *it;
4090
/* no need to lock: fields are constant */
4091
Utf8Str path = image->filePathFull();
4092
LogFlowThisFunc (("DVD.fullPath={%s}\n", path.raw()));
4094
if (RTPathStartsWith (path, aOldPath))
4096
Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
4097
path.raw() + oldPathLen);
4099
calculateRelativePath (path, path);
4100
image->updatePath (newPath, path);
4102
LogFlowThisFunc (("-> updated: full={%s} rel={%s}\n",
4103
newPath.raw(), path.raw()));
4107
/* check Floppy paths */
4108
for (FloppyImageList::iterator it = mData.mFloppyImages.begin();
4109
it != mData.mFloppyImages.end();
4112
ComObjPtr <FloppyImage> image = *it;
4114
/* no need to lock: fields are constant */
4115
Utf8Str path = image->filePathFull();
4116
LogFlowThisFunc (("Floppy.fullPath={%s}\n", path.raw()));
4118
if (RTPathStartsWith (path, aOldPath))
4120
Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
4121
path.raw() + oldPathLen);
4123
calculateRelativePath (path, path);
4124
image->updatePath (newPath, path);
4126
LogFlowThisFunc (("-> updated: full={%s} rel={%s}\n",
4127
newPath.raw(), path.raw()));
4131
/* check HardDisk paths */
4132
for (HardDiskList::const_iterator it = mData.mHardDisks.begin();
4133
it != mData.mHardDisks.end();
4136
(*it)->updatePaths (aOldPath, aNewPath);
4139
HRESULT rc = saveConfig();
4145
* Helper to register the DVD image.
4147
* @param aImage object to register
4148
* @param aOnStartUp whether this method called during VirtualBox init or not
4150
* @return COM status code
4152
* @note Locks objects!
4154
HRESULT VirtualBox::registerDVDImage (DVDImage *aImage, bool aOnStartUp)
4156
AssertReturn (aImage, E_INVALIDARG);
4158
AutoCaller autoCaller (this);
4159
AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4161
AutoLock alock (this);
4163
HRESULT rc = checkMediaForConflicts (NULL, &aImage->id(),
4164
aImage->filePathFull());
4165
CheckComRCReturnRC (rc);
4167
/* add to the collection */
4168
mData.mDVDImages.push_back (aImage);
4170
/* save global config file if we're supposed to */
4178
* Helper to register the floppy image.
4180
* @param aImage object to register
4181
* @param aOnStartUp whether this method called during VirtualBox init or not
4183
* @return COM status code
4185
* @note Locks objects!
4187
HRESULT VirtualBox::registerFloppyImage (FloppyImage *aImage, bool aOnStartUp)
4189
AssertReturn (aImage, E_INVALIDARG);
4191
AutoCaller autoCaller (this);
4192
AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4194
AutoLock alock (this);
4196
HRESULT rc = checkMediaForConflicts (NULL, &aImage->id(),
4197
aImage->filePathFull());
4198
CheckComRCReturnRC (rc);
4200
/* add to the collection */
4201
mData.mFloppyImages.push_back (aImage);
4203
/* save global config file if we're supposed to */
4211
* Helper function to create the guest OS type objects and our collection
4213
* @returns COM status code
4215
HRESULT VirtualBox::registerGuestOSTypes()
4217
AutoCaller autoCaller (this);
4218
AssertComRCReturn (autoCaller.rc(), E_FAIL);
4219
AssertReturn (autoCaller.state() == InInit, E_FAIL);
4223
// this table represents our os type / string mapping
4226
const char *id; // utf-8
4227
const char *description; // utf-8
4228
const OSType osType;
4229
const uint32_t recommendedRAM;
4230
const uint32_t recommendedVRAM;
4231
const uint32_t recommendedHDD;
4234
/// @todo (dmik) get the list of OS types from the XML schema
4235
/* NOTE1: we assume that unknown is always the first entry!
4236
* NOTE2: please use powers of 2 when specifying the size of harddisks since
4237
* '2GB' looks better than '1.95GB' (= 2000MB) */
4238
{ "unknown", tr ("Other/Unknown"), OSTypeUnknown, 64, 4, 2 * _1K },
4239
{ "dos", "DOS", OSTypeDOS, 32, 4, 512 },
4240
{ "win31", "Windows 3.1", OSTypeWin31, 32, 4, 1 * _1K },
4241
{ "win95", "Windows 95", OSTypeWin95, 64, 4, 2 * _1K },
4242
{ "win98", "Windows 98", OSTypeWin98, 64, 4, 2 * _1K },
4243
{ "winme", "Windows Me", OSTypeWinMe, 64, 4, 4 * _1K },
4244
{ "winnt4", "Windows NT 4", OSTypeWinNT4, 128, 4, 2 * _1K },
4245
{ "win2k", "Windows 2000", OSTypeWin2k, 168, 4, 4 * _1K },
4246
{ "winxp", "Windows XP", OSTypeWinXP, 192, 4, 10 * _1K },
4247
{ "win2k3", "Windows Server 2003", OSTypeWin2k3, 256, 4, 20 * _1K },
4248
{ "winvista", "Windows Vista", OSTypeWinVista, 512, 4, 20 * _1K },
4249
{ "os2warp3", "OS/2 Warp 3", OSTypeOS2Warp3, 48, 4, 1 * _1K },
4250
{ "os2warp4", "OS/2 Warp 4", OSTypeOS2Warp4, 64, 4, 2 * _1K },
4251
{ "os2warp45", "OS/2 Warp 4.5", OSTypeOS2Warp45, 96, 4, 2 * _1K },
4252
{ "linux22", "Linux 2.2", OSTypeLinux22, 64, 4, 2 * _1K },
4253
{ "linux24", "Linux 2.4", OSTypeLinux24, 128, 4, 4 * _1K },
4254
{ "linux26", "Linux 2.6", OSTypeLinux26, 256, 4, 8 * _1K },
4255
{ "freebsd", "FreeBSD", OSTypeFreeBSD, 64, 4, 2 * _1K },
4256
{ "openbsd", "OpenBSD", OSTypeOpenBSD, 64, 4, 2 * _1K },
4257
{ "netbsd", "NetBSD", OSTypeNetBSD, 64, 4, 2 * _1K },
4258
{ "netware", "Netware", OSTypeNetware, 128, 4, 4 * _1K },
4259
{ "solaris", "Solaris", OSTypeSolaris, 128, 4, 8 * _1K },
4260
{ "l4", "L4", OSTypeL4, 64, 4, 2 * _1K }
4263
for (uint32_t i = 0; i < ELEMENTS (OSTypes) && SUCCEEDED (rc); i++)
4265
ComObjPtr <GuestOSType> guestOSTypeObj;
4266
rc = guestOSTypeObj.createObject();
4269
rc = guestOSTypeObj->init (OSTypes[i].id,
4270
OSTypes[i].description,
4272
OSTypes[i].recommendedRAM,
4273
OSTypes[i].recommendedVRAM,
4274
OSTypes[i].recommendedHDD);
4276
mData.mGuestOSTypes.push_back (guestOSTypeObj);
4284
* Helper to lock the VirtualBox configuration for write access.
4286
* @note This method is not thread safe (must be called only from #init()
4289
* @note If the configuration file is not found, the method returns
4290
* S_OK, but subsequent #isConfigLocked() will return FALSE. This is used
4291
* in some places to determine the (valid) situation when no config file
4292
* exists yet, and therefore a new one should be created from scatch.
4294
HRESULT VirtualBox::lockConfig()
4296
AutoCaller autoCaller (this);
4297
AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4298
AssertReturn (autoCaller.state() == InInit, E_FAIL);
4302
Assert (!isConfigLocked());
4303
if (!isConfigLocked())
4305
/* open the associated config file */
4306
int vrc = RTFileOpen (&mData.mCfgFile.mHandle,
4307
Utf8Str (mData.mCfgFile.mName),
4308
RTFILE_O_READWRITE | RTFILE_O_OPEN |
4309
RTFILE_O_DENY_WRITE);
4310
if (VBOX_FAILURE (vrc))
4312
mData.mCfgFile.mHandle = NIL_RTFILE;
4315
* It is ok if the file is not found, it will be created by
4316
* init(). Otherwise return an error.
4318
if (vrc != VERR_FILE_NOT_FOUND)
4319
rc = setError (E_FAIL,
4320
tr ("Could not lock the settings file '%ls' (%Vrc)"),
4321
mData.mCfgFile.mName.raw(), vrc);
4324
LogFlowThisFunc (("mCfgFile.mName='%ls', mCfgFile.mHandle=%d, rc=%08X\n",
4325
mData.mCfgFile.mName.raw(), mData.mCfgFile.mHandle, rc));
4332
* Helper to unlock the VirtualBox configuration from write access.
4334
* @note This method is not thread safe (must be called only from #init()
4337
HRESULT VirtualBox::unlockConfig()
4339
AutoCaller autoCaller (this);
4340
AssertComRCReturn (autoCaller.rc(), E_FAIL);
4341
AssertReturn (autoCaller.state() == InUninit, E_FAIL);
4345
if (isConfigLocked())
4347
RTFileFlush (mData.mCfgFile.mHandle);
4348
RTFileClose (mData.mCfgFile.mHandle);
4349
/** @todo flush the directory too. */
4350
mData.mCfgFile.mHandle = NIL_RTFILE;
4351
LogFlowThisFunc (("\n"));
4358
* Thread function that watches the termination of all client processes
4359
* that have opened sessions using IVirtualBox::OpenSession()
4362
DECLCALLBACK(int) VirtualBox::ClientWatcher (RTTHREAD thread, void *pvUser)
4366
VirtualBox *that = (VirtualBox *) pvUser;
4369
SessionMachineVector machines;
4372
#if defined(RT_OS_WINDOWS)
4374
HRESULT hrc = CoInitializeEx (NULL,
4375
COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
4376
COINIT_SPEED_OVER_MEMORY);
4379
/// @todo (dmik) processes reaping!
4381
HANDLE *handles = new HANDLE [1];
4382
handles [0] = that->mWatcherData.mUpdateReq;
4386
AutoCaller autoCaller (that);
4387
/* VirtualBox has been early uninitialized, terminate */
4388
if (!autoCaller.isOk())
4393
/* release the caller to let uninit() ever proceed */
4394
autoCaller.release();
4396
DWORD rc = ::WaitForMultipleObjects (cnt + 1, handles, FALSE, INFINITE);
4399
* Restore the caller before using VirtualBox. If it fails, this
4400
* means VirtualBox is being uninitialized and we must terminate.
4403
if (!autoCaller.isOk())
4406
bool update = false;
4407
if (rc == WAIT_OBJECT_0)
4409
/* update event is signaled */
4412
else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
4414
/* machine mutex is released */
4415
(machines [rc - WAIT_OBJECT_0 - 1])->checkForDeath();
4418
else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
4420
/* machine mutex is abandoned due to client process termination */
4421
(machines [rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
4426
/* obtain a new set of opened machines */
4427
that->getOpenedMachines (machines);
4428
cnt = machines.size();
4429
LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4430
AssertMsg ((cnt + 1) <= MAXIMUM_WAIT_OBJECTS,
4431
("MAXIMUM_WAIT_OBJECTS reached"));
4432
/* renew the set of event handles */
4434
handles = new HANDLE [cnt + 1];
4435
handles [0] = that->mWatcherData.mUpdateReq;
4436
for (size_t i = 0; i < cnt; ++ i)
4437
handles [i + 1] = (machines [i])->ipcSem();
4444
/* delete the set of event handles */
4447
/* delete the set of opened machines if any */
4452
#elif defined (RT_OS_OS2)
4454
/// @todo (dmik) processes reaping!
4456
/* according to PMREF, 64 is the maximum for the muxwait list */
4457
SEMRECORD handles [64];
4459
HMUX muxSem = NULLHANDLE;
4463
AutoCaller autoCaller (that);
4464
/* VirtualBox has been early uninitialized, terminate */
4465
if (!autoCaller.isOk())
4470
/* release the caller to let uninit() ever proceed */
4471
autoCaller.release();
4473
int vrc = RTSemEventWait (that->mWatcherData.mUpdateReq, 500);
4476
* Restore the caller before using VirtualBox. If it fails, this
4477
* means VirtualBox is being uninitialized and we must terminate.
4480
if (!autoCaller.isOk())
4483
bool update = false;
4485
if (VBOX_SUCCESS (vrc))
4487
/* update event is signaled */
4492
AssertMsg (vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
4493
("RTSemEventWait returned %Vrc\n", vrc));
4495
/* are there any mutexes? */
4498
/* figure out what's going on with machines */
4500
unsigned long semId = 0;
4501
APIRET arc = ::DosWaitMuxWaitSem (muxSem,
4502
SEM_IMMEDIATE_RETURN, &semId);
4504
if (arc == NO_ERROR)
4506
/* machine mutex is normally released */
4507
Assert (semId >= 0 && semId < cnt);
4508
if (semId >= 0 && semId < cnt)
4509
machines [semId]->checkForDeath();
4512
else if (arc == ERROR_SEM_OWNER_DIED)
4514
/* machine mutex is abandoned due to client process
4515
* termination; find which mutex is in the Owner Died
4517
for (size_t i = 0; i < cnt; ++ i)
4520
unsigned long reqCnt;
4521
arc = DosQueryMutexSem ((HMTX) handles [i].hsemCur, &pid,
4523
if (arc == ERROR_SEM_OWNER_DIED)
4525
/* close the dead mutex as asked by PMREF */
4526
::DosCloseMutexSem ((HMTX) handles [i].hsemCur);
4528
Assert (i >= 0 && i < cnt);
4529
if (i >= 0 && i < cnt)
4530
machines [i]->checkForDeath();
4536
AssertMsg (arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
4537
("DosWaitMuxWaitSem returned %d\n", arc));
4543
/* close the old muxsem */
4544
if (muxSem != NULLHANDLE)
4545
::DosCloseMuxWaitSem (muxSem);
4546
/* obtain a new set of opened machines */
4547
that->getOpenedMachines (machines);
4548
cnt = machines.size();
4549
LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4550
/// @todo use several muxwait sems if cnt is > 64
4551
AssertMsg (cnt <= 64 /* according to PMREF */,
4552
("maximum of 64 mutex semaphores reached (%d)", cnt));
4557
/* renew the set of event handles */
4558
for (size_t i = 0; i < cnt; ++ i)
4560
handles [i].hsemCur = (HSEM) machines [i]->ipcSem();
4561
handles [i].ulUser = i;
4563
/* create a new muxsem */
4564
APIRET arc = ::DosCreateMuxWaitSem (NULL, &muxSem, cnt, handles,
4566
AssertMsg (arc == NO_ERROR,
4567
("DosCreateMuxWaitSem returned %d\n", arc));
4575
/* close the muxsem */
4576
if (muxSem != NULLHANDLE)
4577
::DosCloseMuxWaitSem (muxSem);
4579
/* delete the set of opened machines if any */
4582
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
4584
bool need_update = false;
4588
AutoCaller autoCaller (that);
4589
if (!autoCaller.isOk())
4594
/* release the caller to let uninit() ever proceed */
4595
autoCaller.release();
4597
int rc = RTSemEventWait (that->mWatcherData.mUpdateReq, 500);
4600
* Restore the caller before using VirtualBox. If it fails, this
4601
* means VirtualBox is being uninitialized and we must terminate.
4604
if (!autoCaller.isOk())
4607
if (VBOX_SUCCESS (rc) || need_update)
4609
/* VBOX_SUCCESS (rc) means an update event is signaled */
4611
/* obtain a new set of opened machines */
4612
that->getOpenedMachines (machines);
4613
cnt = machines.size();
4614
LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4617
need_update = false;
4618
for (size_t i = 0; i < cnt; ++ i)
4619
need_update |= (machines [i])->checkForDeath();
4621
/* reap child processes */
4623
AutoLock alock (that);
4624
if (that->mWatcherData.mProcesses.size())
4626
LogFlowFunc (("UPDATE: child process count = %d\n",
4627
that->mWatcherData.mProcesses.size()));
4628
ClientWatcherData::ProcessList::iterator it =
4629
that->mWatcherData.mProcesses.begin();
4630
while (it != that->mWatcherData.mProcesses.end())
4632
RTPROCESS pid = *it;
4633
RTPROCSTATUS status;
4634
int vrc = ::RTProcWait (pid, RTPROCWAIT_FLAGS_NOBLOCK,
4636
if (vrc == VINF_SUCCESS)
4638
LogFlowFunc (("pid %d (%x) was reaped, "
4639
"status=%d, reason=%d\n",
4640
pid, pid, status.iStatus,
4642
it = that->mWatcherData.mProcesses.erase (it);
4646
LogFlowFunc (("pid %d (%x) was NOT reaped, vrc=%Vrc\n",
4648
if (vrc != VERR_PROCESS_RUNNING)
4650
/* remove the process if it is not already running */
4651
it = that->mWatcherData.mProcesses.erase (it);
4664
/* delete the set of opened machines if any */
4676
* Thread function that handles custom events posted using #postEvent().
4679
DECLCALLBACK(int) VirtualBox::AsyncEventHandler (RTTHREAD thread, void *pvUser)
4683
AssertReturn (pvUser, VERR_INVALID_POINTER);
4685
// create an event queue for the current thread
4686
EventQueue *eventQ = new EventQueue();
4687
AssertReturn (eventQ, VERR_NO_MEMORY);
4689
// return the queue to the one who created this thread
4690
*(static_cast <EventQueue **> (pvUser)) = eventQ;
4691
// signal that we're ready
4692
RTThreadUserSignal (thread);
4695
Event *event = NULL;
4697
while ((ok = eventQ->waitForEvent (&event)) && event)
4698
eventQ->handleEvent (event);
4700
AssertReturn (ok, VERR_GENERAL_FAILURE);
4709
////////////////////////////////////////////////////////////////////////////////
4712
* Takes the current list of registered callbacks of the managed VirtualBox
4713
* instance, and calls #handleCallback() for every callback item from the
4714
* list, passing the item as an argument.
4716
* @note Locks the managed VirtualBox object for reading but leaves the lock
4717
* before iterating over callbacks and calling their methods.
4719
void *VirtualBox::CallbackEvent::handler()
4721
if (mVirtualBox.isNull())
4724
AutoCaller autoCaller (mVirtualBox);
4725
if (!autoCaller.isOk())
4727
LogWarningFunc (("VirtualBox has been uninitialized (state=%d), "
4728
"the callback event is discarded!\n",
4729
autoCaller.state()));
4730
/* We don't need mVirtualBox any more, so release it */
4731
mVirtualBox.setNull();
4735
CallbackVector callbacks;
4737
/* Make a copy to release the lock before iterating */
4738
AutoReaderLock alock (mVirtualBox);
4739
callbacks = CallbackVector (mVirtualBox->mData.mCallbacks.begin(),
4740
mVirtualBox->mData.mCallbacks.end());
4741
/* We don't need mVirtualBox any more, so release it */
4742
mVirtualBox.setNull();
4745
for (VirtualBox::CallbackVector::const_iterator it = callbacks.begin();
4746
it != callbacks.end(); ++ it)
4747
handleCallback (*it);