1
/* $Id: MachineImpl.cpp 35610 2011-01-18 14:24:36Z vboxsync $ */
3
* Implementation of IMachine in VBoxSVC.
7
* Copyright (C) 2006-2011 Oracle Corporation
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 (GPL) as published by the Free Software
13
* Foundation, in version 2 as it comes in the "COPYING" file of the
14
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18
/* Make sure all the stdint.h macros are included - must come first! */
19
#ifndef __STDC_LIMIT_MACROS
20
# define __STDC_LIMIT_MACROS
22
#ifndef __STDC_CONSTANT_MACROS
23
# define __STDC_CONSTANT_MACROS
26
#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
28
# include <sys/types.h>
29
# include <sys/stat.h>
35
#include "VirtualBoxImpl.h"
36
#include "MachineImpl.h"
37
#include "ProgressImpl.h"
38
#include "ProgressProxyImpl.h"
39
#include "MediumAttachmentImpl.h"
40
#include "MediumImpl.h"
41
#include "MediumLock.h"
42
#include "USBControllerImpl.h"
44
#include "SharedFolderImpl.h"
45
#include "GuestOSTypeImpl.h"
46
#include "VirtualBoxErrorInfoImpl.h"
47
#include "GuestImpl.h"
48
#include "StorageControllerImpl.h"
49
#include "DisplayImpl.h"
50
#include "DisplayUtils.h"
51
#include "BandwidthControlImpl.h"
54
# include "USBProxyService.h"
57
#include "AutoCaller.h"
58
#include "Performance.h"
61
#include <iprt/path.h>
64
#include <iprt/lockvalidator.h>
65
#include <iprt/process.h>
66
#include <iprt/cpp/utils.h>
67
#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68
#include <iprt/string.h>
70
#include <VBox/com/array.h>
73
#include <VBox/param.h>
74
#include <VBox/settings.h>
75
#include <VBox/vmm/ssm.h>
77
#ifdef VBOX_WITH_GUEST_PROPS
78
# include <VBox/HostServices/GuestPropertySvc.h>
79
# include <VBox/com/array.h>
82
#include "VBox/com/MultiResult.h"
88
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
89
# define HOSTSUFF_EXE ".exe"
90
#else /* !RT_OS_WINDOWS */
91
# define HOSTSUFF_EXE ""
92
#endif /* !RT_OS_WINDOWS */
94
// defines / prototypes
95
/////////////////////////////////////////////////////////////////////////////
97
/////////////////////////////////////////////////////////////////////////////
98
// Machine::Data structure
99
/////////////////////////////////////////////////////////////////////////////
101
Machine::Data::Data()
104
pMachineConfigFile = NULL;
107
/* mUuid is initialized in Machine::init() */
109
mMachineState = MachineState_PoweredOff;
110
RTTimeNow(&mLastStateChange);
112
mMachineStateDeps = 0;
113
mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
114
mMachineStateChangePending = 0;
116
mCurrentStateModified = TRUE;
117
mGuestPropertiesModified = FALSE;
119
mSession.mPid = NIL_RTPROCESS;
120
mSession.mState = SessionState_Unlocked;
123
Machine::Data::~Data()
125
if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
127
RTSemEventMultiDestroy(mMachineStateDepsSem);
128
mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
130
if (pMachineConfigFile)
132
delete pMachineConfigFile;
133
pMachineConfigFile = NULL;
137
/////////////////////////////////////////////////////////////////////////////
138
// Machine::HWData structure
139
/////////////////////////////////////////////////////////////////////////////
141
Machine::HWData::HWData()
143
/* default values for a newly created machine */
144
mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
147
mCPUHotPlugEnabled = false;
148
mMemoryBalloonSize = 0;
149
mPageFusionEnabled = false;
151
mAccelerate3DEnabled = false;
152
mAccelerate2DVideoEnabled = false;
154
mHWVirtExEnabled = true;
155
mHWVirtExNestedPagingEnabled = true;
156
#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
157
mHWVirtExLargePagesEnabled = true;
159
/* Not supported on 32 bits hosts. */
160
mHWVirtExLargePagesEnabled = false;
162
mHWVirtExVPIDEnabled = true;
163
mHWVirtExForceEnabled = false;
164
#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
165
mHWVirtExExclusive = false;
167
mHWVirtExExclusive = true;
169
#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
174
mSyntheticCpu = false;
175
mHpetEnabled = false;
177
/* default boot order: floppy - DVD - HDD */
178
mBootOrder[0] = DeviceType_Floppy;
179
mBootOrder[1] = DeviceType_DVD;
180
mBootOrder[2] = DeviceType_HardDisk;
181
for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
182
mBootOrder[i] = DeviceType_Null;
184
mClipboardMode = ClipboardMode_Bidirectional;
185
mGuestPropertyNotificationPatterns = "";
187
mFirmwareType = FirmwareType_BIOS;
188
mKeyboardHidType = KeyboardHidType_PS2Keyboard;
189
mPointingHidType = PointingHidType_PS2Mouse;
190
mChipsetType = ChipsetType_PIIX3;
192
for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
193
mCPUAttached[i] = false;
195
mIoCacheEnabled = true;
196
mIoCacheSize = 5; /* 5MB */
198
/* Maximum CPU execution cap by default. */
199
mCpuExecutionCap = 100;
202
Machine::HWData::~HWData()
206
/////////////////////////////////////////////////////////////////////////////
207
// Machine::HDData structure
208
/////////////////////////////////////////////////////////////////////////////
210
Machine::MediaData::MediaData()
214
Machine::MediaData::~MediaData()
218
/////////////////////////////////////////////////////////////////////////////
220
/////////////////////////////////////////////////////////////////////////////
222
// constructor / destructor
223
/////////////////////////////////////////////////////////////////////////////
234
HRESULT Machine::FinalConstruct()
236
LogFlowThisFunc(("\n"));
240
void Machine::FinalRelease()
242
LogFlowThisFunc(("\n"));
247
* Initializes a new machine instance; this init() variant creates a new, empty machine.
248
* This gets called from VirtualBox::CreateMachine().
250
* @param aParent Associated parent object
251
* @param strConfigFile Local file system path to the VM settings file (can
252
* be relative to the VirtualBox config directory).
253
* @param strName name for the machine
254
* @param aId UUID for the new machine.
255
* @param aOsType OS Type of this machine or NULL.
256
* @param fForceOverwrite Whether to overwrite an existing machine settings file.
258
* @return Success indicator. if not S_OK, the machine object is invalid
260
HRESULT Machine::init(VirtualBox *aParent,
261
const Utf8Str &strConfigFile,
262
const Utf8Str &strName,
263
GuestOSType *aOsType,
265
bool fForceOverwrite)
267
LogFlowThisFuncEnter();
268
LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
270
/* Enclose the state transition NotReady->InInit->Ready */
271
AutoInitSpan autoInitSpan(this);
272
AssertReturn(autoInitSpan.isOk(), E_FAIL);
274
HRESULT rc = initImpl(aParent, strConfigFile);
275
if (FAILED(rc)) return rc;
277
rc = tryCreateMachineConfigFile(fForceOverwrite);
278
if (FAILED(rc)) return rc;
282
// create an empty machine config
283
mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
285
rc = initDataAndChildObjects();
290
// set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
291
mData->mAccessible = TRUE;
293
unconst(mData->mUuid) = aId;
295
mUserData->s.strName = strName;
297
// the "name sync" flag determines whether the machine directory gets renamed along
298
// with the machine file; say so if the settings file name is the same as the
299
// settings file parent directory (machine directory)
300
mUserData->s.fNameSync = isInOwnDir();
302
// initialize the default snapshots folder
303
rc = COMSETTER(SnapshotFolder)(NULL);
309
mUserData->s.strOsType = aOsType->id();
311
/* Apply BIOS defaults */
312
mBIOSSettings->applyDefaults(aOsType);
314
/* Apply network adapters defaults */
315
for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); ++slot)
316
mNetworkAdapters[slot]->applyDefaults(aOsType);
318
/* Apply serial port defaults */
319
for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
320
mSerialPorts[slot]->applyDefaults(aOsType);
323
/* commit all changes made during the initialization */
327
/* Confirm a successful initialization when it's the case */
330
if (mData->mAccessible)
331
autoInitSpan.setSucceeded();
333
autoInitSpan.setLimited();
336
LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
337
!!mUserData ? mUserData->s.strName.c_str() : "NULL",
342
LogFlowThisFuncLeave();
348
* Initializes a new instance with data from machine XML (formerly Init_Registered).
349
* Gets called in two modes:
351
* -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
352
* UUID is specified and we mark the machine as "registered";
354
* -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
355
* and the machine remains unregistered until RegisterMachine() is called.
357
* @param aParent Associated parent object
358
* @param aConfigFile Local file system path to the VM settings file (can
359
* be relative to the VirtualBox config directory).
360
* @param aId UUID of the machine or NULL (see above).
362
* @return Success indicator. if not S_OK, the machine object is invalid
364
HRESULT Machine::init(VirtualBox *aParent,
365
const Utf8Str &strConfigFile,
368
LogFlowThisFuncEnter();
369
LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
371
/* Enclose the state transition NotReady->InInit->Ready */
372
AutoInitSpan autoInitSpan(this);
373
AssertReturn(autoInitSpan.isOk(), E_FAIL);
375
HRESULT rc = initImpl(aParent, strConfigFile);
376
if (FAILED(rc)) return rc;
380
// loading a registered VM:
381
unconst(mData->mUuid) = *aId;
382
mData->mRegistered = TRUE;
383
// now load the settings from XML:
384
rc = registeredInit();
385
// this calls initDataAndChildObjects() and loadSettings()
389
// opening an unregistered VM (VirtualBox::OpenMachine()):
390
rc = initDataAndChildObjects();
394
// set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
395
mData->mAccessible = TRUE;
399
// load and parse machine XML; this will throw on XML or logic errors
400
mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
402
// reject VM UUID duplicates, they can happen if someone
403
// tries to register an already known VM config again
404
if (aParent->findMachine(mData->pMachineConfigFile->uuid,
405
true /* fPermitInaccessible */,
406
false /* aDoSetError */,
407
NULL) != VBOX_E_OBJECT_NOT_FOUND)
409
throw setError(E_FAIL,
410
tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
411
mData->m_strConfigFile.c_str());
414
// use UUID from machine config
415
unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
417
rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
418
NULL /* puuidRegistry */);
419
if (FAILED(rc)) throw rc;
425
/* we assume that error info is set by the thrower */
430
rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
435
/* Confirm a successful initialization when it's the case */
438
if (mData->mAccessible)
439
autoInitSpan.setSucceeded();
442
autoInitSpan.setLimited();
444
// uninit media from this machine's media registry, or else
445
// reloading the settings will fail
446
mParent->unregisterMachineMedia(getId());
450
LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
452
!!mUserData ? mUserData->s.strName.c_str() : "NULL",
453
mData->mRegistered, mData->mAccessible, rc));
455
LogFlowThisFuncLeave();
461
* Initializes a new instance from a machine config that is already in memory
462
* (import OVF case). Since we are importing, the UUID in the machine
463
* config is ignored and we always generate a fresh one.
465
* @param strName Name for the new machine; this overrides what is specified in config and is used
466
* for the settings file as well.
467
* @param config Machine configuration loaded and parsed from XML.
469
* @return Success indicator. if not S_OK, the machine object is invalid
471
HRESULT Machine::init(VirtualBox *aParent,
472
const Utf8Str &strName,
473
const settings::MachineConfigFile &config)
475
LogFlowThisFuncEnter();
477
/* Enclose the state transition NotReady->InInit->Ready */
478
AutoInitSpan autoInitSpan(this);
479
AssertReturn(autoInitSpan.isOk(), E_FAIL);
481
Utf8Str strConfigFile;
482
aParent->getDefaultMachineFolder(strConfigFile);
483
strConfigFile.append(RTPATH_DELIMITER);
484
strConfigFile.append(strName);
485
strConfigFile.append(RTPATH_DELIMITER);
486
strConfigFile.append(strName);
487
strConfigFile.append(".vbox");
489
HRESULT rc = initImpl(aParent, strConfigFile);
490
if (FAILED(rc)) return rc;
492
rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
493
if (FAILED(rc)) return rc;
495
rc = initDataAndChildObjects();
499
// set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
500
mData->mAccessible = TRUE;
502
// create empty machine config for instance data
503
mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
505
// generate fresh UUID, ignore machine config
506
unconst(mData->mUuid).create();
508
rc = loadMachineDataFromSettings(config,
509
&mData->mUuid); // puuidRegistry: initialize media with this registry ID
511
// override VM name as well, it may be different
512
mUserData->s.strName = strName;
514
/* commit all changes made during the initialization */
519
/* Confirm a successful initialization when it's the case */
522
if (mData->mAccessible)
523
autoInitSpan.setSucceeded();
526
autoInitSpan.setLimited();
528
// uninit media from this machine's media registry, or else
529
// reloading the settings will fail
530
mParent->unregisterMachineMedia(getId());
534
LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
536
!!mUserData ? mUserData->s.strName.c_str() : "NULL",
537
mData->mRegistered, mData->mAccessible, rc));
539
LogFlowThisFuncLeave();
545
* Shared code between the various init() implementations.
549
HRESULT Machine::initImpl(VirtualBox *aParent,
550
const Utf8Str &strConfigFile)
552
LogFlowThisFuncEnter();
554
AssertReturn(aParent, E_INVALIDARG);
555
AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
559
/* share the parent weakly */
560
unconst(mParent) = aParent;
562
/* allocate the essential machine data structure (the rest will be
563
* allocated later by initDataAndChildObjects() */
566
/* memorize the config file name (as provided) */
567
mData->m_strConfigFile = strConfigFile;
569
/* get the full file name */
570
int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
571
if (RT_FAILURE(vrc1))
572
return setError(VBOX_E_FILE_ERROR,
573
tr("Invalid machine settings file name '%s' (%Rrc)"),
574
strConfigFile.c_str(),
577
LogFlowThisFuncLeave();
583
* Tries to create a machine settings file in the path stored in the machine
584
* instance data. Used when a new machine is created to fail gracefully if
585
* the settings file could not be written (e.g. because machine dir is read-only).
588
HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
592
// when we create a new machine, we must be able to create the settings file
593
RTFILE f = NIL_RTFILE;
594
int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
596
|| vrc == VERR_SHARING_VIOLATION
601
if (!fForceOverwrite)
602
rc = setError(VBOX_E_FILE_ERROR,
603
tr("Machine settings file '%s' already exists"),
604
mData->m_strConfigFileFull.c_str());
607
/* try to delete the config file, as otherwise the creation
608
* of a new settings file will fail. */
609
int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
610
if (RT_FAILURE(vrc2))
611
rc = setError(VBOX_E_FILE_ERROR,
612
tr("Could not delete the existing settings file '%s' (%Rrc)"),
613
mData->m_strConfigFileFull.c_str(), vrc2);
616
else if ( vrc != VERR_FILE_NOT_FOUND
617
&& vrc != VERR_PATH_NOT_FOUND
619
rc = setError(VBOX_E_FILE_ERROR,
620
tr("Invalid machine settings file name '%s' (%Rrc)"),
621
mData->m_strConfigFileFull.c_str(),
627
* Initializes the registered machine by loading the settings file.
628
* This method is separated from #init() in order to make it possible to
629
* retry the operation after VirtualBox startup instead of refusing to
630
* startup the whole VirtualBox server in case if the settings file of some
631
* registered VM is invalid or inaccessible.
633
* @note Must be always called from this object's write lock
634
* (unless called from #init() that doesn't need any locking).
635
* @note Locks the mUSBController method for writing.
636
* @note Subclasses must not call this method.
638
HRESULT Machine::registeredInit()
640
AssertReturn(!isSessionMachine(), E_FAIL);
641
AssertReturn(!isSnapshotMachine(), E_FAIL);
642
AssertReturn(!mData->mUuid.isEmpty(), E_FAIL);
643
AssertReturn(!mData->mAccessible, E_FAIL);
645
HRESULT rc = initDataAndChildObjects();
649
/* Temporarily reset the registered flag in order to let setters
650
* potentially called from loadSettings() succeed (isMutable() used in
651
* all setters will return FALSE for a Machine instance if mRegistered
653
mData->mRegistered = FALSE;
657
// load and parse machine XML; this will throw on XML or logic errors
658
mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
660
if (mData->mUuid != mData->pMachineConfigFile->uuid)
661
throw setError(E_FAIL,
662
tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
663
mData->pMachineConfigFile->uuid.raw(),
664
mData->m_strConfigFileFull.c_str(),
665
mData->mUuid.toString().c_str(),
666
mParent->settingsFilePath().c_str());
668
rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
669
NULL /* const Guid *puuidRegistry */);
670
if (FAILED(rc)) throw rc;
674
/* we assume that error info is set by the thrower */
679
rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
682
/* Restore the registered flag (even on failure) */
683
mData->mRegistered = TRUE;
688
/* Set mAccessible to TRUE only if we successfully locked and loaded
689
* the settings file */
690
mData->mAccessible = TRUE;
692
/* commit all changes made during loading the settings file */
693
commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
697
/* If the machine is registered, then, instead of returning a
698
* failure, we mark it as inaccessible and set the result to
699
* success to give it a try later */
701
/* fetch the current error info */
702
mData->mAccessError = com::ErrorInfo();
703
LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
705
mData->mAccessError.getText().raw()));
707
/* rollback all changes */
708
rollback(false /* aNotify */);
710
// uninit media from this machine's media registry, or else
711
// reloading the settings will fail
712
mParent->unregisterMachineMedia(getId());
714
/* uninitialize the common part to make sure all data is reset to
715
* default (null) values */
716
uninitDataAndChildObjects();
725
* Uninitializes the instance.
726
* Called either from FinalRelease() or by the parent when it gets destroyed.
728
* @note The caller of this method must make sure that this object
729
* a) doesn't have active callers on the current thread and b) is not locked
730
* by the current thread; otherwise uninit() will hang either a) due to
731
* AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
732
* a dead-lock caused by this thread waiting for all callers on the other
733
* threads are done but preventing them from doing so by holding a lock.
735
void Machine::uninit()
737
LogFlowThisFuncEnter();
739
Assert(!isWriteLockOnCurrentThread());
741
/* Enclose the state transition Ready->InUninit->NotReady */
742
AutoUninitSpan autoUninitSpan(this);
743
if (autoUninitSpan.uninitDone())
746
Assert(!isSnapshotMachine());
747
Assert(!isSessionMachine());
750
LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
751
LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
753
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
755
if (!mData->mSession.mMachine.isNull())
757
/* Theoretically, this can only happen if the VirtualBox server has been
758
* terminated while there were clients running that owned open direct
759
* sessions. Since in this case we are definitely called by
760
* VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
761
* won't happen on the client watcher thread (because it does
762
* VirtualBox::addCaller() for the duration of the
763
* SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
764
* cannot happen until the VirtualBox caller is released). This is
765
* important, because SessionMachine::uninit() cannot correctly operate
766
* after we return from this method (it expects the Machine instance is
767
* still valid). We'll call it ourselves below.
769
LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
770
(SessionMachine*)mData->mSession.mMachine));
772
if (Global::IsOnlineOrTransient(mData->mMachineState))
774
LogWarningThisFunc(("Setting state to Aborted!\n"));
775
/* set machine state using SessionMachine reimplementation */
776
static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
780
* Uninitialize SessionMachine using public uninit() to indicate
781
* an unexpected uninitialization.
783
mData->mSession.mMachine->uninit();
784
/* SessionMachine::uninit() must set mSession.mMachine to null */
785
Assert(mData->mSession.mMachine.isNull());
788
// uninit media from this machine's media registry, if they're still there
789
Guid uuidMachine(getId());
790
if (!uuidMachine.isEmpty()) // can be empty if we're called from a failure of Machine::init
791
mParent->unregisterMachineMedia(uuidMachine);
793
/* the lock is no more necessary (SessionMachine is uninitialized) */
796
// has machine been modified?
797
if (mData->flModifications)
799
LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
800
rollback(false /* aNotify */);
803
if (mData->mAccessible)
804
uninitDataAndChildObjects();
806
/* free the essential data structure last */
809
LogFlowThisFuncLeave();
812
// IMachine properties
813
/////////////////////////////////////////////////////////////////////////////
815
STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
817
CheckComArgOutPointerValid(aParent);
819
AutoLimitedCaller autoCaller(this);
820
if (FAILED(autoCaller.rc())) return autoCaller.rc();
822
/* mParent is constant during life time, no need to lock */
823
ComObjPtr<VirtualBox> pVirtualBox(mParent);
824
pVirtualBox.queryInterfaceTo(aParent);
829
STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
831
CheckComArgOutPointerValid(aAccessible);
833
AutoLimitedCaller autoCaller(this);
834
if (FAILED(autoCaller.rc())) return autoCaller.rc();
836
LogFlowThisFunc(("ENTER\n"));
838
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
842
if (!mData->mAccessible)
844
/* try to initialize the VM once more if not accessible */
846
AutoReinitSpan autoReinitSpan(this);
847
AssertReturn(autoReinitSpan.isOk(), E_FAIL);
850
LogFlowThisFunc(("Dumping media backreferences\n"));
851
mParent->dumpAllBackRefs();
854
if (mData->pMachineConfigFile)
856
// reset the XML file to force loadSettings() (called from registeredInit())
857
// to parse it again; the file might have changed
858
delete mData->pMachineConfigFile;
859
mData->pMachineConfigFile = NULL;
862
rc = registeredInit();
864
if (SUCCEEDED(rc) && mData->mAccessible)
866
autoReinitSpan.setSucceeded();
868
/* make sure interesting parties will notice the accessibility
870
mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
871
mParent->onMachineDataChange(mData->mUuid);
876
*aAccessible = mData->mAccessible;
878
LogFlowThisFuncLeave();
883
STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
885
CheckComArgOutPointerValid(aAccessError);
887
AutoLimitedCaller autoCaller(this);
888
if (FAILED(autoCaller.rc())) return autoCaller.rc();
890
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
892
if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
901
ComObjPtr<VirtualBoxErrorInfo> errorInfo;
902
rc = errorInfo.createObject();
905
errorInfo->init(mData->mAccessError.getResultCode(),
906
mData->mAccessError.getInterfaceID().ref(),
907
Utf8Str(mData->mAccessError.getComponent()).c_str(),
908
Utf8Str(mData->mAccessError.getText()));
909
rc = errorInfo.queryInterfaceTo(aAccessError);
915
STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
917
CheckComArgOutPointerValid(aName);
919
AutoCaller autoCaller(this);
920
if (FAILED(autoCaller.rc())) return autoCaller.rc();
922
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
924
mUserData->s.strName.cloneTo(aName);
929
STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
931
CheckComArgStrNotEmptyOrNull(aName);
933
AutoCaller autoCaller(this);
934
if (FAILED(autoCaller.rc())) return autoCaller.rc();
936
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
938
HRESULT rc = checkStateDependency(MutableStateDep);
939
if (FAILED(rc)) return rc;
941
setModified(IsModified_MachineData);
943
mUserData->s.strName = aName;
948
STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
950
CheckComArgOutPointerValid(aDescription);
952
AutoCaller autoCaller(this);
953
if (FAILED(autoCaller.rc())) return autoCaller.rc();
955
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
957
mUserData->s.strDescription.cloneTo(aDescription);
962
STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
964
AutoCaller autoCaller(this);
965
if (FAILED(autoCaller.rc())) return autoCaller.rc();
967
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
969
HRESULT rc = checkStateDependency(MutableStateDep);
970
if (FAILED(rc)) return rc;
972
setModified(IsModified_MachineData);
974
mUserData->s.strDescription = aDescription;
979
STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
981
CheckComArgOutPointerValid(aId);
983
AutoLimitedCaller autoCaller(this);
984
if (FAILED(autoCaller.rc())) return autoCaller.rc();
986
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
988
mData->mUuid.toUtf16().cloneTo(aId);
993
STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
995
CheckComArgOutPointerValid(aOSTypeId);
997
AutoCaller autoCaller(this);
998
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1000
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1002
mUserData->s.strOsType.cloneTo(aOSTypeId);
1007
STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1009
CheckComArgStrNotEmptyOrNull(aOSTypeId);
1011
AutoCaller autoCaller(this);
1012
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1014
/* look up the object by Id to check it is valid */
1015
ComPtr<IGuestOSType> guestOSType;
1016
HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1017
if (FAILED(rc)) return rc;
1019
/* when setting, always use the "etalon" value for consistency -- lookup
1020
* by ID is case-insensitive and the input value may have different case */
1022
rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1023
if (FAILED(rc)) return rc;
1025
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1027
rc = checkStateDependency(MutableStateDep);
1028
if (FAILED(rc)) return rc;
1030
setModified(IsModified_MachineData);
1032
mUserData->s.strOsType = osTypeId;
1038
STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1040
CheckComArgOutPointerValid(aFirmwareType);
1042
AutoCaller autoCaller(this);
1043
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1045
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1047
*aFirmwareType = mHWData->mFirmwareType;
1052
STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1054
AutoCaller autoCaller(this);
1055
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1056
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1058
int rc = checkStateDependency(MutableStateDep);
1059
if (FAILED(rc)) return rc;
1061
setModified(IsModified_MachineData);
1063
mHWData->mFirmwareType = aFirmwareType;
1068
STDMETHODIMP Machine::COMGETTER(KeyboardHidType)(KeyboardHidType_T *aKeyboardHidType)
1070
CheckComArgOutPointerValid(aKeyboardHidType);
1072
AutoCaller autoCaller(this);
1073
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1075
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1077
*aKeyboardHidType = mHWData->mKeyboardHidType;
1082
STDMETHODIMP Machine::COMSETTER(KeyboardHidType)(KeyboardHidType_T aKeyboardHidType)
1084
AutoCaller autoCaller(this);
1085
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1086
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1088
int rc = checkStateDependency(MutableStateDep);
1089
if (FAILED(rc)) return rc;
1091
setModified(IsModified_MachineData);
1093
mHWData->mKeyboardHidType = aKeyboardHidType;
1098
STDMETHODIMP Machine::COMGETTER(PointingHidType)(PointingHidType_T *aPointingHidType)
1100
CheckComArgOutPointerValid(aPointingHidType);
1102
AutoCaller autoCaller(this);
1103
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1105
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1107
*aPointingHidType = mHWData->mPointingHidType;
1112
STDMETHODIMP Machine::COMSETTER(PointingHidType)(PointingHidType_T aPointingHidType)
1114
AutoCaller autoCaller(this);
1115
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1116
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1118
int rc = checkStateDependency(MutableStateDep);
1119
if (FAILED(rc)) return rc;
1121
setModified(IsModified_MachineData);
1123
mHWData->mPointingHidType = aPointingHidType;
1128
STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1130
CheckComArgOutPointerValid(aChipsetType);
1132
AutoCaller autoCaller(this);
1133
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1135
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1137
*aChipsetType = mHWData->mChipsetType;
1142
STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1144
AutoCaller autoCaller(this);
1145
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1146
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1148
int rc = checkStateDependency(MutableStateDep);
1149
if (FAILED(rc)) return rc;
1151
setModified(IsModified_MachineData);
1153
mHWData->mChipsetType = aChipsetType;
1158
STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1163
AutoCaller autoCaller(this);
1164
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1166
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1168
mHWData->mHWVersion.cloneTo(aHWVersion);
1173
STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1175
/* check known version */
1176
Utf8Str hwVersion = aHWVersion;
1177
if ( hwVersion.compare("1") != 0
1178
&& hwVersion.compare("2") != 0)
1179
return setError(E_INVALIDARG,
1180
tr("Invalid hardware version: %ls\n"), aHWVersion);
1182
AutoCaller autoCaller(this);
1183
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1185
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1187
HRESULT rc = checkStateDependency(MutableStateDep);
1188
if (FAILED(rc)) return rc;
1190
setModified(IsModified_MachineData);
1192
mHWData->mHWVersion = hwVersion;
1197
STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1199
CheckComArgOutPointerValid(aUUID);
1201
AutoCaller autoCaller(this);
1202
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1204
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1206
if (!mHWData->mHardwareUUID.isEmpty())
1207
mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1209
mData->mUuid.toUtf16().cloneTo(aUUID);
1214
STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1216
Guid hardwareUUID(aUUID);
1217
if (hardwareUUID.isEmpty())
1218
return E_INVALIDARG;
1220
AutoCaller autoCaller(this);
1221
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1223
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1225
HRESULT rc = checkStateDependency(MutableStateDep);
1226
if (FAILED(rc)) return rc;
1228
setModified(IsModified_MachineData);
1230
if (hardwareUUID == mData->mUuid)
1231
mHWData->mHardwareUUID.clear();
1233
mHWData->mHardwareUUID = hardwareUUID;
1238
STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1243
AutoCaller autoCaller(this);
1244
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1246
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1248
*memorySize = mHWData->mMemorySize;
1253
STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1255
/* check RAM limits */
1256
if ( memorySize < MM_RAM_MIN_IN_MB
1257
|| memorySize > MM_RAM_MAX_IN_MB
1259
return setError(E_INVALIDARG,
1260
tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1261
memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1263
AutoCaller autoCaller(this);
1264
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1266
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1268
HRESULT rc = checkStateDependency(MutableStateDep);
1269
if (FAILED(rc)) return rc;
1271
setModified(IsModified_MachineData);
1273
mHWData->mMemorySize = memorySize;
1278
STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1283
AutoCaller autoCaller(this);
1284
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1286
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1288
*CPUCount = mHWData->mCPUCount;
1293
STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1295
/* check CPU limits */
1296
if ( CPUCount < SchemaDefs::MinCPUCount
1297
|| CPUCount > SchemaDefs::MaxCPUCount
1299
return setError(E_INVALIDARG,
1300
tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1301
CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1303
AutoCaller autoCaller(this);
1304
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1306
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1308
/* We cant go below the current number of CPUs if hotplug is enabled*/
1309
if (mHWData->mCPUHotPlugEnabled)
1311
for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1313
if (mHWData->mCPUAttached[idx])
1314
return setError(E_INVALIDARG,
1315
tr(": %lu (must be higher than or equal to %lu)"),
1320
HRESULT rc = checkStateDependency(MutableStateDep);
1321
if (FAILED(rc)) return rc;
1323
setModified(IsModified_MachineData);
1325
mHWData->mCPUCount = CPUCount;
1330
STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1335
AutoCaller autoCaller(this);
1336
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1338
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1340
*aExecutionCap = mHWData->mCpuExecutionCap;
1345
STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1349
/* check throttle limits */
1350
if ( aExecutionCap < 1
1351
|| aExecutionCap > 100
1353
return setError(E_INVALIDARG,
1354
tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1355
aExecutionCap, 1, 100);
1357
AutoCaller autoCaller(this);
1358
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1360
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1363
rc = onCPUExecutionCapChange(aExecutionCap);
1365
if (FAILED(rc)) return rc;
1367
setModified(IsModified_MachineData);
1369
mHWData->mCpuExecutionCap = aExecutionCap;
1371
/* Save settings if online - todo why is this required?? */
1372
if (Global::IsOnline(mData->mMachineState))
1379
STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
1384
AutoCaller autoCaller(this);
1385
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1387
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1389
*enabled = mHWData->mCPUHotPlugEnabled;
1394
STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
1398
AutoCaller autoCaller(this);
1399
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1401
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1403
rc = checkStateDependency(MutableStateDep);
1404
if (FAILED(rc)) return rc;
1406
if (mHWData->mCPUHotPlugEnabled != enabled)
1410
setModified(IsModified_MachineData);
1413
/* Add the amount of CPUs currently attached */
1414
for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1416
mHWData->mCPUAttached[i] = true;
1422
* We can disable hotplug only if the amount of maximum CPUs is equal
1423
* to the amount of attached CPUs
1425
unsigned cCpusAttached = 0;
1426
unsigned iHighestId = 0;
1428
for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1430
if (mHWData->mCPUAttached[i])
1437
if ( (cCpusAttached != mHWData->mCPUCount)
1438
|| (iHighestId >= mHWData->mCPUCount))
1439
return setError(E_INVALIDARG,
1440
tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached\n"));
1442
setModified(IsModified_MachineData);
1447
mHWData->mCPUHotPlugEnabled = enabled;
1452
STDMETHODIMP Machine::COMGETTER(HpetEnabled)(BOOL *enabled)
1454
CheckComArgOutPointerValid(enabled);
1456
AutoCaller autoCaller(this);
1457
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1458
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1460
*enabled = mHWData->mHpetEnabled;
1465
STDMETHODIMP Machine::COMSETTER(HpetEnabled)(BOOL enabled)
1469
AutoCaller autoCaller(this);
1470
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1471
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1473
rc = checkStateDependency(MutableStateDep);
1474
if (FAILED(rc)) return rc;
1476
setModified(IsModified_MachineData);
1479
mHWData->mHpetEnabled = enabled;
1484
STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1489
AutoCaller autoCaller(this);
1490
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1492
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1494
*memorySize = mHWData->mVRAMSize;
1499
STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1501
/* check VRAM limits */
1502
if (memorySize < SchemaDefs::MinGuestVRAM ||
1503
memorySize > SchemaDefs::MaxGuestVRAM)
1504
return setError(E_INVALIDARG,
1505
tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1506
memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1508
AutoCaller autoCaller(this);
1509
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1511
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1513
HRESULT rc = checkStateDependency(MutableStateDep);
1514
if (FAILED(rc)) return rc;
1516
setModified(IsModified_MachineData);
1518
mHWData->mVRAMSize = memorySize;
1523
/** @todo this method should not be public */
1524
STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1526
if (!memoryBalloonSize)
1529
AutoCaller autoCaller(this);
1530
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1532
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1534
*memoryBalloonSize = mHWData->mMemoryBalloonSize;
1540
* Set the memory balloon size.
1542
* This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1543
* we have to make sure that we never call IGuest from here.
1545
STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1547
/* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1548
#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1550
if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1551
return setError(E_INVALIDARG,
1552
tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1553
memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1555
AutoCaller autoCaller(this);
1556
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1558
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1560
setModified(IsModified_MachineData);
1562
mHWData->mMemoryBalloonSize = memoryBalloonSize;
1566
NOREF(memoryBalloonSize);
1567
return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1571
STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
1576
AutoCaller autoCaller(this);
1577
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1579
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1581
*enabled = mHWData->mPageFusionEnabled;
1585
STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
1587
#ifdef VBOX_WITH_PAGE_SHARING
1588
AutoCaller autoCaller(this);
1589
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1591
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1593
/** @todo must support changes for running vms and keep this in sync with IGuest. */
1594
setModified(IsModified_MachineData);
1596
mHWData->mPageFusionEnabled = enabled;
1600
return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1604
STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1609
AutoCaller autoCaller(this);
1610
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1612
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1614
*enabled = mHWData->mAccelerate3DEnabled;
1619
STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1621
AutoCaller autoCaller(this);
1622
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1624
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1626
HRESULT rc = checkStateDependency(MutableStateDep);
1627
if (FAILED(rc)) return rc;
1629
/** @todo check validity! */
1631
setModified(IsModified_MachineData);
1633
mHWData->mAccelerate3DEnabled = enable;
1639
STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1644
AutoCaller autoCaller(this);
1645
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1647
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1649
*enabled = mHWData->mAccelerate2DVideoEnabled;
1654
STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1656
AutoCaller autoCaller(this);
1657
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1659
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1661
HRESULT rc = checkStateDependency(MutableStateDep);
1662
if (FAILED(rc)) return rc;
1664
/** @todo check validity! */
1666
setModified(IsModified_MachineData);
1668
mHWData->mAccelerate2DVideoEnabled = enable;
1673
STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
1678
AutoCaller autoCaller(this);
1679
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1681
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1683
*monitorCount = mHWData->mMonitorCount;
1688
STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
1690
/* make sure monitor count is a sensible number */
1691
if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
1692
return setError(E_INVALIDARG,
1693
tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1694
monitorCount, 1, SchemaDefs::MaxGuestMonitors);
1696
AutoCaller autoCaller(this);
1697
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1699
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1701
HRESULT rc = checkStateDependency(MutableStateDep);
1702
if (FAILED(rc)) return rc;
1704
setModified(IsModified_MachineData);
1706
mHWData->mMonitorCount = monitorCount;
1711
STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
1716
AutoCaller autoCaller(this);
1717
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1719
/* mBIOSSettings is constant during life time, no need to lock */
1720
mBIOSSettings.queryInterfaceTo(biosSettings);
1725
STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
1730
AutoCaller autoCaller(this);
1731
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1733
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1737
case CPUPropertyType_PAE:
1738
*aVal = mHWData->mPAEEnabled;
1741
case CPUPropertyType_Synthetic:
1742
*aVal = mHWData->mSyntheticCpu;
1746
return E_INVALIDARG;
1751
STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
1753
AutoCaller autoCaller(this);
1754
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1756
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1758
HRESULT rc = checkStateDependency(MutableStateDep);
1759
if (FAILED(rc)) return rc;
1763
case CPUPropertyType_PAE:
1764
setModified(IsModified_MachineData);
1766
mHWData->mPAEEnabled = !!aVal;
1769
case CPUPropertyType_Synthetic:
1770
setModified(IsModified_MachineData);
1772
mHWData->mSyntheticCpu = !!aVal;
1776
return E_INVALIDARG;
1781
STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
1783
CheckComArgOutPointerValid(aValEax);
1784
CheckComArgOutPointerValid(aValEbx);
1785
CheckComArgOutPointerValid(aValEcx);
1786
CheckComArgOutPointerValid(aValEdx);
1788
AutoCaller autoCaller(this);
1789
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1791
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1806
if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
1807
return setError(E_INVALIDARG, tr("CpuId override leaf %#x is not set"), aId);
1809
*aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
1810
*aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
1811
*aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
1812
*aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
1826
if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
1827
return setError(E_INVALIDARG, tr("CpuId override leaf %#x is not set"), aId);
1829
*aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
1830
*aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
1831
*aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
1832
*aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
1836
return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
1841
STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
1843
AutoCaller autoCaller(this);
1844
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1846
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1848
HRESULT rc = checkStateDependency(MutableStateDep);
1849
if (FAILED(rc)) return rc;
1864
AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xA);
1865
AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
1866
setModified(IsModified_MachineData);
1868
mHWData->mCpuIdStdLeafs[aId].ulId = aId;
1869
mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
1870
mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
1871
mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
1872
mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
1886
AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xA);
1887
AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
1888
setModified(IsModified_MachineData);
1890
mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
1891
mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
1892
mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
1893
mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
1894
mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
1898
return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
1903
STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
1905
AutoCaller autoCaller(this);
1906
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1908
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1910
HRESULT rc = checkStateDependency(MutableStateDep);
1911
if (FAILED(rc)) return rc;
1926
AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xA);
1927
AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
1928
setModified(IsModified_MachineData);
1930
/* Invalidate leaf. */
1931
mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
1945
AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xA);
1946
AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
1947
setModified(IsModified_MachineData);
1949
/* Invalidate leaf. */
1950
mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
1954
return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
1959
STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
1961
AutoCaller autoCaller(this);
1962
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1964
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1966
HRESULT rc = checkStateDependency(MutableStateDep);
1967
if (FAILED(rc)) return rc;
1969
setModified(IsModified_MachineData);
1972
/* Invalidate all standard leafs. */
1973
for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
1974
mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
1976
/* Invalidate all extended leafs. */
1977
for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
1978
mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
1983
STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
1988
AutoCaller autoCaller(this);
1989
if (FAILED(autoCaller.rc())) return autoCaller.rc();
1991
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1995
case HWVirtExPropertyType_Enabled:
1996
*aVal = mHWData->mHWVirtExEnabled;
1999
case HWVirtExPropertyType_Exclusive:
2000
*aVal = mHWData->mHWVirtExExclusive;
2003
case HWVirtExPropertyType_VPID:
2004
*aVal = mHWData->mHWVirtExVPIDEnabled;
2007
case HWVirtExPropertyType_NestedPaging:
2008
*aVal = mHWData->mHWVirtExNestedPagingEnabled;
2011
case HWVirtExPropertyType_LargePages:
2012
*aVal = mHWData->mHWVirtExLargePagesEnabled;
2015
case HWVirtExPropertyType_Force:
2016
*aVal = mHWData->mHWVirtExForceEnabled;
2020
return E_INVALIDARG;
2025
STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2027
AutoCaller autoCaller(this);
2028
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2030
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2032
HRESULT rc = checkStateDependency(MutableStateDep);
2033
if (FAILED(rc)) return rc;
2037
case HWVirtExPropertyType_Enabled:
2038
setModified(IsModified_MachineData);
2040
mHWData->mHWVirtExEnabled = !!aVal;
2043
case HWVirtExPropertyType_Exclusive:
2044
setModified(IsModified_MachineData);
2046
mHWData->mHWVirtExExclusive = !!aVal;
2049
case HWVirtExPropertyType_VPID:
2050
setModified(IsModified_MachineData);
2052
mHWData->mHWVirtExVPIDEnabled = !!aVal;
2055
case HWVirtExPropertyType_NestedPaging:
2056
setModified(IsModified_MachineData);
2058
mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2061
case HWVirtExPropertyType_LargePages:
2062
setModified(IsModified_MachineData);
2064
mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2067
case HWVirtExPropertyType_Force:
2068
setModified(IsModified_MachineData);
2070
mHWData->mHWVirtExForceEnabled = !!aVal;
2074
return E_INVALIDARG;
2080
STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2082
CheckComArgOutPointerValid(aSnapshotFolder);
2084
AutoCaller autoCaller(this);
2085
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2087
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2089
Utf8Str strFullSnapshotFolder;
2090
calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2091
strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2096
STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2099
* 1. Allow to change the name of the snapshot folder containing snapshots
2100
* 2. Rename the folder on disk instead of just changing the property
2101
* value (to be smart and not to leave garbage). Note that it cannot be
2102
* done here because the change may be rolled back. Thus, the right
2103
* place is #saveSettings().
2106
AutoCaller autoCaller(this);
2107
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2109
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2111
HRESULT rc = checkStateDependency(MutableStateDep);
2112
if (FAILED(rc)) return rc;
2114
if (!mData->mCurrentSnapshot.isNull())
2115
return setError(E_FAIL,
2116
tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2118
Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2120
Utf8Str strSnapshotFolder(strSnapshotFolder0);
2121
if (strSnapshotFolder.isEmpty())
2122
strSnapshotFolder = "Snapshots";
2123
int vrc = calculateFullPath(strSnapshotFolder,
2125
if (RT_FAILURE(vrc))
2126
return setError(E_FAIL,
2127
tr("Invalid snapshot folder '%ls' (%Rrc)"),
2128
aSnapshotFolder, vrc);
2130
setModified(IsModified_MachineData);
2133
copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2138
STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2140
if (ComSafeArrayOutIsNull(aAttachments))
2143
AutoCaller autoCaller(this);
2144
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2146
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2148
SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2149
attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2154
STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2159
AutoCaller autoCaller(this);
2160
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2162
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2164
Assert(!!mVRDEServer);
2165
mVRDEServer.queryInterfaceTo(vrdeServer);
2170
STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2175
AutoCaller autoCaller(this);
2176
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2178
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2180
mAudioAdapter.queryInterfaceTo(audioAdapter);
2184
STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2186
#ifdef VBOX_WITH_VUSB
2187
CheckComArgOutPointerValid(aUSBController);
2189
AutoCaller autoCaller(this);
2190
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2191
MultiResult rc(S_OK);
2193
# ifdef VBOX_WITH_USB
2194
rc = mParent->host()->checkUSBProxyService();
2195
if (FAILED(rc)) return rc;
2198
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2200
return rc = mUSBController.queryInterfaceTo(aUSBController);
2202
/* Note: The GUI depends on this method returning E_NOTIMPL with no
2203
* extended error info to indicate that USB is simply not available
2204
* (w/o treating it as a failure), for example, as in OSE */
2205
NOREF(aUSBController);
2206
ReturnComNotImplemented();
2207
#endif /* VBOX_WITH_VUSB */
2210
STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2212
CheckComArgOutPointerValid(aFilePath);
2214
AutoLimitedCaller autoCaller(this);
2215
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2217
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2219
mData->m_strConfigFileFull.cloneTo(aFilePath);
2223
STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2225
CheckComArgOutPointerValid(aModified);
2227
AutoCaller autoCaller(this);
2228
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2230
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2232
HRESULT rc = checkStateDependency(MutableStateDep);
2233
if (FAILED(rc)) return rc;
2235
if (!mData->pMachineConfigFile->fileExists())
2236
// this is a new machine, and no config file exists yet:
2239
*aModified = (mData->flModifications != 0);
2244
STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2246
CheckComArgOutPointerValid(aSessionState);
2248
AutoCaller autoCaller(this);
2249
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2251
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2253
*aSessionState = mData->mSession.mState;
2258
STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2260
CheckComArgOutPointerValid(aSessionType);
2262
AutoCaller autoCaller(this);
2263
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2265
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2267
mData->mSession.mType.cloneTo(aSessionType);
2272
STDMETHODIMP Machine::COMGETTER(SessionPid)(ULONG *aSessionPid)
2274
CheckComArgOutPointerValid(aSessionPid);
2276
AutoCaller autoCaller(this);
2277
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2279
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2281
*aSessionPid = mData->mSession.mPid;
2286
STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2291
AutoCaller autoCaller(this);
2292
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2294
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2296
*machineState = mData->mMachineState;
2301
STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2303
CheckComArgOutPointerValid(aLastStateChange);
2305
AutoCaller autoCaller(this);
2306
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2308
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2310
*aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2315
STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2317
CheckComArgOutPointerValid(aStateFilePath);
2319
AutoCaller autoCaller(this);
2320
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2322
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2324
mSSData->mStateFilePath.cloneTo(aStateFilePath);
2329
STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2331
CheckComArgOutPointerValid(aLogFolder);
2333
AutoCaller autoCaller(this);
2334
AssertComRCReturnRC(autoCaller.rc());
2336
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2339
getLogFolder(logFolder);
2340
logFolder.cloneTo(aLogFolder);
2345
STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2347
CheckComArgOutPointerValid(aCurrentSnapshot);
2349
AutoCaller autoCaller(this);
2350
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2352
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2354
mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2359
STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2361
CheckComArgOutPointerValid(aSnapshotCount);
2363
AutoCaller autoCaller(this);
2364
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2366
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2368
*aSnapshotCount = mData->mFirstSnapshot.isNull()
2370
: mData->mFirstSnapshot->getAllChildrenCount() + 1;
2375
STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2377
CheckComArgOutPointerValid(aCurrentStateModified);
2379
AutoCaller autoCaller(this);
2380
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2382
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2384
/* Note: for machines with no snapshots, we always return FALSE
2385
* (mData->mCurrentStateModified will be TRUE in this case, for historical
2388
*aCurrentStateModified = mData->mFirstSnapshot.isNull()
2390
: mData->mCurrentStateModified;
2395
STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2397
CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2399
AutoCaller autoCaller(this);
2400
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2402
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2404
SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2405
folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2410
STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2412
CheckComArgOutPointerValid(aClipboardMode);
2414
AutoCaller autoCaller(this);
2415
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2417
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2419
*aClipboardMode = mHWData->mClipboardMode;
2425
Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2427
AutoCaller autoCaller(this);
2428
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2430
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2432
HRESULT rc = checkStateDependency(MutableStateDep);
2433
if (FAILED(rc)) return rc;
2435
setModified(IsModified_MachineData);
2437
mHWData->mClipboardMode = aClipboardMode;
2443
Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2445
CheckComArgOutPointerValid(aPatterns);
2447
AutoCaller autoCaller(this);
2448
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2450
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2454
mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2458
return VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
2465
Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2467
AutoCaller autoCaller(this);
2468
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2470
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2472
HRESULT rc = checkStateDependency(MutableStateDep);
2473
if (FAILED(rc)) return rc;
2475
setModified(IsModified_MachineData);
2477
mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2482
Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2484
CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2486
AutoCaller autoCaller(this);
2487
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2489
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2491
SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2492
ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2498
Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2500
CheckComArgOutPointerValid(aEnabled);
2502
AutoCaller autoCaller(this);
2503
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2505
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2507
*aEnabled = mUserData->s.fTeleporterEnabled;
2512
STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2514
AutoCaller autoCaller(this);
2515
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2517
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2519
/* Only allow it to be set to true when PoweredOff or Aborted.
2520
(Clearing it is always permitted.) */
2522
&& mData->mRegistered
2523
&& ( !isSessionMachine()
2524
|| ( mData->mMachineState != MachineState_PoweredOff
2525
&& mData->mMachineState != MachineState_Teleported
2526
&& mData->mMachineState != MachineState_Aborted
2530
return setError(VBOX_E_INVALID_VM_STATE,
2531
tr("The machine is not powered off (state is %s)"),
2532
Global::stringifyMachineState(mData->mMachineState));
2534
setModified(IsModified_MachineData);
2536
mUserData->s.fTeleporterEnabled = !!aEnabled;
2541
STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2543
CheckComArgOutPointerValid(aPort);
2545
AutoCaller autoCaller(this);
2546
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2548
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2550
*aPort = (ULONG)mUserData->s.uTeleporterPort;
2555
STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2558
return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2560
AutoCaller autoCaller(this);
2561
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2563
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2565
HRESULT rc = checkStateDependency(MutableStateDep);
2566
if (FAILED(rc)) return rc;
2568
setModified(IsModified_MachineData);
2570
mUserData->s.uTeleporterPort = (uint32_t)aPort;
2575
STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
2577
CheckComArgOutPointerValid(aAddress);
2579
AutoCaller autoCaller(this);
2580
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2582
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2584
mUserData->s.strTeleporterAddress.cloneTo(aAddress);
2589
STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
2591
AutoCaller autoCaller(this);
2592
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2594
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2596
HRESULT rc = checkStateDependency(MutableStateDep);
2597
if (FAILED(rc)) return rc;
2599
setModified(IsModified_MachineData);
2601
mUserData->s.strTeleporterAddress = aAddress;
2606
STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
2608
CheckComArgOutPointerValid(aPassword);
2610
AutoCaller autoCaller(this);
2611
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2613
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2615
mUserData->s.strTeleporterPassword.cloneTo(aPassword);
2620
STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
2622
AutoCaller autoCaller(this);
2623
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2625
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2627
HRESULT rc = checkStateDependency(MutableStateDep);
2628
if (FAILED(rc)) return rc;
2630
setModified(IsModified_MachineData);
2632
mUserData->s.strTeleporterPassword = aPassword;
2637
STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
2639
CheckComArgOutPointerValid(aState);
2641
AutoCaller autoCaller(this);
2642
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2644
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2646
*aState = mUserData->s.enmFaultToleranceState;
2650
STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
2652
AutoCaller autoCaller(this);
2653
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2655
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2657
/* @todo deal with running state change. */
2658
HRESULT rc = checkStateDependency(MutableStateDep);
2659
if (FAILED(rc)) return rc;
2661
setModified(IsModified_MachineData);
2663
mUserData->s.enmFaultToleranceState = aState;
2667
STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
2669
CheckComArgOutPointerValid(aAddress);
2671
AutoCaller autoCaller(this);
2672
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2674
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2676
mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
2680
STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
2682
AutoCaller autoCaller(this);
2683
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2685
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2687
/* @todo deal with running state change. */
2688
HRESULT rc = checkStateDependency(MutableStateDep);
2689
if (FAILED(rc)) return rc;
2691
setModified(IsModified_MachineData);
2693
mUserData->s.strFaultToleranceAddress = aAddress;
2697
STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
2699
CheckComArgOutPointerValid(aPort);
2701
AutoCaller autoCaller(this);
2702
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2704
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2706
*aPort = mUserData->s.uFaultTolerancePort;
2710
STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
2712
AutoCaller autoCaller(this);
2713
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2715
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2717
/* @todo deal with running state change. */
2718
HRESULT rc = checkStateDependency(MutableStateDep);
2719
if (FAILED(rc)) return rc;
2721
setModified(IsModified_MachineData);
2723
mUserData->s.uFaultTolerancePort = aPort;
2727
STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
2729
CheckComArgOutPointerValid(aPassword);
2731
AutoCaller autoCaller(this);
2732
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2734
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2736
mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
2741
STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
2743
AutoCaller autoCaller(this);
2744
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2746
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2748
/* @todo deal with running state change. */
2749
HRESULT rc = checkStateDependency(MutableStateDep);
2750
if (FAILED(rc)) return rc;
2752
setModified(IsModified_MachineData);
2754
mUserData->s.strFaultTolerancePassword = aPassword;
2759
STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
2761
CheckComArgOutPointerValid(aInterval);
2763
AutoCaller autoCaller(this);
2764
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2766
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2768
*aInterval = mUserData->s.uFaultToleranceInterval;
2772
STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
2774
AutoCaller autoCaller(this);
2775
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2777
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2779
/* @todo deal with running state change. */
2780
HRESULT rc = checkStateDependency(MutableStateDep);
2781
if (FAILED(rc)) return rc;
2783
setModified(IsModified_MachineData);
2785
mUserData->s.uFaultToleranceInterval = aInterval;
2789
STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
2791
CheckComArgOutPointerValid(aEnabled);
2793
AutoCaller autoCaller(this);
2794
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2796
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2798
*aEnabled = mUserData->s.fRTCUseUTC;
2803
STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
2805
AutoCaller autoCaller(this);
2806
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2808
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2810
/* Only allow it to be set to true when PoweredOff or Aborted.
2811
(Clearing it is always permitted.) */
2813
&& mData->mRegistered
2814
&& ( !isSessionMachine()
2815
|| ( mData->mMachineState != MachineState_PoweredOff
2816
&& mData->mMachineState != MachineState_Teleported
2817
&& mData->mMachineState != MachineState_Aborted
2821
return setError(VBOX_E_INVALID_VM_STATE,
2822
tr("The machine is not powered off (state is %s)"),
2823
Global::stringifyMachineState(mData->mMachineState));
2825
setModified(IsModified_MachineData);
2827
mUserData->s.fRTCUseUTC = !!aEnabled;
2832
STDMETHODIMP Machine::COMGETTER(IoCacheEnabled)(BOOL *aEnabled)
2834
CheckComArgOutPointerValid(aEnabled);
2836
AutoCaller autoCaller(this);
2837
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2839
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2841
*aEnabled = mHWData->mIoCacheEnabled;
2846
STDMETHODIMP Machine::COMSETTER(IoCacheEnabled)(BOOL aEnabled)
2848
AutoCaller autoCaller(this);
2849
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2851
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2853
HRESULT rc = checkStateDependency(MutableStateDep);
2854
if (FAILED(rc)) return rc;
2856
setModified(IsModified_MachineData);
2858
mHWData->mIoCacheEnabled = aEnabled;
2863
STDMETHODIMP Machine::COMGETTER(IoCacheSize)(ULONG *aIoCacheSize)
2865
CheckComArgOutPointerValid(aIoCacheSize);
2867
AutoCaller autoCaller(this);
2868
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2870
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2872
*aIoCacheSize = mHWData->mIoCacheSize;
2877
STDMETHODIMP Machine::COMSETTER(IoCacheSize)(ULONG aIoCacheSize)
2879
AutoCaller autoCaller(this);
2880
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2882
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2884
HRESULT rc = checkStateDependency(MutableStateDep);
2885
if (FAILED(rc)) return rc;
2887
setModified(IsModified_MachineData);
2889
mHWData->mIoCacheSize = aIoCacheSize;
2896
* @note Locks objects!
2898
STDMETHODIMP Machine::LockMachine(ISession *aSession,
2899
LockType_T lockType)
2901
CheckComArgNotNull(aSession);
2903
AutoCaller autoCaller(this);
2904
if (FAILED(autoCaller.rc())) return autoCaller.rc();
2906
/* check the session state */
2907
SessionState_T state;
2908
HRESULT rc = aSession->COMGETTER(State)(&state);
2909
if (FAILED(rc)) return rc;
2911
if (state != SessionState_Unlocked)
2912
return setError(VBOX_E_INVALID_OBJECT_STATE,
2913
tr("The given session is busy"));
2915
// get the client's IInternalSessionControl interface
2916
ComPtr<IInternalSessionControl> pSessionControl = aSession;
2917
ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
2920
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2922
if (!mData->mRegistered)
2923
return setError(E_UNEXPECTED,
2924
tr("The machine '%s' is not registered"),
2925
mUserData->s.strName.c_str());
2927
LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2929
SessionState_T oldState = mData->mSession.mState;
2930
/* Hack: in case the session is closing and there is a progress object
2931
* which allows waiting for the session to be closed, take the opportunity
2932
* and do a limited wait (max. 1 second). This helps a lot when the system
2933
* is busy and thus session closing can take a little while. */
2934
if ( mData->mSession.mState == SessionState_Unlocking
2935
&& mData->mSession.mProgress)
2938
mData->mSession.mProgress->WaitForCompletion(1000);
2940
LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2944
if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
2945
&& (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
2948
// OK, share the session... we are now dealing with three processes:
2949
// 1) VBoxSVC (where this code runs);
2950
// 2) process C: the caller's client process (who wants a shared session);
2951
// 3) process W: the process which already holds the write lock on the machine (write-locking session)
2953
// copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
2954
ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
2955
ComAssertRet(!pSessionW.isNull(), E_FAIL);
2956
ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
2957
AssertReturn(!pSessionMachine.isNull(), E_FAIL);
2960
* Leave the lock before calling the client process. It's safe here
2961
* since the only thing to do after we get the lock again is to add
2962
* the remote control to the list (which doesn't directly influence
2967
// get the console of the session holding the write lock (this is a remote call)
2968
ComPtr<IConsole> pConsoleW;
2969
LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
2970
rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
2971
LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
2973
// the failure may occur w/o any error info (from RPC), so provide one
2974
return setError(VBOX_E_VM_ERROR,
2975
tr("Failed to get a console object from the direct session (%Rrc)"), rc);
2977
ComAssertRet(!pConsoleW.isNull(), E_FAIL);
2979
// share the session machine and W's console with the caller's session
2980
LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
2981
rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
2982
LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
2985
// the failure may occur w/o any error info (from RPC), so provide one
2986
return setError(VBOX_E_VM_ERROR,
2987
tr("Failed to assign the machine to the session (%Rrc)"), rc);
2990
// need to revalidate the state after entering the lock again
2991
if (mData->mSession.mState != SessionState_Locked)
2993
pSessionControl->Uninitialize();
2994
return setError(VBOX_E_INVALID_SESSION_STATE,
2995
tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
2996
mUserData->s.strName.c_str());
2999
// add the caller's session to the list
3000
mData->mSession.mRemoteControls.push_back(pSessionControl);
3002
else if ( mData->mSession.mState == SessionState_Locked
3003
|| mData->mSession.mState == SessionState_Unlocking
3006
// sharing not permitted, or machine still unlocking:
3007
return setError(VBOX_E_INVALID_OBJECT_STATE,
3008
tr("The machine '%s' is already locked for a session (or being unlocked)"),
3009
mUserData->s.strName.c_str());
3013
// machine is not locked: then write-lock the machine (create the session machine)
3016
AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3018
// get the caller's session PID
3019
RTPROCESS pid = NIL_RTPROCESS;
3020
AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3021
pSessionControl->GetPID((ULONG*)&pid);
3022
Assert(pid != NIL_RTPROCESS);
3024
bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3026
if (fLaunchingVMProcess)
3028
// this machine is awaiting for a spawning session to be opened:
3029
// then the calling process must be the one that got started by
3030
// launchVMProcess()
3032
LogFlowThisFunc(("mSession.mPid=%d(0x%x)\n", mData->mSession.mPid, mData->mSession.mPid));
3033
LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3035
if (mData->mSession.mPid != pid)
3036
return setError(E_ACCESSDENIED,
3037
tr("An unexpected process (PID=0x%08X) has tried to lock the "
3038
"machine '%s', while only the process started by launchVMProcess (PID=0x%08X) is allowed"),
3039
pid, mUserData->s.strName.c_str(), mData->mSession.mPid);
3042
// create the mutable SessionMachine from the current machine
3043
ComObjPtr<SessionMachine> sessionMachine;
3044
sessionMachine.createObject();
3045
rc = sessionMachine->init(this);
3048
/* NOTE: doing return from this function after this point but
3049
* before the end is forbidden since it may call SessionMachine::uninit()
3050
* (through the ComObjPtr's destructor) which requests the VirtualBox write
3051
* lock while still holding the Machine lock in alock so that a deadlock
3052
* is possible due to the wrong lock order. */
3057
* Set the session state to Spawning to protect against subsequent
3058
* attempts to open a session and to unregister the machine after
3059
* we leave the lock.
3061
SessionState_T origState = mData->mSession.mState;
3062
mData->mSession.mState = SessionState_Spawning;
3065
* Leave the lock before calling the client process -- it will call
3066
* Machine/SessionMachine methods. Leaving the lock here is quite safe
3067
* because the state is Spawning, so that openRemotesession() and
3068
* openExistingSession() calls will fail. This method, called before we
3069
* enter the lock again, will fail because of the wrong PID.
3071
* Note that mData->mSession.mRemoteControls accessed outside
3072
* the lock may not be modified when state is Spawning, so it's safe.
3076
LogFlowThisFunc(("Calling AssignMachine()...\n"));
3077
rc = pSessionControl->AssignMachine(sessionMachine);
3078
LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3080
/* The failure may occur w/o any error info (from RPC), so provide one */
3082
setError(VBOX_E_VM_ERROR,
3083
tr("Failed to assign the machine to the session (%Rrc)"), rc);
3086
&& fLaunchingVMProcess
3089
/* complete the remote session initialization */
3091
/* get the console from the direct session */
3092
ComPtr<IConsole> console;
3093
rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3096
if (SUCCEEDED(rc) && !console)
3098
ComAssert(!!console);
3102
/* assign machine & console to the remote session */
3106
* after openRemoteSession(), the first and the only
3107
* entry in remoteControls is that remote session
3109
LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3110
rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3111
LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3113
/* The failure may occur w/o any error info (from RPC), so provide one */
3115
setError(VBOX_E_VM_ERROR,
3116
tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3120
pSessionControl->Uninitialize();
3123
/* enter the lock again */
3126
/* Restore the session state */
3127
mData->mSession.mState = origState;
3130
// finalize spawning anyway (this is why we don't return on errors above)
3131
if (fLaunchingVMProcess)
3133
/* Note that the progress object is finalized later */
3134
/** @todo Consider checking mData->mSession.mProgress for cancellation
3137
/* We don't reset mSession.mPid here because it is necessary for
3138
* SessionMachine::uninit() to reap the child process later. */
3142
/* Close the remote session, remove the remote control from the list
3143
* and reset session state to Closed (@note keep the code in sync
3144
* with the relevant part in openSession()). */
3146
Assert(mData->mSession.mRemoteControls.size() == 1);
3147
if (mData->mSession.mRemoteControls.size() == 1)
3149
ErrorInfoKeeper eik;
3150
mData->mSession.mRemoteControls.front()->Uninitialize();
3153
mData->mSession.mRemoteControls.clear();
3154
mData->mSession.mState = SessionState_Unlocked;
3159
/* memorize PID of the directly opened session */
3161
mData->mSession.mPid = pid;
3166
/* memorize the direct session control and cache IUnknown for it */
3167
mData->mSession.mDirectControl = pSessionControl;
3168
mData->mSession.mState = SessionState_Locked;
3169
/* associate the SessionMachine with this Machine */
3170
mData->mSession.mMachine = sessionMachine;
3172
/* request an IUnknown pointer early from the remote party for later
3173
* identity checks (it will be internally cached within mDirectControl
3174
* at least on XPCOM) */
3175
ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3179
/* Leave the lock since SessionMachine::uninit() locks VirtualBox which
3180
* would break the lock order */
3183
/* uninitialize the created session machine on failure */
3185
sessionMachine->uninit();
3192
* tell the client watcher thread to update the set of
3193
* machines that have open sessions
3195
mParent->updateClientWatcher();
3197
if (oldState != SessionState_Locked)
3199
mParent->onSessionStateChange(getId(), SessionState_Locked);
3206
* @note Locks objects!
3208
STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3210
IN_BSTR aEnvironment,
3211
IProgress **aProgress)
3213
CheckComArgNotNull(aSession);
3214
CheckComArgStrNotEmptyOrNull(aType);
3215
CheckComArgOutPointerValid(aProgress);
3217
AutoCaller autoCaller(this);
3218
if (FAILED(autoCaller.rc())) return autoCaller.rc();
3220
/* check the session state */
3221
SessionState_T state;
3222
HRESULT rc = aSession->COMGETTER(State)(&state);
3223
if (FAILED(rc)) return rc;
3225
if (state != SessionState_Unlocked)
3226
return setError(VBOX_E_INVALID_OBJECT_STATE,
3227
tr("The given session is busy"));
3229
/* get the IInternalSessionControl interface */
3230
ComPtr<IInternalSessionControl> control = aSession;
3231
ComAssertMsgRet(!!control, ("No IInternalSessionControl interface"),
3234
/* get the teleporter enable state for the progress object init. */
3235
BOOL fTeleporterEnabled;
3236
rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3240
/* create a progress object */
3241
ComObjPtr<ProgressProxy> progress;
3242
progress.createObject();
3243
rc = progress->init(mParent,
3244
static_cast<IMachine*>(this),
3245
Bstr(tr("Spawning session")).raw(),
3246
TRUE /* aCancelable */,
3247
fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3248
Bstr(tr("Spawning session")).raw(),
3249
2 /* uFirstOperationWeight */,
3250
fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3253
rc = openRemoteSession(control, aType, aEnvironment, progress);
3256
progress.queryInterfaceTo(aProgress);
3258
/* signal the client watcher thread */
3259
mParent->updateClientWatcher();
3262
mParent->onSessionStateChange(getId(), SessionState_Spawning);
3269
STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3271
if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3272
return setError(E_INVALIDARG,
3273
tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3274
aPosition, SchemaDefs::MaxBootPosition);
3276
if (aDevice == DeviceType_USB)
3277
return setError(E_NOTIMPL,
3278
tr("Booting from USB device is currently not supported"));
3280
AutoCaller autoCaller(this);
3281
if (FAILED(autoCaller.rc())) return autoCaller.rc();
3283
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3285
HRESULT rc = checkStateDependency(MutableStateDep);
3286
if (FAILED(rc)) return rc;
3288
setModified(IsModified_MachineData);
3290
mHWData->mBootOrder[aPosition - 1] = aDevice;
3295
STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3297
if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3298
return setError(E_INVALIDARG,
3299
tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3300
aPosition, SchemaDefs::MaxBootPosition);
3302
AutoCaller autoCaller(this);
3303
if (FAILED(autoCaller.rc())) return autoCaller.rc();
3305
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3307
*aDevice = mHWData->mBootOrder[aPosition - 1];
3312
STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3313
LONG aControllerPort,
3318
LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aId=\"%ls\"\n",
3319
aControllerName, aControllerPort, aDevice, aType, aMedium));
3321
CheckComArgStrNotEmptyOrNull(aControllerName);
3323
AutoCaller autoCaller(this);
3324
if (FAILED(autoCaller.rc())) return autoCaller.rc();
3326
// request the host lock first, since might be calling Host methods for getting host drives;
3327
// next, protect the media tree all the while we're in here, as well as our member variables
3328
AutoMultiWriteLock2 alock(mParent->host()->lockHandle(),
3329
this->lockHandle() COMMA_LOCKVAL_SRC_POS);
3330
AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3332
HRESULT rc = checkStateDependency(MutableStateDep);
3333
if (FAILED(rc)) return rc;
3335
GuidList llRegistriesThatNeedSaving;
3337
/// @todo NEWMEDIA implicit machine registration
3338
if (!mData->mRegistered)
3339
return setError(VBOX_E_INVALID_OBJECT_STATE,
3340
tr("Cannot attach storage devices to an unregistered machine"));
3342
AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3344
if (Global::IsOnlineOrTransient(mData->mMachineState))
3345
return setError(VBOX_E_INVALID_VM_STATE,
3346
tr("Invalid machine state: %s"),
3347
Global::stringifyMachineState(mData->mMachineState));
3349
/* Check for an existing controller. */
3350
ComObjPtr<StorageController> ctl;
3351
rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3352
if (FAILED(rc)) return rc;
3354
// check that the port and device are not out of range
3355
rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3356
if (FAILED(rc)) return rc;
3358
/* check if the device slot is already busy */
3359
MediumAttachment *pAttachTemp;
3360
if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3365
Medium *pMedium = pAttachTemp->getMedium();
3368
AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3369
return setError(VBOX_E_OBJECT_IN_USE,
3370
tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3371
pMedium->getLocationFull().c_str(),
3377
return setError(VBOX_E_OBJECT_IN_USE,
3378
tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3379
aControllerPort, aDevice, aControllerName);
3382
ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3383
if (aMedium && medium.isNull())
3384
return setError(E_INVALIDARG, "The given medium pointer is invalid");
3386
AutoCaller mediumCaller(medium);
3387
if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3389
AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3391
if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3394
return setError(VBOX_E_OBJECT_IN_USE,
3395
tr("Medium '%s' is already attached to this virtual machine"),
3396
medium->getLocationFull().c_str());
3398
if (!medium.isNull())
3400
MediumType_T mtype = medium->getType();
3401
// MediumType_Readonly is also new, but only applies to DVDs and floppies.
3402
// For DVDs it's not written to the config file, so needs no global config
3403
// version bump. For floppies it's a new attribute "type", which is ignored
3404
// by older VirtualBox version, so needs no global config version bump either.
3405
// For hard disks this type is not accepted.
3406
if (mtype == MediumType_MultiAttach)
3408
// This type is new with VirtualBox 4.0 and therefore requires settings
3409
// version 1.11 in the settings backend. Unfortunately it is not enough to do
3410
// the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3411
// two reasons: The medium type is a property of the media registry tree, which
3412
// can reside in the global config file (for pre-4.0 media); we would therefore
3413
// possibly need to bump the global config version. We don't want to do that though
3414
// because that might make downgrading to pre-4.0 impossible.
3415
// As a result, we can only use these two new types if the medium is NOT in the
3417
const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3418
if ( medium->isInRegistry(uuidGlobalRegistry)
3419
|| !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3421
return setError(VBOX_E_INVALID_OBJECT_STATE,
3422
tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3423
"to machines that were created with VirtualBox 4.0 or later"),
3424
medium->getLocationFull().c_str());
3428
bool fIndirect = false;
3429
if (!medium.isNull())
3430
fIndirect = medium->isReadOnly();
3431
bool associate = true;
3435
if ( aType == DeviceType_HardDisk
3436
&& mMediaData.isBackedUp())
3438
const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3440
/* check if the medium was attached to the VM before we started
3441
* changing attachments in which case the attachment just needs to
3443
if ((pAttachTemp = findAttachment(oldAtts, medium)))
3445
AssertReturn(!fIndirect, E_FAIL);
3447
/* see if it's the same bus/channel/device */
3448
if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3450
/* the simplest case: restore the whole attachment
3451
* and return, nothing else to do */
3452
mMediaData->mAttachments.push_back(pAttachTemp);
3456
/* bus/channel/device differ; we need a new attachment object,
3457
* but don't try to associate it again */
3463
/* go further only if the attachment is to be indirect */
3467
/* perform the so called smart attachment logic for indirect
3468
* attachments. Note that smart attachment is only applicable to base
3471
if (medium->getParent().isNull())
3473
/* first, investigate the backup copy of the current hard disk
3474
* attachments to make it possible to re-attach existing diffs to
3475
* another device slot w/o losing their contents */
3476
if (mMediaData.isBackedUp())
3478
const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3480
MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3481
uint32_t foundLevel = 0;
3483
for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
3484
it != oldAtts.end();
3488
MediumAttachment *pAttach = *it;
3489
ComObjPtr<Medium> pMedium = pAttach->getMedium();
3490
Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3491
if (pMedium.isNull())
3494
if (pMedium->getBase(&level) == medium)
3496
/* skip the hard disk if its currently attached (we
3497
* cannot attach the same hard disk twice) */
3498
if (findAttachment(mMediaData->mAttachments,
3502
/* matched device, channel and bus (i.e. attached to the
3503
* same place) will win and immediately stop the search;
3504
* otherwise the attachment that has the youngest
3505
* descendant of medium will be used
3507
if (pAttach->matches(aControllerName, aControllerPort, aDevice))
3509
/* the simplest case: restore the whole attachment
3510
* and return, nothing else to do */
3511
mMediaData->mAttachments.push_back(*it);
3514
else if ( foundIt == oldAtts.end()
3515
|| level > foundLevel /* prefer younger */
3524
if (foundIt != oldAtts.end())
3526
/* use the previously attached hard disk */
3527
medium = (*foundIt)->getMedium();
3528
mediumCaller.attach(medium);
3529
if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3530
mediumLock.attach(medium);
3531
/* not implicit, doesn't require association with this VM */
3534
/* go right to the MediumAttachment creation */
3539
/* must give up the medium lock and medium tree lock as below we
3540
* go over snapshots, which needs a lock with higher lock order. */
3541
mediumLock.release();
3544
/* then, search through snapshots for the best diff in the given
3545
* hard disk's chain to base the new diff on */
3547
ComObjPtr<Medium> base;
3548
ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3551
AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3553
const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
3555
MediumAttachment *pAttachFound = NULL;
3556
uint32_t foundLevel = 0;
3558
for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
3559
it != snapAtts.end();
3562
MediumAttachment *pAttach = *it;
3563
ComObjPtr<Medium> pMedium = pAttach->getMedium();
3564
Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3565
if (pMedium.isNull())
3569
if (pMedium->getBase(&level) == medium)
3571
/* matched device, channel and bus (i.e. attached to the
3572
* same place) will win and immediately stop the search;
3573
* otherwise the attachment that has the youngest
3574
* descendant of medium will be used
3576
if ( pAttach->getDevice() == aDevice
3577
&& pAttach->getPort() == aControllerPort
3578
&& pAttach->getControllerName() == aControllerName
3581
pAttachFound = pAttach;
3584
else if ( !pAttachFound
3585
|| level > foundLevel /* prefer younger */
3588
pAttachFound = pAttach;
3596
base = pAttachFound->getMedium();
3600
snap = snap->getParent();
3603
/* re-lock medium tree and the medium, as we need it below */
3605
mediumLock.acquire();
3607
/* found a suitable diff, use it as a base */
3611
mediumCaller.attach(medium);
3612
if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3613
mediumLock.attach(medium);
3617
Utf8Str strFullSnapshotFolder;
3618
calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3620
ComObjPtr<Medium> diff;
3621
diff.createObject();
3622
rc = diff->init(mParent,
3623
medium->getPreferredDiffFormat(),
3624
strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3625
medium->getFirstRegistryMachineId(), // store this diff in the same registry as the parent
3626
&llRegistriesThatNeedSaving);
3627
if (FAILED(rc)) return rc;
3629
/* Apply the normal locking logic to the entire chain. */
3630
MediumLockList *pMediumLockList(new MediumLockList());
3631
rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
3632
true /* fMediumLockWrite */,
3637
rc = pMediumLockList->Lock();
3640
tr("Could not lock medium when creating diff '%s'"),
3641
diff->getLocationFull().c_str());
3644
/* will leave the lock before the potentially lengthy operation, so
3645
* protect with the special state */
3646
MachineState_T oldState = mData->mMachineState;
3647
setMachineState(MachineState_SettingUp);
3653
rc = medium->createDiffStorage(diff,
3654
MediumVariant_Standard,
3656
NULL /* aProgress */,
3658
&llRegistriesThatNeedSaving);
3664
setMachineState(oldState);
3668
/* Unlock the media and free the associated memory. */
3669
delete pMediumLockList;
3671
if (FAILED(rc)) return rc;
3673
/* use the created diff for the actual attachment */
3675
mediumCaller.attach(medium);
3676
if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3677
mediumLock.attach(medium);
3681
ComObjPtr<MediumAttachment> attachment;
3682
attachment.createObject();
3683
rc = attachment->init(this,
3691
if (FAILED(rc)) return rc;
3693
if (associate && !medium.isNull())
3695
// as the last step, associate the medium to the VM
3696
rc = medium->addBackReference(mData->mUuid);
3697
// here we can fail because of Deleting, or being in process of creating a Diff
3698
if (FAILED(rc)) return rc;
3700
// and decide which medium registry to use now that the medium is attached:
3702
if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
3703
// machine XML is VirtualBox 4.0 or higher:
3704
uuid = getId(); // machine UUID
3706
uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
3708
if (medium->addRegistry(uuid))
3709
// registry actually changed:
3710
mParent->addGuidToListUniquely(llRegistriesThatNeedSaving, uuid);
3713
/* success: finally remember the attachment */
3714
setModified(IsModified_Storage);
3715
mMediaData.backup();
3716
mMediaData->mAttachments.push_back(attachment);
3718
mediumLock.release();
3722
mParent->saveRegistries(llRegistriesThatNeedSaving);
3727
STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
3730
CheckComArgStrNotEmptyOrNull(aControllerName);
3732
LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
3733
aControllerName, aControllerPort, aDevice));
3735
AutoCaller autoCaller(this);
3736
if (FAILED(autoCaller.rc())) return autoCaller.rc();
3738
GuidList llRegistriesThatNeedSaving;
3740
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3742
HRESULT rc = checkStateDependency(MutableStateDep);
3743
if (FAILED(rc)) return rc;
3745
AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3747
if (Global::IsOnlineOrTransient(mData->mMachineState))
3748
return setError(VBOX_E_INVALID_VM_STATE,
3749
tr("Invalid machine state: %s"),
3750
Global::stringifyMachineState(mData->mMachineState));
3752
MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
3757
return setError(VBOX_E_OBJECT_NOT_FOUND,
3758
tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
3759
aDevice, aControllerPort, aControllerName);
3761
rc = detachDevice(pAttach, alock, NULL /* pSnapshot */, &llRegistriesThatNeedSaving);
3766
rc = mParent->saveRegistries(llRegistriesThatNeedSaving);
3771
STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
3772
LONG aDevice, BOOL aPassthrough)
3774
CheckComArgStrNotEmptyOrNull(aControllerName);
3776
LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aPassthrough=%d\n",
3777
aControllerName, aControllerPort, aDevice, aPassthrough));
3779
AutoCaller autoCaller(this);
3780
if (FAILED(autoCaller.rc())) return autoCaller.rc();
3782
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3784
HRESULT rc = checkStateDependency(MutableStateDep);
3785
if (FAILED(rc)) return rc;
3787
AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3789
if (Global::IsOnlineOrTransient(mData->mMachineState))
3790
return setError(VBOX_E_INVALID_VM_STATE,
3791
tr("Invalid machine state: %s"),
3792
Global::stringifyMachineState(mData->mMachineState));
3794
MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
3799
return setError(VBOX_E_OBJECT_NOT_FOUND,
3800
tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
3801
aDevice, aControllerPort, aControllerName);
3804
setModified(IsModified_Storage);
3805
mMediaData.backup();
3807
AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3809
if (pAttach->getType() != DeviceType_DVD)
3810
return setError(E_INVALIDARG,
3811
tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
3812
aDevice, aControllerPort, aControllerName);
3813
pAttach->updatePassthrough(!!aPassthrough);
3818
STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
3819
LONG aDevice, IBandwidthGroup *aBandwidthGroup)
3821
CheckComArgStrNotEmptyOrNull(aControllerName);
3823
LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
3824
aControllerName, aControllerPort, aDevice));
3826
AutoCaller autoCaller(this);
3827
if (FAILED(autoCaller.rc())) return autoCaller.rc();
3829
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3831
HRESULT rc = checkStateDependency(MutableStateDep);
3832
if (FAILED(rc)) return rc;
3834
AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3836
if (Global::IsOnlineOrTransient(mData->mMachineState))
3837
return setError(VBOX_E_INVALID_VM_STATE,
3838
tr("Invalid machine state: %s"),
3839
Global::stringifyMachineState(mData->mMachineState));
3841
MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
3846
return setError(VBOX_E_OBJECT_NOT_FOUND,
3847
tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
3848
aDevice, aControllerPort, aControllerName);
3851
setModified(IsModified_Storage);
3852
mMediaData.backup();
3854
ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
3855
if (aBandwidthGroup && group.isNull())
3856
return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
3858
AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3860
pAttach->updateBandwidthGroup(group);
3866
STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
3867
LONG aControllerPort,
3873
LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aForce=%d\n",
3874
aControllerName, aControllerPort, aDevice, aForce));
3876
CheckComArgStrNotEmptyOrNull(aControllerName);
3878
AutoCaller autoCaller(this);
3879
if (FAILED(autoCaller.rc())) return autoCaller.rc();
3881
// request the host lock first, since might be calling Host methods for getting host drives;
3882
// next, protect the media tree all the while we're in here, as well as our member variables
3883
AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
3885
&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3887
ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
3891
if (pAttach.isNull())
3892
return setError(VBOX_E_OBJECT_NOT_FOUND,
3893
tr("No drive attached to device slot %d on port %d of controller '%ls'"),
3894
aDevice, aControllerPort, aControllerName);
3896
/* Remember previously mounted medium. The medium before taking the
3897
* backup is not necessarily the same thing. */
3898
ComObjPtr<Medium> oldmedium;
3899
oldmedium = pAttach->getMedium();
3901
ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
3902
if (aMedium && pMedium.isNull())
3903
return setError(E_INVALIDARG, "The given medium pointer is invalid");
3905
AutoCaller mediumCaller(pMedium);
3906
if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3908
AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3911
DeviceType_T mediumType = pAttach->getType();
3914
case DeviceType_DVD:
3915
case DeviceType_Floppy:
3919
return setError(VBOX_E_INVALID_OBJECT_STATE,
3920
tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
3927
setModified(IsModified_Storage);
3928
mMediaData.backup();
3930
GuidList llRegistriesThatNeedSaving;
3933
// The backup operation makes the pAttach reference point to the
3934
// old settings. Re-get the correct reference.
3935
pAttach = findAttachment(mMediaData->mAttachments,
3939
AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3940
if (!oldmedium.isNull())
3941
oldmedium->removeBackReference(mData->mUuid);
3942
if (!pMedium.isNull())
3944
pMedium->addBackReference(mData->mUuid);
3946
// and decide which medium registry to use now that the medium is attached:
3948
if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
3949
// machine XML is VirtualBox 4.0 or higher:
3950
uuid = getId(); // machine UUID
3952
uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
3954
if (pMedium->addRegistry(uuid))
3955
// registry actually changed:
3956
mParent->addGuidToListUniquely(llRegistriesThatNeedSaving, uuid);
3959
pAttach->updateMedium(pMedium);
3962
setModified(IsModified_Storage);
3964
mediumLock.release();
3965
multiLock.release();
3966
rc = onMediumChange(pAttach, aForce);
3967
multiLock.acquire();
3968
mediumLock.acquire();
3970
/* On error roll back this change only. */
3973
if (!pMedium.isNull())
3974
pMedium->removeBackReference(mData->mUuid);
3975
pAttach = findAttachment(mMediaData->mAttachments,
3979
/* If the attachment is gone in the meantime, bail out. */
3980
if (pAttach.isNull())
3982
AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3983
if (!oldmedium.isNull())
3984
oldmedium->addBackReference(mData->mUuid);
3985
pAttach->updateMedium(oldmedium);
3988
mediumLock.release();
3989
multiLock.release();
3991
mParent->saveRegistries(llRegistriesThatNeedSaving);
3996
STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
3997
LONG aControllerPort,
4001
LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
4002
aControllerName, aControllerPort, aDevice));
4004
CheckComArgStrNotEmptyOrNull(aControllerName);
4005
CheckComArgOutPointerValid(aMedium);
4007
AutoCaller autoCaller(this);
4008
if (FAILED(autoCaller.rc())) return autoCaller.rc();
4010
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4014
ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4018
if (pAttach.isNull())
4019
return setError(VBOX_E_OBJECT_NOT_FOUND,
4020
tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4021
aDevice, aControllerPort, aControllerName);
4023
pAttach->getMedium().queryInterfaceTo(aMedium);
4028
STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4030
CheckComArgOutPointerValid(port);
4031
CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4033
AutoCaller autoCaller(this);
4034
if (FAILED(autoCaller.rc())) return autoCaller.rc();
4036
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4038
mSerialPorts[slot].queryInterfaceTo(port);
4043
STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4045
CheckComArgOutPointerValid(port);
4046
CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4048
AutoCaller autoCaller(this);
4049
if (FAILED(autoCaller.rc())) return autoCaller.rc();
4051
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4053
mParallelPorts[slot].queryInterfaceTo(port);
4058
STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4060
CheckComArgOutPointerValid(adapter);
4061
CheckComArgExpr(slot, slot < RT_ELEMENTS(mNetworkAdapters));
4063
AutoCaller autoCaller(this);
4064
if (FAILED(autoCaller.rc())) return autoCaller.rc();
4066
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4068
mNetworkAdapters[slot].queryInterfaceTo(adapter);
4073
STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4075
if (ComSafeArrayOutIsNull(aKeys))
4078
AutoCaller autoCaller(this);
4079
if (FAILED(autoCaller.rc())) return autoCaller.rc();
4081
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4083
com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4085
for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4086
it != mData->pMachineConfigFile->mapExtraDataItems.end();
4089
const Utf8Str &strKey = it->first;
4090
strKey.cloneTo(&saKeys[i]);
4092
saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4098
* @note Locks this object for reading.
4100
STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4103
CheckComArgStrNotEmptyOrNull(aKey);
4104
CheckComArgOutPointerValid(aValue);
4106
AutoCaller autoCaller(this);
4107
if (FAILED(autoCaller.rc())) return autoCaller.rc();
4109
/* start with nothing found */
4110
Bstr bstrResult("");
4112
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4114
settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4115
if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4117
bstrResult = it->second; // source is a Utf8Str
4119
/* return the result to caller (may be empty) */
4120
bstrResult.cloneTo(aValue);
4126
* @note Locks mParent for writing + this object for writing.
4128
STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4130
CheckComArgStrNotEmptyOrNull(aKey);
4132
AutoCaller autoCaller(this);
4133
if (FAILED(autoCaller.rc())) return autoCaller.rc();
4135
Utf8Str strKey(aKey);
4136
Utf8Str strValue(aValue);
4137
Utf8Str strOldValue; // empty
4139
// locking note: we only hold the read lock briefly to look up the old value,
4140
// then release it and call the onExtraCanChange callbacks. There is a small
4141
// chance of a race insofar as the callback might be called twice if two callers
4142
// change the same key at the same time, but that's a much better solution
4143
// than the deadlock we had here before. The actual changing of the extradata
4144
// is then performed under the write lock and race-free.
4146
// look up the old value first; if nothing has changed then we need not do anything
4148
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4149
settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4150
if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4151
strOldValue = it->second;
4155
if ((fChanged = (strOldValue != strValue)))
4157
// ask for permission from all listeners outside the locks;
4158
// onExtraDataCanChange() only briefly requests the VirtualBox
4159
// lock to copy the list of callbacks to invoke
4161
Bstr bstrValue(aValue);
4163
if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4165
const char *sep = error.isEmpty() ? "" : ": ";
4166
CBSTR err = error.raw();
4167
LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4169
return setError(E_ACCESSDENIED,
4170
tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
4177
// data is changing and change not vetoed: then write it out under the lock
4178
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4180
if (isSnapshotMachine())
4182
HRESULT rc = checkStateDependency(MutableStateDep);
4183
if (FAILED(rc)) return rc;
4186
if (strValue.isEmpty())
4187
mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
4189
mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
4190
// creates a new key if needed
4192
bool fNeedsGlobalSaveSettings = false;
4193
saveSettings(&fNeedsGlobalSaveSettings);
4195
if (fNeedsGlobalSaveSettings)
4197
// save the global settings; for that we should hold only the VirtualBox lock
4199
AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4200
mParent->saveSettings();
4204
// fire notification outside the lock
4206
mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
4211
STDMETHODIMP Machine::SaveSettings()
4213
AutoCaller autoCaller(this);
4214
if (FAILED(autoCaller.rc())) return autoCaller.rc();
4216
AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4218
/* when there was auto-conversion, we want to save the file even if
4219
* the VM is saved */
4220
HRESULT rc = checkStateDependency(MutableStateDep);
4221
if (FAILED(rc)) return rc;
4223
/* the settings file path may never be null */
4224
ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4226
/* save all VM data excluding snapshots */
4227
bool fNeedsGlobalSaveSettings = false;
4228
rc = saveSettings(&fNeedsGlobalSaveSettings);
4231
if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4233
// save the global settings; for that we should hold only the VirtualBox lock
4234
AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4235
rc = mParent->saveSettings();
4241
STDMETHODIMP Machine::DiscardSettings()
4243
AutoCaller autoCaller(this);
4244
if (FAILED(autoCaller.rc())) return autoCaller.rc();
4246
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4248
HRESULT rc = checkStateDependency(MutableStateDep);
4249
if (FAILED(rc)) return rc;
4252
* during this rollback, the session will be notified if data has
4253
* been actually changed
4255
rollback(true /* aNotify */);
4260
/** @note Locks objects! */
4261
STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
4262
ComSafeArrayOut(IMedium*, aMedia))
4264
// use AutoLimitedCaller because this call is valid on inaccessible machines as well
4265
AutoLimitedCaller autoCaller(this);
4266
AssertComRCReturnRC(autoCaller.rc());
4268
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4272
if (mData->mSession.mState != SessionState_Unlocked)
4273
return setError(VBOX_E_INVALID_OBJECT_STATE,
4274
tr("Cannot unregister the machine '%s' while it is locked"),
4275
mUserData->s.strName.c_str());
4277
// wait for state dependents to drop to zero
4278
ensureNoStateDependencies();
4280
if (!mData->mAccessible)
4282
// inaccessible maschines can only be unregistered; uninitialize ourselves
4283
// here because currently there may be no unregistered that are inaccessible
4284
// (this state combination is not supported). Note releasing the caller and
4285
// leaving the lock before calling uninit()
4287
autoCaller.release();
4291
mParent->unregisterMachine(this, id);
4292
// calls VirtualBox::saveSettings()
4299
// discard saved state
4300
if (mData->mMachineState == MachineState_Saved)
4302
// add the saved state file to the list of files the caller should delete
4303
Assert(!mSSData->mStateFilePath.isEmpty());
4304
mData->llFilesToDelete.push_back(mSSData->mStateFilePath);
4306
mSSData->mStateFilePath.setNull();
4308
// unconditionally set the machine state to powered off, we now
4309
// know no session has locked the machine
4310
mData->mMachineState = MachineState_PoweredOff;
4313
size_t cSnapshots = 0;
4314
if (mData->mFirstSnapshot)
4315
cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
4316
if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
4317
// fail now before we start detaching media
4318
return setError(VBOX_E_INVALID_OBJECT_STATE,
4319
tr("Cannot unregister the machine '%s' because it has %d snapshots"),
4320
mUserData->s.strName.c_str(), cSnapshots);
4322
// this list collects the medium objects from all medium attachments
4323
// which got detached from the machine and its snapshots, in the following
4325
// 1) media from machine attachments (these have the "leaf" attachments with snapshots
4326
// and must be closed first, or closing the parents will fail because they will
4328
// 2) media from the youngest snapshots followed by those from the parent snapshots until
4329
// the root ("first") snapshot of the machine
4330
// This order allows for closing the media on this list from the beginning to the end
4331
// without getting "media in use" errors.
4334
if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
4335
&& mMediaData->mAttachments.size()
4338
// we have media attachments: detach them all and add the Medium objects to our list
4339
if (cleanupMode != CleanupMode_UnregisterOnly)
4340
detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
4342
return setError(VBOX_E_INVALID_OBJECT_STATE,
4343
tr("Cannot unregister the machine '%s' because it has %d media attachments"),
4344
mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
4349
// autoCleanup must be true here, or we would have failed above
4351
// add the media from the medium attachments of the snapshots to llMedia
4352
// as well, after the "main" machine media; Snapshot::uninitRecursively()
4353
// calls Machine::detachAllMedia() for the snapshot machine, recursing
4354
// into the children first
4356
// Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4357
MachineState_T oldState = mData->mMachineState;
4358
mData->mMachineState = MachineState_DeletingSnapshot;
4360
// make a copy of the first snapshot so the refcount does not drop to 0
4361
// in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
4362
// because of the AutoCaller voodoo)
4363
ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4366
pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
4368
mData->mMachineState = oldState;
4377
// commit all the media changes made above
4380
mData->mRegistered = false;
4382
// machine lock no longer needed
4385
// return media to caller
4386
SafeIfaceArray<IMedium> sfaMedia(llMedia);
4387
sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
4389
mParent->unregisterMachine(this, id);
4390
// calls VirtualBox::saveSettings()
4395
struct Machine::DeleteTask
4397
ComObjPtr<Machine> pMachine;
4398
std::list<Utf8Str> llFilesToDelete;
4399
ComObjPtr<Progress> pProgress;
4400
GuidList llRegistriesThatNeedSaving;
4403
STDMETHODIMP Machine::Delete(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
4407
AutoCaller autoCaller(this);
4408
if (FAILED(autoCaller.rc())) return autoCaller.rc();
4410
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4412
HRESULT rc = checkStateDependency(MutableStateDep);
4413
if (FAILED(rc)) return rc;
4415
if (mData->mRegistered)
4416
return setError(VBOX_E_INVALID_VM_STATE,
4417
tr("Cannot delete settings of a registered machine"));
4419
DeleteTask *pTask = new DeleteTask;
4420
pTask->pMachine = this;
4421
com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
4423
// collect files to delete
4424
pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
4426
for (size_t i = 0; i < sfaMedia.size(); ++i)
4428
IMedium *pIMedium(sfaMedia[i]);
4429
Medium *pMedium = static_cast<Medium*>(pIMedium);
4430
AutoCaller mediumAutoCaller(pMedium);
4431
if (FAILED(mediumAutoCaller.rc())) return mediumAutoCaller.rc();
4433
Utf8Str bstrLocation = pMedium->getLocationFull();
4435
bool fDoesMediumNeedFileDeletion = pMedium->isMediumFormatFile();
4437
// close the medium now; if that succeeds, then that means the medium is no longer
4438
// in use and we can add it to the list of files to delete
4439
rc = pMedium->close(&pTask->llRegistriesThatNeedSaving,
4441
if (SUCCEEDED(rc) && fDoesMediumNeedFileDeletion)
4442
pTask->llFilesToDelete.push_back(bstrLocation);
4444
if (mData->pMachineConfigFile->fileExists())
4445
pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
4447
pTask->pProgress.createObject();
4448
pTask->pProgress->init(getVirtualBox(),
4449
static_cast<IMachine*>(this) /* aInitiator */,
4450
Bstr(tr("Deleting files")).raw(),
4451
true /* fCancellable */,
4452
pTask->llFilesToDelete.size() + 1, // cOperations
4453
BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
4455
int vrc = RTThreadCreate(NULL,
4456
Machine::deleteThread,
4459
RTTHREADTYPE_MAIN_WORKER,
4463
pTask->pProgress.queryInterfaceTo(aProgress);
4465
if (RT_FAILURE(vrc))
4468
return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
4477
* Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
4478
* calls Machine::deleteTaskWorker() on the actual machine object.
4484
DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
4488
DeleteTask *pTask = (DeleteTask*)pvUser;
4490
Assert(pTask->pMachine);
4491
Assert(pTask->pProgress);
4493
HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
4494
pTask->pProgress->notifyComplete(rc);
4502
return VINF_SUCCESS;
4506
* Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
4510
HRESULT Machine::deleteTaskWorker(DeleteTask &task)
4512
AutoCaller autoCaller(this);
4513
if (FAILED(autoCaller.rc())) return autoCaller.rc();
4515
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4517
ULONG uLogHistoryCount = 3;
4518
ComPtr<ISystemProperties> systemProperties;
4519
mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
4520
if (!systemProperties.isNull())
4521
systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
4523
// delete the files pushed on the task list by Machine::Delete()
4524
// (this includes saved states of the machine and snapshots and
4525
// medium storage files from the IMedium list passed in, and the
4526
// machine XML file)
4527
std::list<Utf8Str>::const_iterator it = task.llFilesToDelete.begin();
4528
while (it != task.llFilesToDelete.end())
4530
const Utf8Str &strFile = *it;
4531
LogFunc(("Deleting file %s\n", strFile.c_str()));
4532
RTFileDelete(strFile.c_str());
4535
if (it == task.llFilesToDelete.end())
4537
task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
4541
task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
4544
/* delete the settings only when the file actually exists */
4545
if (mData->pMachineConfigFile->fileExists())
4547
/* Delete any backup or uncommitted XML files. Ignore failures.
4548
See the fSafe parameter of xml::XmlFileWriter::write for details. */
4549
/** @todo Find a way to avoid referring directly to iprt/xml.h here. */
4550
Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
4551
RTFileDelete(otherXml.c_str());
4552
otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
4553
RTFileDelete(otherXml.c_str());
4555
/* delete the Logs folder, nothing important should be left
4556
* there (we don't check for errors because the user might have
4557
* some private files there that we don't want to delete) */
4559
getLogFolder(logFolder);
4560
Assert(logFolder.length());
4561
if (RTDirExists(logFolder.c_str()))
4563
/* Delete all VBox.log[.N] files from the Logs folder
4564
* (this must be in sync with the rotation logic in
4565
* Console::powerUpThread()). Also, delete the VBox.png[.N]
4566
* files that may have been created by the GUI. */
4567
Utf8Str log = Utf8StrFmt("%s%cVBox.log",
4568
logFolder.c_str(), RTPATH_DELIMITER);
4569
RTFileDelete(log.c_str());
4570
log = Utf8StrFmt("%s%cVBox.png",
4571
logFolder.c_str(), RTPATH_DELIMITER);
4572
RTFileDelete(log.c_str());
4573
for (int i = uLogHistoryCount; i > 0; i--)
4575
log = Utf8StrFmt("%s%cVBox.log.%d",
4576
logFolder.c_str(), RTPATH_DELIMITER, i);
4577
RTFileDelete(log.c_str());
4578
log = Utf8StrFmt("%s%cVBox.png.%d",
4579
logFolder.c_str(), RTPATH_DELIMITER, i);
4580
RTFileDelete(log.c_str());
4583
RTDirRemove(logFolder.c_str());
4586
/* delete the Snapshots folder, nothing important should be left
4587
* there (we don't check for errors because the user might have
4588
* some private files there that we don't want to delete) */
4589
Utf8Str strFullSnapshotFolder;
4590
calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4591
Assert(!strFullSnapshotFolder.isEmpty());
4592
if (RTDirExists(strFullSnapshotFolder.c_str()))
4593
RTDirRemove(strFullSnapshotFolder.c_str());
4595
// delete the directory that contains the settings file, but only
4596
// if it matches the VM name
4597
Utf8Str settingsDir;
4598
if (isInOwnDir(&settingsDir))
4599
RTDirRemove(settingsDir.c_str());
4604
mParent->saveRegistries(task.llRegistriesThatNeedSaving);
4609
STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
4611
CheckComArgOutPointerValid(aSnapshot);
4613
AutoCaller autoCaller(this);
4614
if (FAILED(autoCaller.rc())) return autoCaller.rc();
4616
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4618
ComObjPtr<Snapshot> pSnapshot;
4621
if (!aNameOrId || !*aNameOrId)
4622
// null case (caller wants root snapshot): findSnapshotById() handles this
4623
rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
4626
Guid uuid(aNameOrId);
4627
if (!uuid.isEmpty())
4628
rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
4630
rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
4632
pSnapshot.queryInterfaceTo(aSnapshot);
4637
STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
4639
CheckComArgStrNotEmptyOrNull(aName);
4640
CheckComArgStrNotEmptyOrNull(aHostPath);
4642
AutoCaller autoCaller(this);
4643
if (FAILED(autoCaller.rc())) return autoCaller.rc();
4645
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4647
HRESULT rc = checkStateDependency(MutableStateDep);
4648
if (FAILED(rc)) return rc;
4650
ComObjPtr<SharedFolder> sharedFolder;
4651
rc = findSharedFolder(aName, sharedFolder, false /* aSetError */);
4653
return setError(VBOX_E_OBJECT_IN_USE,
4654
tr("Shared folder named '%ls' already exists"),
4657
sharedFolder.createObject();
4658
rc = sharedFolder->init(getMachine(), aName, aHostPath, aWritable, aAutoMount);
4659
if (FAILED(rc)) return rc;
4661
setModified(IsModified_SharedFolders);
4663
mHWData->mSharedFolders.push_back(sharedFolder);
4665
/* inform the direct session if any */
4667
onSharedFolderChange();
4672
STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
4674
CheckComArgStrNotEmptyOrNull(aName);
4676
AutoCaller autoCaller(this);
4677
if (FAILED(autoCaller.rc())) return autoCaller.rc();
4679
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4681
HRESULT rc = checkStateDependency(MutableStateDep);
4682
if (FAILED(rc)) return rc;
4684
ComObjPtr<SharedFolder> sharedFolder;
4685
rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
4686
if (FAILED(rc)) return rc;
4688
setModified(IsModified_SharedFolders);
4690
mHWData->mSharedFolders.remove(sharedFolder);
4692
/* inform the direct session if any */
4694
onSharedFolderChange();
4699
STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
4701
CheckComArgOutPointerValid(aCanShow);
4706
AutoCaller autoCaller(this);
4707
AssertComRCReturnRC(autoCaller.rc());
4709
ComPtr<IInternalSessionControl> directControl;
4711
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4713
if (mData->mSession.mState != SessionState_Locked)
4714
return setError(VBOX_E_INVALID_VM_STATE,
4715
tr("Machine is not locked for session (session state: %s)"),
4716
Global::stringifySessionState(mData->mSession.mState));
4718
directControl = mData->mSession.mDirectControl;
4721
/* ignore calls made after #OnSessionEnd() is called */
4726
return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
4729
STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
4731
CheckComArgOutPointerValid(aWinId);
4733
AutoCaller autoCaller(this);
4734
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4736
ComPtr<IInternalSessionControl> directControl;
4738
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4740
if (mData->mSession.mState != SessionState_Locked)
4741
return setError(E_FAIL,
4742
tr("Machine is not locked for session (session state: %s)"),
4743
Global::stringifySessionState(mData->mSession.mState));
4745
directControl = mData->mSession.mDirectControl;
4748
/* ignore calls made after #OnSessionEnd() is called */
4753
return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
4756
#ifdef VBOX_WITH_GUEST_PROPS
4758
* Look up a guest property in VBoxSVC's internal structures.
4760
HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
4765
using namespace guestProp;
4767
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4768
Utf8Str strName(aName);
4769
HWData::GuestPropertyList::const_iterator it;
4771
for (it = mHWData->mGuestProperties.begin();
4772
it != mHWData->mGuestProperties.end(); ++it)
4774
if (it->strName == strName)
4776
char szFlags[MAX_FLAGS_LEN + 1];
4777
it->strValue.cloneTo(aValue);
4778
*aTimestamp = it->mTimestamp;
4779
writeFlags(it->mFlags, szFlags);
4780
Bstr(szFlags).cloneTo(aFlags);
4788
* Query the VM that a guest property belongs to for the property.
4789
* @returns E_ACCESSDENIED if the VM process is not available or not
4790
* currently handling queries and the lookup should then be done in
4793
HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
4799
ComPtr<IInternalSessionControl> directControl;
4800
directControl = mData->mSession.mDirectControl;
4802
/* fail if we were called after #OnSessionEnd() is called. This is a
4803
* silly race condition. */
4806
rc = E_ACCESSDENIED;
4808
rc = directControl->AccessGuestProperty(aName, NULL, NULL,
4809
false /* isSetter */,
4810
aValue, aTimestamp, aFlags);
4813
#endif // VBOX_WITH_GUEST_PROPS
4815
STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
4820
#ifndef VBOX_WITH_GUEST_PROPS
4821
ReturnComNotImplemented();
4822
#else // VBOX_WITH_GUEST_PROPS
4823
CheckComArgStrNotEmptyOrNull(aName);
4824
CheckComArgOutPointerValid(aValue);
4825
CheckComArgOutPointerValid(aTimestamp);
4826
CheckComArgOutPointerValid(aFlags);
4828
AutoCaller autoCaller(this);
4829
if (FAILED(autoCaller.rc())) return autoCaller.rc();
4831
HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
4832
if (rc == E_ACCESSDENIED)
4833
/* The VM is not running or the service is not (yet) accessible */
4834
rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
4836
#endif // VBOX_WITH_GUEST_PROPS
4839
STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
4841
LONG64 dummyTimestamp;
4843
return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
4846
STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
4850
return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
4853
#ifdef VBOX_WITH_GUEST_PROPS
4855
* Set a guest property in VBoxSVC's internal structures.
4857
HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
4860
using namespace guestProp;
4862
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4864
HWData::GuestProperty property;
4865
property.mFlags = NILFLAG;
4868
rc = checkStateDependency(MutableStateDep);
4869
if (FAILED(rc)) return rc;
4873
Utf8Str utf8Name(aName);
4874
Utf8Str utf8Flags(aFlags);
4875
uint32_t fFlags = NILFLAG;
4876
if ( (aFlags != NULL)
4877
&& RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
4879
return setError(E_INVALIDARG,
4880
tr("Invalid flag values: '%ls'"),
4883
/** @todo r=bird: see efficiency rant in PushGuestProperty. (Yeah, I
4884
* know, this is simple and do an OK job atm.) */
4885
HWData::GuestPropertyList::iterator it;
4886
for (it = mHWData->mGuestProperties.begin();
4887
it != mHWData->mGuestProperties.end(); ++it)
4888
if (it->strName == utf8Name)
4891
if (it->mFlags & (RDONLYHOST))
4892
rc = setError(E_ACCESSDENIED,
4893
tr("The property '%ls' cannot be changed by the host"),
4897
setModified(IsModified_MachineData);
4898
mHWData.backup(); // @todo r=dj backup in a loop?!?
4900
/* The backup() operation invalidates our iterator, so
4902
for (it = mHWData->mGuestProperties.begin();
4903
it->strName != utf8Name;
4906
mHWData->mGuestProperties.erase(it);
4911
if (found && SUCCEEDED(rc))
4916
property.strValue = aValue;
4917
property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
4919
property.mFlags = fFlags;
4920
mHWData->mGuestProperties.push_back(property);
4923
else if (SUCCEEDED(rc) && *aValue)
4926
setModified(IsModified_MachineData);
4928
property.strName = aName;
4929
property.strValue = aValue;
4930
property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
4931
property.mFlags = fFlags;
4932
mHWData->mGuestProperties.push_back(property);
4935
&& ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
4936
|| RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
4944
/** @todo r=bird: Why aren't we leaving the lock here? The
4945
* same code in PushGuestProperty does... */
4946
mParent->onGuestPropertyChange(mData->mUuid, aName, aValue, aFlags);
4949
catch (std::bad_alloc &)
4958
* Set a property on the VM that that property belongs to.
4959
* @returns E_ACCESSDENIED if the VM process is not available or not
4960
* currently handling queries and the setting should then be done in
4963
HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
4969
ComPtr<IInternalSessionControl> directControl =
4970
mData->mSession.mDirectControl;
4972
BSTR dummy = NULL; /* will not be changed (setter) */
4975
rc = E_ACCESSDENIED;
4977
rc = directControl->AccessGuestProperty
4979
/** @todo Fix when adding DeleteGuestProperty(),
4981
*aValue ? aValue : NULL, aFlags, true /* isSetter */,
4982
&dummy, &dummy64, &dummy);
4984
catch (std::bad_alloc &)
4991
#endif // VBOX_WITH_GUEST_PROPS
4993
STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
4996
#ifndef VBOX_WITH_GUEST_PROPS
4997
ReturnComNotImplemented();
4998
#else // VBOX_WITH_GUEST_PROPS
4999
CheckComArgStrNotEmptyOrNull(aName);
5000
if ((aFlags != NULL) && !VALID_PTR(aFlags))
5001
return E_INVALIDARG;
5002
AutoCaller autoCaller(this);
5003
if (FAILED(autoCaller.rc())) return autoCaller.rc();
5005
HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5006
if (rc == E_ACCESSDENIED)
5007
/* The VM is not running or the service is not (yet) accessible */
5008
rc = setGuestPropertyToService(aName, aValue, aFlags);
5010
#endif // VBOX_WITH_GUEST_PROPS
5013
STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5015
return SetGuestProperty(aName, aValue, NULL);
5018
#ifdef VBOX_WITH_GUEST_PROPS
5020
* Enumerate the guest properties in VBoxSVC's internal structures.
5022
HRESULT Machine::enumerateGuestPropertiesInService
5023
(IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5024
ComSafeArrayOut(BSTR, aValues),
5025
ComSafeArrayOut(LONG64, aTimestamps),
5026
ComSafeArrayOut(BSTR, aFlags))
5028
using namespace guestProp;
5030
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5031
Utf8Str strPatterns(aPatterns);
5034
* Look for matching patterns and build up a list.
5036
HWData::GuestPropertyList propList;
5037
for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
5038
it != mHWData->mGuestProperties.end();
5040
if ( strPatterns.isEmpty()
5041
|| RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5043
it->strName.c_str(),
5047
propList.push_back(*it);
5050
* And build up the arrays for returning the property information.
5052
size_t cEntries = propList.size();
5053
SafeArray<BSTR> names(cEntries);
5054
SafeArray<BSTR> values(cEntries);
5055
SafeArray<LONG64> timestamps(cEntries);
5056
SafeArray<BSTR> flags(cEntries);
5058
for (HWData::GuestPropertyList::iterator it = propList.begin();
5059
it != propList.end();
5062
char szFlags[MAX_FLAGS_LEN + 1];
5063
it->strName.cloneTo(&names[iProp]);
5064
it->strValue.cloneTo(&values[iProp]);
5065
timestamps[iProp] = it->mTimestamp;
5066
writeFlags(it->mFlags, szFlags);
5067
Bstr(szFlags).cloneTo(&flags[iProp]);
5070
names.detachTo(ComSafeArrayOutArg(aNames));
5071
values.detachTo(ComSafeArrayOutArg(aValues));
5072
timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5073
flags.detachTo(ComSafeArrayOutArg(aFlags));
5078
* Enumerate the properties managed by a VM.
5079
* @returns E_ACCESSDENIED if the VM process is not available or not
5080
* currently handling queries and the setting should then be done in
5083
HRESULT Machine::enumerateGuestPropertiesOnVM
5084
(IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5085
ComSafeArrayOut(BSTR, aValues),
5086
ComSafeArrayOut(LONG64, aTimestamps),
5087
ComSafeArrayOut(BSTR, aFlags))
5090
ComPtr<IInternalSessionControl> directControl;
5091
directControl = mData->mSession.mDirectControl;
5094
rc = E_ACCESSDENIED;
5096
rc = directControl->EnumerateGuestProperties
5097
(aPatterns, ComSafeArrayOutArg(aNames),
5098
ComSafeArrayOutArg(aValues),
5099
ComSafeArrayOutArg(aTimestamps),
5100
ComSafeArrayOutArg(aFlags));
5103
#endif // VBOX_WITH_GUEST_PROPS
5105
STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5106
ComSafeArrayOut(BSTR, aNames),
5107
ComSafeArrayOut(BSTR, aValues),
5108
ComSafeArrayOut(LONG64, aTimestamps),
5109
ComSafeArrayOut(BSTR, aFlags))
5111
#ifndef VBOX_WITH_GUEST_PROPS
5112
ReturnComNotImplemented();
5113
#else // VBOX_WITH_GUEST_PROPS
5114
if (!VALID_PTR(aPatterns) && (aPatterns != NULL))
5117
CheckComArgOutSafeArrayPointerValid(aNames);
5118
CheckComArgOutSafeArrayPointerValid(aValues);
5119
CheckComArgOutSafeArrayPointerValid(aTimestamps);
5120
CheckComArgOutSafeArrayPointerValid(aFlags);
5122
AutoCaller autoCaller(this);
5123
if (FAILED(autoCaller.rc())) return autoCaller.rc();
5125
HRESULT rc = enumerateGuestPropertiesOnVM
5126
(aPatterns, ComSafeArrayOutArg(aNames),
5127
ComSafeArrayOutArg(aValues),
5128
ComSafeArrayOutArg(aTimestamps),
5129
ComSafeArrayOutArg(aFlags));
5130
if (rc == E_ACCESSDENIED)
5131
/* The VM is not running or the service is not (yet) accessible */
5132
rc = enumerateGuestPropertiesInService
5133
(aPatterns, ComSafeArrayOutArg(aNames),
5134
ComSafeArrayOutArg(aValues),
5135
ComSafeArrayOutArg(aTimestamps),
5136
ComSafeArrayOutArg(aFlags));
5138
#endif // VBOX_WITH_GUEST_PROPS
5141
STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
5142
ComSafeArrayOut(IMediumAttachment*, aAttachments))
5144
MediaData::AttachmentList atts;
5146
HRESULT rc = getMediumAttachmentsOfController(aName, atts);
5147
if (FAILED(rc)) return rc;
5149
SafeIfaceArray<IMediumAttachment> attachments(atts);
5150
attachments.detachTo(ComSafeArrayOutArg(aAttachments));
5155
STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
5156
LONG aControllerPort,
5158
IMediumAttachment **aAttachment)
5160
LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5161
aControllerName, aControllerPort, aDevice));
5163
CheckComArgStrNotEmptyOrNull(aControllerName);
5164
CheckComArgOutPointerValid(aAttachment);
5166
AutoCaller autoCaller(this);
5167
if (FAILED(autoCaller.rc())) return autoCaller.rc();
5169
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5171
*aAttachment = NULL;
5173
ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5177
if (pAttach.isNull())
5178
return setError(VBOX_E_OBJECT_NOT_FOUND,
5179
tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5180
aDevice, aControllerPort, aControllerName);
5182
pAttach.queryInterfaceTo(aAttachment);
5187
STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
5188
StorageBus_T aConnectionType,
5189
IStorageController **controller)
5191
CheckComArgStrNotEmptyOrNull(aName);
5193
if ( (aConnectionType <= StorageBus_Null)
5194
|| (aConnectionType > StorageBus_SAS))
5195
return setError(E_INVALIDARG,
5196
tr("Invalid connection type: %d"),
5199
AutoCaller autoCaller(this);
5200
if (FAILED(autoCaller.rc())) return autoCaller.rc();
5202
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5204
HRESULT rc = checkStateDependency(MutableStateDep);
5205
if (FAILED(rc)) return rc;
5207
/* try to find one with the name first. */
5208
ComObjPtr<StorageController> ctrl;
5210
rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
5212
return setError(VBOX_E_OBJECT_IN_USE,
5213
tr("Storage controller named '%ls' already exists"),
5216
ctrl.createObject();
5218
/* get a new instance number for the storage controller */
5219
ULONG ulInstance = 0;
5220
bool fBootable = true;
5221
for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5222
it != mStorageControllers->end();
5225
if ((*it)->getStorageBus() == aConnectionType)
5227
ULONG ulCurInst = (*it)->getInstance();
5229
if (ulCurInst >= ulInstance)
5230
ulInstance = ulCurInst + 1;
5232
/* Only one controller of each type can be marked as bootable. */
5233
if ((*it)->getBootable())
5238
rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5239
if (FAILED(rc)) return rc;
5241
setModified(IsModified_Storage);
5242
mStorageControllers.backup();
5243
mStorageControllers->push_back(ctrl);
5245
ctrl.queryInterfaceTo(controller);
5247
/* inform the direct session if any */
5249
onStorageControllerChange();
5254
STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
5255
IStorageController **aStorageController)
5257
CheckComArgStrNotEmptyOrNull(aName);
5259
AutoCaller autoCaller(this);
5260
if (FAILED(autoCaller.rc())) return autoCaller.rc();
5262
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5264
ComObjPtr<StorageController> ctrl;
5266
HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5268
ctrl.queryInterfaceTo(aStorageController);
5273
STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
5274
IStorageController **aStorageController)
5276
AutoCaller autoCaller(this);
5277
if (FAILED(autoCaller.rc())) return autoCaller.rc();
5279
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5281
for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5282
it != mStorageControllers->end();
5285
if ((*it)->getInstance() == aInstance)
5287
(*it).queryInterfaceTo(aStorageController);
5292
return setError(VBOX_E_OBJECT_NOT_FOUND,
5293
tr("Could not find a storage controller with instance number '%lu'"),
5297
STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
5299
AutoCaller autoCaller(this);
5300
if (FAILED(autoCaller.rc())) return autoCaller.rc();
5302
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5304
HRESULT rc = checkStateDependency(MutableStateDep);
5305
if (FAILED(rc)) return rc;
5307
ComObjPtr<StorageController> ctrl;
5309
rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5312
/* Ensure that only one controller of each type is marked as bootable. */
5313
if (fBootable == TRUE)
5315
for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5316
it != mStorageControllers->end();
5319
ComObjPtr<StorageController> aCtrl = (*it);
5321
if ( (aCtrl->getName() != Utf8Str(aName))
5322
&& aCtrl->getBootable() == TRUE
5323
&& aCtrl->getStorageBus() == ctrl->getStorageBus()
5324
&& aCtrl->getControllerType() == ctrl->getControllerType())
5326
aCtrl->setBootable(FALSE);
5334
ctrl->setBootable(fBootable);
5335
setModified(IsModified_Storage);
5341
/* inform the direct session if any */
5343
onStorageControllerChange();
5349
STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
5351
CheckComArgStrNotEmptyOrNull(aName);
5353
AutoCaller autoCaller(this);
5354
if (FAILED(autoCaller.rc())) return autoCaller.rc();
5356
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5358
HRESULT rc = checkStateDependency(MutableStateDep);
5359
if (FAILED(rc)) return rc;
5361
ComObjPtr<StorageController> ctrl;
5362
rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5363
if (FAILED(rc)) return rc;
5365
/* We can remove the controller only if there is no device attached. */
5366
/* check if the device slot is already busy */
5367
for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
5368
it != mMediaData->mAttachments.end();
5371
if ((*it)->getControllerName() == aName)
5372
return setError(VBOX_E_OBJECT_IN_USE,
5373
tr("Storage controller named '%ls' has still devices attached"),
5377
/* We can remove it now. */
5378
setModified(IsModified_Storage);
5379
mStorageControllers.backup();
5383
mStorageControllers->remove(ctrl);
5385
/* inform the direct session if any */
5387
onStorageControllerChange();
5392
STDMETHODIMP Machine::QuerySavedGuestSize(ULONG uScreenId, ULONG *puWidth, ULONG *puHeight)
5394
LogFlowThisFunc(("\n"));
5396
CheckComArgNotNull(puWidth);
5397
CheckComArgNotNull(puHeight);
5399
uint32_t u32Width = 0;
5400
uint32_t u32Height = 0;
5402
int vrc = readSavedGuestSize(mSSData->mStateFilePath, uScreenId, &u32Width, &u32Height);
5403
if (RT_FAILURE(vrc))
5404
return setError(VBOX_E_IPRT_ERROR,
5405
tr("Saved guest size is not available (%Rrc)"),
5408
*puWidth = u32Width;
5409
*puHeight = u32Height;
5414
STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
5416
LogFlowThisFunc(("\n"));
5418
CheckComArgNotNull(aSize);
5419
CheckComArgNotNull(aWidth);
5420
CheckComArgNotNull(aHeight);
5425
AutoCaller autoCaller(this);
5426
if (FAILED(autoCaller.rc())) return autoCaller.rc();
5428
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5430
uint8_t *pu8Data = NULL;
5431
uint32_t cbData = 0;
5432
uint32_t u32Width = 0;
5433
uint32_t u32Height = 0;
5435
int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
5437
if (RT_FAILURE(vrc))
5438
return setError(VBOX_E_IPRT_ERROR,
5439
tr("Saved screenshot data is not available (%Rrc)"),
5444
*aHeight = u32Height;
5446
freeSavedDisplayScreenshot(pu8Data);
5451
STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
5453
LogFlowThisFunc(("\n"));
5455
CheckComArgNotNull(aWidth);
5456
CheckComArgNotNull(aHeight);
5457
CheckComArgOutSafeArrayPointerValid(aData);
5462
AutoCaller autoCaller(this);
5463
if (FAILED(autoCaller.rc())) return autoCaller.rc();
5465
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5467
uint8_t *pu8Data = NULL;
5468
uint32_t cbData = 0;
5469
uint32_t u32Width = 0;
5470
uint32_t u32Height = 0;
5472
int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
5474
if (RT_FAILURE(vrc))
5475
return setError(VBOX_E_IPRT_ERROR,
5476
tr("Saved screenshot data is not available (%Rrc)"),
5480
*aHeight = u32Height;
5482
com::SafeArray<BYTE> bitmap(cbData);
5483
/* Convert pixels to format expected by the API caller. */
5486
/* [0] B, [1] G, [2] R, [3] A. */
5487
for (unsigned i = 0; i < cbData; i += 4)
5489
bitmap[i] = pu8Data[i];
5490
bitmap[i + 1] = pu8Data[i + 1];
5491
bitmap[i + 2] = pu8Data[i + 2];
5492
bitmap[i + 3] = 0xff;
5497
/* [0] R, [1] G, [2] B, [3] A. */
5498
for (unsigned i = 0; i < cbData; i += 4)
5500
bitmap[i] = pu8Data[i + 2];
5501
bitmap[i + 1] = pu8Data[i + 1];
5502
bitmap[i + 2] = pu8Data[i];
5503
bitmap[i + 3] = 0xff;
5506
bitmap.detachTo(ComSafeArrayOutArg(aData));
5508
freeSavedDisplayScreenshot(pu8Data);
5514
STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
5516
LogFlowThisFunc(("\n"));
5518
CheckComArgNotNull(aWidth);
5519
CheckComArgNotNull(aHeight);
5520
CheckComArgOutSafeArrayPointerValid(aData);
5525
AutoCaller autoCaller(this);
5526
if (FAILED(autoCaller.rc())) return autoCaller.rc();
5528
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5530
uint8_t *pu8Data = NULL;
5531
uint32_t cbData = 0;
5532
uint32_t u32Width = 0;
5533
uint32_t u32Height = 0;
5535
int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
5537
if (RT_FAILURE(vrc))
5538
return setError(VBOX_E_IPRT_ERROR,
5539
tr("Saved screenshot data is not available (%Rrc)"),
5543
*aHeight = u32Height;
5545
uint8_t *pu8PNG = NULL;
5550
DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
5552
com::SafeArray<BYTE> screenData(cbPNG);
5553
screenData.initFrom(pu8PNG, cbPNG);
5556
screenData.detachTo(ComSafeArrayOutArg(aData));
5558
freeSavedDisplayScreenshot(pu8Data);
5563
STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
5565
LogFlowThisFunc(("\n"));
5567
CheckComArgNotNull(aSize);
5568
CheckComArgNotNull(aWidth);
5569
CheckComArgNotNull(aHeight);
5574
AutoCaller autoCaller(this);
5575
if (FAILED(autoCaller.rc())) return autoCaller.rc();
5577
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5579
uint8_t *pu8Data = NULL;
5580
uint32_t cbData = 0;
5581
uint32_t u32Width = 0;
5582
uint32_t u32Height = 0;
5584
int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
5586
if (RT_FAILURE(vrc))
5587
return setError(VBOX_E_IPRT_ERROR,
5588
tr("Saved screenshot data is not available (%Rrc)"),
5593
*aHeight = u32Height;
5595
freeSavedDisplayScreenshot(pu8Data);
5600
STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
5602
LogFlowThisFunc(("\n"));
5604
CheckComArgNotNull(aWidth);
5605
CheckComArgNotNull(aHeight);
5606
CheckComArgOutSafeArrayPointerValid(aData);
5611
AutoCaller autoCaller(this);
5612
if (FAILED(autoCaller.rc())) return autoCaller.rc();
5614
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5616
uint8_t *pu8Data = NULL;
5617
uint32_t cbData = 0;
5618
uint32_t u32Width = 0;
5619
uint32_t u32Height = 0;
5621
int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
5623
if (RT_FAILURE(vrc))
5624
return setError(VBOX_E_IPRT_ERROR,
5625
tr("Saved screenshot thumbnail data is not available (%Rrc)"),
5629
*aHeight = u32Height;
5631
com::SafeArray<BYTE> png(cbData);
5632
png.initFrom(pu8Data, cbData);
5633
png.detachTo(ComSafeArrayOutArg(aData));
5635
freeSavedDisplayScreenshot(pu8Data);
5640
STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
5643
LogFlowThisFunc(("\n"));
5645
AutoCaller autoCaller(this);
5646
if (FAILED(autoCaller.rc())) return autoCaller.rc();
5648
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5650
if (!mHWData->mCPUHotPlugEnabled)
5651
return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
5653
if (aCpu >= mHWData->mCPUCount)
5654
return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
5656
if (mHWData->mCPUAttached[aCpu])
5657
return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
5660
rc = onCPUChange(aCpu, false);
5662
if (FAILED(rc)) return rc;
5664
setModified(IsModified_MachineData);
5666
mHWData->mCPUAttached[aCpu] = true;
5668
/* Save settings if online */
5669
if (Global::IsOnline(mData->mMachineState))
5675
STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
5678
LogFlowThisFunc(("\n"));
5680
AutoCaller autoCaller(this);
5681
if (FAILED(autoCaller.rc())) return autoCaller.rc();
5683
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5685
if (!mHWData->mCPUHotPlugEnabled)
5686
return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
5688
if (aCpu >= SchemaDefs::MaxCPUCount)
5689
return setError(E_INVALIDARG,
5690
tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
5691
SchemaDefs::MaxCPUCount);
5693
if (!mHWData->mCPUAttached[aCpu])
5694
return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
5696
/* CPU 0 can't be detached */
5698
return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
5701
rc = onCPUChange(aCpu, true);
5703
if (FAILED(rc)) return rc;
5705
setModified(IsModified_MachineData);
5707
mHWData->mCPUAttached[aCpu] = false;
5709
/* Save settings if online */
5710
if (Global::IsOnline(mData->mMachineState))
5716
STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
5718
LogFlowThisFunc(("\n"));
5720
CheckComArgNotNull(aCpuAttached);
5722
*aCpuAttached = false;
5724
AutoCaller autoCaller(this);
5725
if (FAILED(autoCaller.rc())) return autoCaller.rc();
5727
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5729
/* If hotplug is enabled the CPU is always enabled. */
5730
if (!mHWData->mCPUHotPlugEnabled)
5732
if (aCpu < mHWData->mCPUCount)
5733
*aCpuAttached = true;
5737
if (aCpu < SchemaDefs::MaxCPUCount)
5738
*aCpuAttached = mHWData->mCPUAttached[aCpu];
5744
STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
5746
CheckComArgOutPointerValid(aName);
5748
AutoCaller autoCaller(this);
5749
if (FAILED(autoCaller.rc())) return autoCaller.rc();
5751
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5753
Utf8Str log = queryLogFilename(aIdx);
5754
if (!RTFileExists(log.c_str()))
5761
STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
5763
LogFlowThisFunc(("\n"));
5764
CheckComArgOutSafeArrayPointerValid(aData);
5766
return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
5768
AutoCaller autoCaller(this);
5769
if (FAILED(autoCaller.rc())) return autoCaller.rc();
5771
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5774
Utf8Str log = queryLogFilename(aIdx);
5776
/* do not unnecessarily hold the lock while doing something which does
5777
* not need the lock and potentially takes a long time. */
5780
/* Limit the chunk size to 32K for now, as that gives better performance
5781
* over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
5782
* One byte expands to approx. 25 bytes of breathtaking XML. */
5783
size_t cbData = (size_t)RT_MIN(aSize, 32768);
5784
com::SafeArray<BYTE> logData(cbData);
5787
int vrc = RTFileOpen(&LogFile, log.c_str(),
5788
RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
5789
if (RT_SUCCESS(vrc))
5791
vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
5792
if (RT_SUCCESS(vrc))
5793
logData.resize(cbData);
5795
rc = setError(VBOX_E_IPRT_ERROR,
5796
tr("Could not read log file '%s' (%Rrc)"),
5798
RTFileClose(LogFile);
5801
rc = setError(VBOX_E_IPRT_ERROR,
5802
tr("Could not open log file '%s' (%Rrc)"),
5807
logData.detachTo(ComSafeArrayOutArg(aData));
5813
STDMETHODIMP Machine::AttachHostPciDevice(LONG hostAddress, LONG desiredGuestAddress, IEventContext * /*eventContext*/, BOOL /*tryToUnbind*/)
5815
AutoCaller autoCaller(this);
5816
if (FAILED(autoCaller.rc())) return autoCaller.rc();
5817
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5819
ComObjPtr<PciDeviceAttachment> pda;
5823
RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
5826
pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
5828
mPciDeviceAssignments.push_back(pda);
5832
STDMETHODIMP Machine::DetachHostPciDevice(LONG /*hostAddress*/)
5834
AutoCaller autoCaller(this);
5835
if (FAILED(autoCaller.rc())) return autoCaller.rc();
5836
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5841
STDMETHODIMP Machine::COMGETTER(PciDeviceAssignments)(ComSafeArrayOut(IPciDeviceAttachment *, aAssignments))
5843
CheckComArgOutSafeArrayPointerValid(aAssignments);
5845
AutoCaller autoCaller(this);
5846
if (FAILED(autoCaller.rc())) return autoCaller.rc();
5848
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5850
SafeIfaceArray<IPciDeviceAttachment> assignments(mPciDeviceAssignments);
5851
assignments.detachTo(ComSafeArrayOutArg(aAssignments));
5856
STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
5858
CheckComArgOutPointerValid(aBandwidthControl);
5860
AutoCaller autoCaller(this);
5861
if (FAILED(autoCaller.rc())) return autoCaller.rc();
5863
mBandwidthControl.queryInterfaceTo(aBandwidthControl);
5868
// public methods for internal purposes
5869
/////////////////////////////////////////////////////////////////////////////
5872
* Adds the given IsModified_* flag to the dirty flags of the machine.
5873
* This must be called either during loadSettings or under the machine write lock.
5876
void Machine::setModified(uint32_t fl)
5878
mData->flModifications |= fl;
5882
* Saves the registry entry of this machine to the given configuration node.
5884
* @param aEntryNode Node to save the registry entry to.
5886
* @note locks this object for reading.
5888
HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
5890
AutoLimitedCaller autoCaller(this);
5891
AssertComRCReturnRC(autoCaller.rc());
5893
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5895
data.uuid = mData->mUuid;
5896
data.strSettingsFile = mData->m_strConfigFile;
5902
* Calculates the absolute path of the given path taking the directory of the
5903
* machine settings file as the current directory.
5905
* @param aPath Path to calculate the absolute path for.
5906
* @param aResult Where to put the result (used only on success, can be the
5907
* same Utf8Str instance as passed in @a aPath).
5908
* @return IPRT result.
5910
* @note Locks this object for reading.
5912
int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
5914
AutoCaller autoCaller(this);
5915
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5917
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5919
AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
5921
Utf8Str strSettingsDir = mData->m_strConfigFileFull;
5923
strSettingsDir.stripFilename();
5924
char folder[RTPATH_MAX];
5925
int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
5926
if (RT_SUCCESS(vrc))
5933
* Copies strSource to strTarget, making it relative to the machine folder
5934
* if it is a subdirectory thereof, or simply copying it otherwise.
5936
* @param strSource Path to evaluate and copy.
5937
* @param strTarget Buffer to receive target path.
5939
* @note Locks this object for reading.
5941
void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
5944
AutoCaller autoCaller(this);
5945
AssertComRCReturn(autoCaller.rc(), (void)0);
5947
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5949
AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
5950
// use strTarget as a temporary buffer to hold the machine settings dir
5951
strTarget = mData->m_strConfigFileFull;
5952
strTarget.stripFilename();
5953
if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
5954
// is relative: then append what's left
5955
strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
5957
// is not relative: then overwrite
5958
strTarget = strSource;
5962
* Returns the full path to the machine's log folder in the
5963
* \a aLogFolder argument.
5965
void Machine::getLogFolder(Utf8Str &aLogFolder)
5967
AutoCaller autoCaller(this);
5968
AssertComRCReturnVoid(autoCaller.rc());
5970
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5972
aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
5973
aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
5974
aLogFolder.append(RTPATH_DELIMITER);
5975
aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
5979
* Returns the full path to the machine's log file for an given index.
5981
Utf8Str Machine::queryLogFilename(ULONG idx)
5984
getLogFolder(logFolder);
5985
Assert(logFolder.length());
5988
log = Utf8StrFmt("%s%cVBox.log",
5989
logFolder.c_str(), RTPATH_DELIMITER);
5991
log = Utf8StrFmt("%s%cVBox.log.%d",
5992
logFolder.c_str(), RTPATH_DELIMITER, idx);
5997
* @note Locks this object for writing, calls the client process
5998
* (inside the lock).
6000
HRESULT Machine::openRemoteSession(IInternalSessionControl *aControl,
6002
IN_BSTR aEnvironment,
6003
ProgressProxy *aProgress)
6005
LogFlowThisFuncEnter();
6007
AssertReturn(aControl, E_FAIL);
6008
AssertReturn(aProgress, E_FAIL);
6010
AutoCaller autoCaller(this);
6011
if (FAILED(autoCaller.rc())) return autoCaller.rc();
6013
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6015
if (!mData->mRegistered)
6016
return setError(E_UNEXPECTED,
6017
tr("The machine '%s' is not registered"),
6018
mUserData->s.strName.c_str());
6020
LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
6022
if ( mData->mSession.mState == SessionState_Locked
6023
|| mData->mSession.mState == SessionState_Spawning
6024
|| mData->mSession.mState == SessionState_Unlocking)
6025
return setError(VBOX_E_INVALID_OBJECT_STATE,
6026
tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
6027
mUserData->s.strName.c_str());
6029
/* may not be busy */
6030
AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
6032
/* get the path to the executable */
6033
char szPath[RTPATH_MAX];
6034
RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
6035
size_t sz = strlen(szPath);
6036
szPath[sz++] = RTPATH_DELIMITER;
6038
char *cmd = szPath + sz;
6039
sz = RTPATH_MAX - sz;
6041
int vrc = VINF_SUCCESS;
6042
RTPROCESS pid = NIL_RTPROCESS;
6044
RTENV env = RTENV_DEFAULT;
6046
if (aEnvironment != NULL && *aEnvironment)
6048
char *newEnvStr = NULL;
6052
/* clone the current environment */
6053
int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
6054
AssertRCBreakStmt(vrc2, vrc = vrc2);
6056
newEnvStr = RTStrDup(Utf8Str(aEnvironment).c_str());
6057
AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
6059
/* put new variables to the environment
6060
* (ignore empty variable names here since RTEnv API
6061
* intentionally doesn't do that) */
6062
char *var = newEnvStr;
6063
for (char *p = newEnvStr; *p; ++p)
6065
if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
6070
char *val = strchr(var, '=');
6074
vrc2 = RTEnvSetEx(env, var, val);
6077
vrc2 = RTEnvUnsetEx(env, var);
6078
if (RT_FAILURE(vrc2))
6084
if (RT_SUCCESS(vrc2) && *var)
6085
vrc2 = RTEnvPutEx(env, var);
6087
AssertRCBreakStmt(vrc2, vrc = vrc2);
6091
if (newEnvStr != NULL)
6092
RTStrFree(newEnvStr);
6095
Utf8Str strType(aType);
6098
#ifdef VBOX_WITH_QTGUI
6099
if (strType == "gui" || strType == "GUI/Qt")
6101
# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
6102
const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
6104
const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
6106
Assert(sz >= sizeof(VirtualBox_exe));
6107
strcpy(cmd, VirtualBox_exe);
6109
Utf8Str idStr = mData->mUuid.toString();
6110
const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
6111
vrc = RTProcCreate(szPath, args, env, 0, &pid);
6113
#else /* !VBOX_WITH_QTGUI */
6116
#endif /* VBOX_WITH_QTGUI */
6120
#ifdef VBOX_WITH_VBOXSDL
6121
if (strType == "sdl" || strType == "GUI/SDL")
6123
const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
6124
Assert(sz >= sizeof(VBoxSDL_exe));
6125
strcpy(cmd, VBoxSDL_exe);
6127
Utf8Str idStr = mData->mUuid.toString();
6128
const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
6129
vrc = RTProcCreate(szPath, args, env, 0, &pid);
6131
#else /* !VBOX_WITH_VBOXSDL */
6134
#endif /* !VBOX_WITH_VBOXSDL */
6138
#ifdef VBOX_WITH_HEADLESS
6139
if ( strType == "headless"
6140
|| strType == "capture"
6141
|| strType == "vrdp" /* Deprecated. Same as headless. */
6144
/* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
6145
* which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
6146
* and a VM works even if the server has not been installed.
6147
* So in 4.0 the "headless" behavior remains the same for default VBox installations.
6148
* Only if a VRDE has been installed and the VM enables it, the "headless" will work
6149
* differently in 4.0 and 3.x.
6151
const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
6152
Assert(sz >= sizeof(VBoxHeadless_exe));
6153
strcpy(cmd, VBoxHeadless_exe);
6155
Utf8Str idStr = mData->mUuid.toString();
6156
/* Leave space for "--capture" arg. */
6157
const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0, 0 };
6158
if (strType == "capture")
6160
unsigned pos = RT_ELEMENTS(args) - 2;
6161
args[pos] = "--capture";
6163
vrc = RTProcCreate(szPath, args, env, 0, &pid);
6165
#else /* !VBOX_WITH_HEADLESS */
6168
#endif /* !VBOX_WITH_HEADLESS */
6172
return setError(E_INVALIDARG,
6173
tr("Invalid session type: '%s'"),
6179
if (RT_FAILURE(vrc))
6180
return setError(VBOX_E_IPRT_ERROR,
6181
tr("Could not launch a process for the machine '%s' (%Rrc)"),
6182
mUserData->s.strName.c_str(), vrc);
6184
LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
6187
* Note that we don't leave the lock here before calling the client,
6188
* because it doesn't need to call us back if called with a NULL argument.
6189
* Leaving the lock here is dangerous because we didn't prepare the
6190
* launch data yet, but the client we've just started may happen to be
6191
* too fast and call openSession() that will fail (because of PID, etc.),
6192
* so that the Machine will never get out of the Spawning session state.
6195
/* inform the session that it will be a remote one */
6196
LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
6197
HRESULT rc = aControl->AssignMachine(NULL);
6198
LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
6202
/* restore the session state */
6203
mData->mSession.mState = SessionState_Unlocked;
6204
/* The failure may occur w/o any error info (from RPC), so provide one */
6205
return setError(VBOX_E_VM_ERROR,
6206
tr("Failed to assign the machine to the session (%Rrc)"), rc);
6209
/* attach launch data to the machine */
6210
Assert(mData->mSession.mPid == NIL_RTPROCESS);
6211
mData->mSession.mRemoteControls.push_back (aControl);
6212
mData->mSession.mProgress = aProgress;
6213
mData->mSession.mPid = pid;
6214
mData->mSession.mState = SessionState_Spawning;
6215
mData->mSession.mType = strType;
6217
LogFlowThisFuncLeave();
6222
* Returns @c true if the given machine has an open direct session and returns
6223
* the session machine instance and additional session data (on some platforms)
6226
* Note that when the method returns @c false, the arguments remain unchanged.
6228
* @param aMachine Session machine object.
6229
* @param aControl Direct session control object (optional).
6230
* @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
6232
* @note locks this object for reading.
6234
#if defined(RT_OS_WINDOWS)
6235
bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
6236
ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
6237
HANDLE *aIPCSem /*= NULL*/,
6238
bool aAllowClosing /*= false*/)
6239
#elif defined(RT_OS_OS2)
6240
bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
6241
ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
6242
HMTX *aIPCSem /*= NULL*/,
6243
bool aAllowClosing /*= false*/)
6245
bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
6246
ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
6247
bool aAllowClosing /*= false*/)
6250
AutoLimitedCaller autoCaller(this);
6251
AssertComRCReturn(autoCaller.rc(), false);
6253
/* just return false for inaccessible machines */
6254
if (autoCaller.state() != Ready)
6257
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6259
if ( mData->mSession.mState == SessionState_Locked
6260
|| (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
6263
AssertReturn(!mData->mSession.mMachine.isNull(), false);
6265
aMachine = mData->mSession.mMachine;
6267
if (aControl != NULL)
6268
*aControl = mData->mSession.mDirectControl;
6270
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
6271
/* Additional session data */
6272
if (aIPCSem != NULL)
6273
*aIPCSem = aMachine->mIPCSem;
6282
* Returns @c true if the given machine has an spawning direct session and
6283
* returns and additional session data (on some platforms) if so.
6285
* Note that when the method returns @c false, the arguments remain unchanged.
6287
* @param aPID PID of the spawned direct session process.
6289
* @note locks this object for reading.
6291
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
6292
bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
6294
bool Machine::isSessionSpawning()
6297
AutoLimitedCaller autoCaller(this);
6298
AssertComRCReturn(autoCaller.rc(), false);
6300
/* just return false for inaccessible machines */
6301
if (autoCaller.state() != Ready)
6304
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6306
if (mData->mSession.mState == SessionState_Spawning)
6308
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
6309
/* Additional session data */
6312
AssertReturn(mData->mSession.mPid != NIL_RTPROCESS, false);
6313
*aPID = mData->mSession.mPid;
6323
* Called from the client watcher thread to check for unexpected client process
6324
* death during Session_Spawning state (e.g. before it successfully opened a
6327
* On Win32 and on OS/2, this method is called only when we've got the
6328
* direct client's process termination notification, so it always returns @c
6331
* On other platforms, this method returns @c true if the client process is
6332
* terminated and @c false if it's still alive.
6334
* @note Locks this object for writing.
6336
bool Machine::checkForSpawnFailure()
6338
AutoCaller autoCaller(this);
6339
if (!autoCaller.isOk())
6342
LogFlowThisFunc(("Already uninitialized!\n"));
6346
/* VirtualBox::addProcessToReap() needs a write lock */
6347
AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
6349
if (mData->mSession.mState != SessionState_Spawning)
6352
LogFlowThisFunc(("Not spawning any more!\n"));
6358
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
6360
/* the process was already unexpectedly terminated, we just need to set an
6361
* error and finalize session spawning */
6362
rc = setError(E_FAIL,
6363
tr("The virtual machine '%s' has terminated unexpectedly during startup"),
6367
/* PID not yet initialized, skip check. */
6368
if (mData->mSession.mPid == NIL_RTPROCESS)
6371
RTPROCSTATUS status;
6372
int vrc = ::RTProcWait(mData->mSession.mPid, RTPROCWAIT_FLAGS_NOBLOCK,
6375
if (vrc != VERR_PROCESS_RUNNING)
6377
if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
6378
rc = setError(E_FAIL,
6379
tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
6380
getName().c_str(), status.iStatus);
6381
else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
6382
rc = setError(E_FAIL,
6383
tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
6384
getName().c_str(), status.iStatus);
6385
else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
6386
rc = setError(E_FAIL,
6387
tr("The virtual machine '%s' has terminated abnormally"),
6388
getName().c_str(), status.iStatus);
6390
rc = setError(E_FAIL,
6391
tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
6392
getName().c_str(), rc);
6399
/* Close the remote session, remove the remote control from the list
6400
* and reset session state to Closed (@note keep the code in sync with
6401
* the relevant part in checkForSpawnFailure()). */
6403
Assert(mData->mSession.mRemoteControls.size() == 1);
6404
if (mData->mSession.mRemoteControls.size() == 1)
6406
ErrorInfoKeeper eik;
6407
mData->mSession.mRemoteControls.front()->Uninitialize();
6410
mData->mSession.mRemoteControls.clear();
6411
mData->mSession.mState = SessionState_Unlocked;
6413
/* finalize the progress after setting the state */
6414
if (!mData->mSession.mProgress.isNull())
6416
mData->mSession.mProgress->notifyComplete(rc);
6417
mData->mSession.mProgress.setNull();
6420
mParent->addProcessToReap(mData->mSession.mPid);
6421
mData->mSession.mPid = NIL_RTPROCESS;
6423
mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
6431
* Checks whether the machine can be registered. If so, commits and saves
6434
* @note Must be called from mParent's write lock. Locks this object and
6435
* children for writing.
6437
HRESULT Machine::prepareRegister()
6439
AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
6441
AutoLimitedCaller autoCaller(this);
6442
AssertComRCReturnRC(autoCaller.rc());
6444
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6446
/* wait for state dependents to drop to zero */
6447
ensureNoStateDependencies();
6449
if (!mData->mAccessible)
6450
return setError(VBOX_E_INVALID_OBJECT_STATE,
6451
tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
6452
mUserData->s.strName.c_str(),
6453
mData->mUuid.toString().c_str());
6455
AssertReturn(autoCaller.state() == Ready, E_FAIL);
6457
if (mData->mRegistered)
6458
return setError(VBOX_E_INVALID_OBJECT_STATE,
6459
tr("The machine '%s' with UUID {%s} is already registered"),
6460
mUserData->s.strName.c_str(),
6461
mData->mUuid.toString().c_str());
6465
// Ensure the settings are saved. If we are going to be registered and
6466
// no config file exists yet, create it by calling saveSettings() too.
6467
if ( (mData->flModifications)
6468
|| (!mData->pMachineConfigFile->fileExists())
6471
rc = saveSettings(NULL);
6472
// no need to check whether VirtualBox.xml needs saving too since
6473
// we can't have a machine XML file rename pending
6474
if (FAILED(rc)) return rc;
6477
/* more config checking goes here */
6481
/* we may have had implicit modifications we want to fix on success */
6484
mData->mRegistered = true;
6488
/* we may have had implicit modifications we want to cancel on failure*/
6489
rollback(false /* aNotify */);
6496
* Increases the number of objects dependent on the machine state or on the
6497
* registered state. Guarantees that these two states will not change at least
6498
* until #releaseStateDependency() is called.
6500
* Depending on the @a aDepType value, additional state checks may be made.
6501
* These checks will set extended error info on failure. See
6502
* #checkStateDependency() for more info.
6504
* If this method returns a failure, the dependency is not added and the caller
6505
* is not allowed to rely on any particular machine state or registration state
6506
* value and may return the failed result code to the upper level.
6508
* @param aDepType Dependency type to add.
6509
* @param aState Current machine state (NULL if not interested).
6510
* @param aRegistered Current registered state (NULL if not interested).
6512
* @note Locks this object for writing.
6514
HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
6515
MachineState_T *aState /* = NULL */,
6516
BOOL *aRegistered /* = NULL */)
6518
AutoCaller autoCaller(this);
6519
AssertComRCReturnRC(autoCaller.rc());
6521
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6523
HRESULT rc = checkStateDependency(aDepType);
6524
if (FAILED(rc)) return rc;
6527
if (mData->mMachineStateChangePending != 0)
6529
/* ensureNoStateDependencies() is waiting for state dependencies to
6530
* drop to zero so don't add more. It may make sense to wait a bit
6531
* and retry before reporting an error (since the pending state
6532
* transition should be really quick) but let's just assert for
6533
* now to see if it ever happens on practice. */
6537
return setError(E_ACCESSDENIED,
6538
tr("Machine state change is in progress. Please retry the operation later."));
6541
++mData->mMachineStateDeps;
6542
Assert(mData->mMachineStateDeps != 0 /* overflow */);
6546
*aState = mData->mMachineState;
6548
*aRegistered = mData->mRegistered;
6554
* Decreases the number of objects dependent on the machine state.
6555
* Must always complete the #addStateDependency() call after the state
6556
* dependency is no more necessary.
6558
void Machine::releaseStateDependency()
6560
AutoCaller autoCaller(this);
6561
AssertComRCReturnVoid(autoCaller.rc());
6563
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6565
/* releaseStateDependency() w/o addStateDependency()? */
6566
AssertReturnVoid(mData->mMachineStateDeps != 0);
6567
-- mData->mMachineStateDeps;
6569
if (mData->mMachineStateDeps == 0)
6571
/* inform ensureNoStateDependencies() that there are no more deps */
6572
if (mData->mMachineStateChangePending != 0)
6574
Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
6575
RTSemEventMultiSignal (mData->mMachineStateDepsSem);
6580
// protected methods
6581
/////////////////////////////////////////////////////////////////////////////
6584
* Performs machine state checks based on the @a aDepType value. If a check
6585
* fails, this method will set extended error info, otherwise it will return
6586
* S_OK. It is supposed, that on failure, the caller will immediately return
6587
* the return value of this method to the upper level.
6589
* When @a aDepType is AnyStateDep, this method always returns S_OK.
6591
* When @a aDepType is MutableStateDep, this method returns S_OK only if the
6592
* current state of this machine object allows to change settings of the
6593
* machine (i.e. the machine is not registered, or registered but not running
6594
* and not saved). It is useful to call this method from Machine setters
6595
* before performing any change.
6597
* When @a aDepType is MutableOrSavedStateDep, this method behaves the same
6598
* as for MutableStateDep except that if the machine is saved, S_OK is also
6599
* returned. This is useful in setters which allow changing machine
6600
* properties when it is in the saved state.
6602
* @param aDepType Dependency type to check.
6604
* @note Non Machine based classes should use #addStateDependency() and
6605
* #releaseStateDependency() methods or the smart AutoStateDependency
6608
* @note This method must be called from under this object's read or write
6611
HRESULT Machine::checkStateDependency(StateDependency aDepType)
6619
case MutableStateDep:
6621
if ( mData->mRegistered
6622
&& ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
6623
|| ( mData->mMachineState != MachineState_Paused
6624
&& mData->mMachineState != MachineState_Running
6625
&& mData->mMachineState != MachineState_Aborted
6626
&& mData->mMachineState != MachineState_Teleported
6627
&& mData->mMachineState != MachineState_PoweredOff
6631
return setError(VBOX_E_INVALID_VM_STATE,
6632
tr("The machine is not mutable (state is %s)"),
6633
Global::stringifyMachineState(mData->mMachineState));
6636
case MutableOrSavedStateDep:
6638
if ( mData->mRegistered
6639
&& ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
6640
|| ( mData->mMachineState != MachineState_Paused
6641
&& mData->mMachineState != MachineState_Running
6642
&& mData->mMachineState != MachineState_Aborted
6643
&& mData->mMachineState != MachineState_Teleported
6644
&& mData->mMachineState != MachineState_Saved
6645
&& mData->mMachineState != MachineState_PoweredOff
6649
return setError(VBOX_E_INVALID_VM_STATE,
6650
tr("The machine is not mutable (state is %s)"),
6651
Global::stringifyMachineState(mData->mMachineState));
6660
* Helper to initialize all associated child objects and allocate data
6663
* This method must be called as a part of the object's initialization procedure
6664
* (usually done in the #init() method).
6666
* @note Must be called only from #init() or from #registeredInit().
6668
HRESULT Machine::initDataAndChildObjects()
6670
AutoCaller autoCaller(this);
6671
AssertComRCReturnRC(autoCaller.rc());
6672
AssertComRCReturn(autoCaller.state() == InInit ||
6673
autoCaller.state() == Limited, E_FAIL);
6675
AssertReturn(!mData->mAccessible, E_FAIL);
6677
/* allocate data structures */
6679
mUserData.allocate();
6681
mMediaData.allocate();
6682
mStorageControllers.allocate();
6684
/* initialize mOSTypeId */
6685
mUserData->s.strOsType = mParent->getUnknownOSType()->id();
6687
/* create associated BIOS settings object */
6688
unconst(mBIOSSettings).createObject();
6689
mBIOSSettings->init(this);
6691
/* create an associated VRDE object (default is disabled) */
6692
unconst(mVRDEServer).createObject();
6693
mVRDEServer->init(this);
6695
/* create associated serial port objects */
6696
for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
6698
unconst(mSerialPorts[slot]).createObject();
6699
mSerialPorts[slot]->init(this, slot);
6702
/* create associated parallel port objects */
6703
for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
6705
unconst(mParallelPorts[slot]).createObject();
6706
mParallelPorts[slot]->init(this, slot);
6709
/* create the audio adapter object (always present, default is disabled) */
6710
unconst(mAudioAdapter).createObject();
6711
mAudioAdapter->init(this);
6713
/* create the USB controller object (always present, default is disabled) */
6714
unconst(mUSBController).createObject();
6715
mUSBController->init(this);
6717
/* create associated network adapter objects */
6718
for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot ++)
6720
unconst(mNetworkAdapters[slot]).createObject();
6721
mNetworkAdapters[slot]->init(this, slot);
6724
/* create the bandwidth control */
6725
unconst(mBandwidthControl).createObject();
6726
mBandwidthControl->init(this);
6732
* Helper to uninitialize all associated child objects and to free all data
6735
* This method must be called as a part of the object's uninitialization
6736
* procedure (usually done in the #uninit() method).
6738
* @note Must be called only from #uninit() or from #registeredInit().
6740
void Machine::uninitDataAndChildObjects()
6742
AutoCaller autoCaller(this);
6743
AssertComRCReturnVoid(autoCaller.rc());
6744
AssertComRCReturnVoid( autoCaller.state() == InUninit
6745
|| autoCaller.state() == Limited);
6747
/* tell all our other child objects we've been uninitialized */
6748
if (mBandwidthControl)
6750
mBandwidthControl->uninit();
6751
unconst(mBandwidthControl).setNull();
6754
for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
6756
if (mNetworkAdapters[slot])
6758
mNetworkAdapters[slot]->uninit();
6759
unconst(mNetworkAdapters[slot]).setNull();
6765
mUSBController->uninit();
6766
unconst(mUSBController).setNull();
6771
mAudioAdapter->uninit();
6772
unconst(mAudioAdapter).setNull();
6775
for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
6777
if (mParallelPorts[slot])
6779
mParallelPorts[slot]->uninit();
6780
unconst(mParallelPorts[slot]).setNull();
6784
for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
6786
if (mSerialPorts[slot])
6788
mSerialPorts[slot]->uninit();
6789
unconst(mSerialPorts[slot]).setNull();
6795
mVRDEServer->uninit();
6796
unconst(mVRDEServer).setNull();
6801
mBIOSSettings->uninit();
6802
unconst(mBIOSSettings).setNull();
6805
/* Deassociate hard disks (only when a real Machine or a SnapshotMachine
6806
* instance is uninitialized; SessionMachine instances refer to real
6807
* Machine hard disks). This is necessary for a clean re-initialization of
6808
* the VM after successfully re-checking the accessibility state. Note
6809
* that in case of normal Machine or SnapshotMachine uninitialization (as
6810
* a result of unregistering or deleting the snapshot), outdated hard
6811
* disk attachments will already be uninitialized and deleted, so this
6812
* code will not affect them. */
6814
&& (!isSessionMachine())
6817
for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
6818
it != mMediaData->mAttachments.end();
6821
ComObjPtr<Medium> hd = (*it)->getMedium();
6824
HRESULT rc = hd->removeBackReference(mData->mUuid, getSnapshotId());
6829
if (!isSessionMachine() && !isSnapshotMachine())
6831
// clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
6832
if (mData->mFirstSnapshot)
6834
// snapshots tree is protected by media write lock; strictly
6835
// this isn't necessary here since we're deleting the entire
6836
// machine, but otherwise we assert in Snapshot::uninit()
6837
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6838
mData->mFirstSnapshot->uninit();
6839
mData->mFirstSnapshot.setNull();
6842
mData->mCurrentSnapshot.setNull();
6845
/* free data structures (the essential mData structure is not freed here
6846
* since it may be still in use) */
6848
mStorageControllers.free();
6855
* Returns a pointer to the Machine object for this machine that acts like a
6856
* parent for complex machine data objects such as shared folders, etc.
6858
* For primary Machine objects and for SnapshotMachine objects, returns this
6859
* object's pointer itself. For SessionMachine objects, returns the peer
6860
* (primary) machine pointer.
6862
Machine* Machine::getMachine()
6864
if (isSessionMachine())
6865
return (Machine*)mPeer;
6870
* Makes sure that there are no machine state dependents. If necessary, waits
6871
* for the number of dependents to drop to zero.
6873
* Make sure this method is called from under this object's write lock to
6874
* guarantee that no new dependents may be added when this method returns
6875
* control to the caller.
6877
* @note Locks this object for writing. The lock will be released while waiting
6880
* @warning To be used only in methods that change the machine state!
6882
void Machine::ensureNoStateDependencies()
6884
AssertReturnVoid(isWriteLockOnCurrentThread());
6886
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6888
/* Wait for all state dependents if necessary */
6889
if (mData->mMachineStateDeps != 0)
6891
/* lazy semaphore creation */
6892
if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
6893
RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
6895
LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
6896
mData->mMachineStateDeps));
6898
++mData->mMachineStateChangePending;
6900
/* reset the semaphore before waiting, the last dependent will signal
6902
RTSemEventMultiReset(mData->mMachineStateDepsSem);
6906
RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
6910
-- mData->mMachineStateChangePending;
6915
* Changes the machine state and informs callbacks.
6917
* This method is not intended to fail so it either returns S_OK or asserts (and
6918
* returns a failure).
6920
* @note Locks this object for writing.
6922
HRESULT Machine::setMachineState(MachineState_T aMachineState)
6924
LogFlowThisFuncEnter();
6925
LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
6927
AutoCaller autoCaller(this);
6928
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
6930
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6932
/* wait for state dependents to drop to zero */
6933
ensureNoStateDependencies();
6935
if (mData->mMachineState != aMachineState)
6937
mData->mMachineState = aMachineState;
6939
RTTimeNow(&mData->mLastStateChange);
6941
mParent->onMachineStateChange(mData->mUuid, aMachineState);
6944
LogFlowThisFuncLeave();
6949
* Searches for a shared folder with the given logical name
6950
* in the collection of shared folders.
6952
* @param aName logical name of the shared folder
6953
* @param aSharedFolder where to return the found object
6954
* @param aSetError whether to set the error info if the folder is
6957
* S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
6960
* must be called from under the object's lock!
6962
HRESULT Machine::findSharedFolder(CBSTR aName,
6963
ComObjPtr<SharedFolder> &aSharedFolder,
6964
bool aSetError /* = false */)
6967
for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
6968
!found && it != mHWData->mSharedFolders.end();
6971
AutoWriteLock alock(*it COMMA_LOCKVAL_SRC_POS);
6972
found = (*it)->getName() == aName;
6974
aSharedFolder = *it;
6977
HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
6979
if (aSetError && !found)
6980
setError(rc, tr("Could not find a shared folder named '%ls'"), aName);
6986
* Initializes all machine instance data from the given settings structures
6987
* from XML. The exception is the machine UUID which needs special handling
6988
* depending on the caller's use case, so the caller needs to set that herself.
6990
* This gets called in several contexts during machine initialization:
6992
* -- When machine XML exists on disk already and needs to be loaded into memory,
6993
* for example, from registeredInit() to load all registered machines on
6994
* VirtualBox startup. In this case, puuidRegistry is NULL because the media
6995
* attached to the machine should be part of some media registry already.
6997
* -- During OVF import, when a machine config has been constructed from an
6998
* OVF file. In this case, puuidRegistry is set to the machine UUID to
6999
* ensure that the media listed as attachments in the config (which have
7000
* been imported from the OVF) receive the correct registry ID.
7002
* @param config Machine settings from XML.
7003
* @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
7006
HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
7007
const Guid *puuidRegistry)
7009
// copy name, description, OS type, teleporter, UTC etc.
7010
mUserData->s = config.machineUserData;
7012
// look up the object by Id to check it is valid
7013
ComPtr<IGuestOSType> guestOSType;
7014
HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
7015
guestOSType.asOutParam());
7016
if (FAILED(rc)) return rc;
7018
// stateFile (optional)
7019
if (config.strStateFile.isEmpty())
7020
mSSData->mStateFilePath.setNull();
7023
Utf8Str stateFilePathFull(config.strStateFile);
7024
int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
7025
if (RT_FAILURE(vrc))
7026
return setError(E_FAIL,
7027
tr("Invalid saved state file path '%s' (%Rrc)"),
7028
config.strStateFile.c_str(),
7030
mSSData->mStateFilePath = stateFilePathFull;
7033
// snapshot folder needs special processing so set it again
7034
rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
7035
if (FAILED(rc)) return rc;
7037
/* currentStateModified (optional, default is true) */
7038
mData->mCurrentStateModified = config.fCurrentStateModified;
7040
mData->mLastStateChange = config.timeLastStateChange;
7043
* note: all mUserData members must be assigned prior this point because
7044
* we need to commit changes in order to let mUserData be shared by all
7045
* snapshot machine instances.
7047
mUserData.commitCopy();
7049
// machine registry, if present (must be loaded before snapshots)
7050
if (config.canHaveOwnMediaRegistry())
7052
// determine machine folder
7053
Utf8Str strMachineFolder = getSettingsFileFull();
7054
strMachineFolder.stripFilename();
7055
rc = mParent->initMedia(getId(), // media registry ID == machine UUID
7056
config.mediaRegistry,
7058
if (FAILED(rc)) return rc;
7061
/* Snapshot node (optional) */
7062
size_t cRootSnapshots;
7063
if ((cRootSnapshots = config.llFirstSnapshot.size()))
7065
// there must be only one root snapshot
7066
Assert(cRootSnapshots == 1);
7068
const settings::Snapshot &snap = config.llFirstSnapshot.front();
7070
rc = loadSnapshot(snap,
7071
config.uuidCurrentSnapshot,
7072
NULL); // no parent == first snapshot
7073
if (FAILED(rc)) return rc;
7077
rc = loadHardware(config.hardwareMachine);
7078
if (FAILED(rc)) return rc;
7080
// load storage controllers
7081
rc = loadStorageControllers(config.storageMachine,
7083
NULL /* puuidSnapshot */);
7084
if (FAILED(rc)) return rc;
7087
* NOTE: the assignment below must be the last thing to do,
7088
* otherwise it will be not possible to change the settings
7089
* somewhere in the code above because all setters will be
7090
* blocked by checkStateDependency(MutableStateDep).
7093
/* set the machine state to Aborted or Saved when appropriate */
7094
if (config.fAborted)
7096
Assert(!mSSData->mStateFilePath.isEmpty());
7097
mSSData->mStateFilePath.setNull();
7099
/* no need to use setMachineState() during init() */
7100
mData->mMachineState = MachineState_Aborted;
7102
else if (!mSSData->mStateFilePath.isEmpty())
7104
/* no need to use setMachineState() during init() */
7105
mData->mMachineState = MachineState_Saved;
7108
// after loading settings, we are no longer different from the XML on disk
7109
mData->flModifications = 0;
7115
* Recursively loads all snapshots starting from the given.
7117
* @param aNode <Snapshot> node.
7118
* @param aCurSnapshotId Current snapshot ID from the settings file.
7119
* @param aParentSnapshot Parent snapshot.
7121
HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
7122
const Guid &aCurSnapshotId,
7123
Snapshot *aParentSnapshot)
7125
AssertReturn(!isSnapshotMachine(), E_FAIL);
7126
AssertReturn(!isSessionMachine(), E_FAIL);
7130
Utf8Str strStateFile;
7131
if (!data.strStateFile.isEmpty())
7134
strStateFile = data.strStateFile;
7135
int vrc = calculateFullPath(strStateFile, strStateFile);
7136
if (RT_FAILURE(vrc))
7137
return setError(E_FAIL,
7138
tr("Invalid saved state file path '%s' (%Rrc)"),
7139
strStateFile.c_str(),
7143
/* create a snapshot machine object */
7144
ComObjPtr<SnapshotMachine> pSnapshotMachine;
7145
pSnapshotMachine.createObject();
7146
rc = pSnapshotMachine->init(this,
7151
if (FAILED(rc)) return rc;
7153
/* create a snapshot object */
7154
ComObjPtr<Snapshot> pSnapshot;
7155
pSnapshot.createObject();
7156
/* initialize the snapshot */
7157
rc = pSnapshot->init(mParent, // VirtualBox object
7160
data.strDescription,
7164
if (FAILED(rc)) return rc;
7166
/* memorize the first snapshot if necessary */
7167
if (!mData->mFirstSnapshot)
7168
mData->mFirstSnapshot = pSnapshot;
7170
/* memorize the current snapshot when appropriate */
7171
if ( !mData->mCurrentSnapshot
7172
&& pSnapshot->getId() == aCurSnapshotId
7174
mData->mCurrentSnapshot = pSnapshot;
7176
// now create the children
7177
for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
7178
it != data.llChildSnapshots.end();
7181
const settings::Snapshot &childData = *it;
7183
rc = loadSnapshot(childData,
7185
pSnapshot); // parent = the one we created above
7186
if (FAILED(rc)) return rc;
7193
* @param aNode <Hardware> node.
7195
HRESULT Machine::loadHardware(const settings::Hardware &data)
7197
AssertReturn(!isSessionMachine(), E_FAIL);
7203
/* The hardware version attribute (optional). */
7204
mHWData->mHWVersion = data.strVersion;
7205
mHWData->mHardwareUUID = data.uuid;
7207
mHWData->mHWVirtExEnabled = data.fHardwareVirt;
7208
mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
7209
mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
7210
mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
7211
mHWData->mHWVirtExVPIDEnabled = data.fVPID;
7212
mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
7213
mHWData->mPAEEnabled = data.fPAE;
7214
mHWData->mSyntheticCpu = data.fSyntheticCpu;
7216
mHWData->mCPUCount = data.cCPUs;
7217
mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
7218
mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
7221
if (mHWData->mCPUHotPlugEnabled)
7223
for (settings::CpuList::const_iterator it = data.llCpus.begin();
7224
it != data.llCpus.end();
7227
const settings::Cpu &cpu = *it;
7229
mHWData->mCPUAttached[cpu.ulId] = true;
7234
for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
7235
it != data.llCpuIdLeafs.end();
7238
const settings::CpuIdLeaf &leaf = *it;
7253
mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
7267
mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
7276
mHWData->mMemorySize = data.ulMemorySizeMB;
7277
mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
7281
i < RT_ELEMENTS(mHWData->mBootOrder);
7284
settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
7285
if (it == data.mapBootOrder.end())
7286
mHWData->mBootOrder[i] = DeviceType_Null;
7288
mHWData->mBootOrder[i] = it->second;
7291
mHWData->mVRAMSize = data.ulVRAMSizeMB;
7292
mHWData->mMonitorCount = data.cMonitors;
7293
mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
7294
mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
7295
mHWData->mFirmwareType = data.firmwareType;
7296
mHWData->mPointingHidType = data.pointingHidType;
7297
mHWData->mKeyboardHidType = data.keyboardHidType;
7298
mHWData->mChipsetType = data.chipsetType;
7299
mHWData->mHpetEnabled = data.fHpetEnabled;
7302
rc = mVRDEServer->loadSettings(data.vrdeSettings);
7303
if (FAILED(rc)) return rc;
7306
rc = mBIOSSettings->loadSettings(data.biosSettings);
7307
if (FAILED(rc)) return rc;
7309
/* USB Controller */
7310
rc = mUSBController->loadSettings(data.usbController);
7311
if (FAILED(rc)) return rc;
7314
for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
7315
it != data.llNetworkAdapters.end();
7318
const settings::NetworkAdapter &nic = *it;
7320
/* slot unicity is guaranteed by XML Schema */
7321
AssertBreak(nic.ulSlot < RT_ELEMENTS(mNetworkAdapters));
7322
rc = mNetworkAdapters[nic.ulSlot]->loadSettings(nic);
7323
if (FAILED(rc)) return rc;
7327
for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
7328
it != data.llSerialPorts.end();
7331
const settings::SerialPort &s = *it;
7333
AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
7334
rc = mSerialPorts[s.ulSlot]->loadSettings(s);
7335
if (FAILED(rc)) return rc;
7338
// parallel ports (optional)
7339
for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
7340
it != data.llParallelPorts.end();
7343
const settings::ParallelPort &p = *it;
7345
AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
7346
rc = mParallelPorts[p.ulSlot]->loadSettings(p);
7347
if (FAILED(rc)) return rc;
7351
rc = mAudioAdapter->loadSettings(data.audioAdapter);
7352
if (FAILED(rc)) return rc;
7354
for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
7355
it != data.llSharedFolders.end();
7358
const settings::SharedFolder &sf = *it;
7359
rc = CreateSharedFolder(Bstr(sf.strName).raw(),
7360
Bstr(sf.strHostPath).raw(),
7361
sf.fWritable, sf.fAutoMount);
7362
if (FAILED(rc)) return rc;
7366
mHWData->mClipboardMode = data.clipboardMode;
7369
mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
7372
mHWData->mIoCacheEnabled = data.ioSettings.fIoCacheEnabled;
7373
mHWData->mIoCacheSize = data.ioSettings.ulIoCacheSize;
7375
// Bandwidth control
7376
rc = mBandwidthControl->loadSettings(data.ioSettings);
7377
if (FAILED(rc)) return rc;
7379
#ifdef VBOX_WITH_GUEST_PROPS
7380
/* Guest properties (optional) */
7381
for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
7382
it != data.llGuestProperties.end();
7385
const settings::GuestProperty &prop = *it;
7386
uint32_t fFlags = guestProp::NILFLAG;
7387
guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
7388
HWData::GuestProperty property = { prop.strName, prop.strValue, prop.timestamp, fFlags };
7389
mHWData->mGuestProperties.push_back(property);
7392
mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
7393
#endif /* VBOX_WITH_GUEST_PROPS defined */
7395
catch(std::bad_alloc &)
7397
return E_OUTOFMEMORY;
7405
* Called from loadMachineDataFromSettings() for the storage controller data, including media.
7408
* @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
7409
* @param puuidSnapshot
7412
HRESULT Machine::loadStorageControllers(const settings::Storage &data,
7413
const Guid *puuidRegistry,
7414
const Guid *puuidSnapshot)
7416
AssertReturn(!isSessionMachine(), E_FAIL);
7420
for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
7421
it != data.llStorageControllers.end();
7424
const settings::StorageController &ctlData = *it;
7426
ComObjPtr<StorageController> pCtl;
7427
/* Try to find one with the name first. */
7428
rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
7430
return setError(VBOX_E_OBJECT_IN_USE,
7431
tr("Storage controller named '%s' already exists"),
7432
ctlData.strName.c_str());
7434
pCtl.createObject();
7435
rc = pCtl->init(this,
7440
if (FAILED(rc)) return rc;
7442
mStorageControllers->push_back(pCtl);
7444
rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
7445
if (FAILED(rc)) return rc;
7447
rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
7448
if (FAILED(rc)) return rc;
7450
rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
7451
if (FAILED(rc)) return rc;
7453
/* Set IDE emulation settings (only for AHCI controller). */
7454
if (ctlData.controllerType == StorageControllerType_IntelAhci)
7456
if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
7457
|| (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
7458
|| (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
7459
|| (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
7464
/* Load the attached devices now. */
7465
rc = loadStorageDevices(pCtl,
7469
if (FAILED(rc)) return rc;
7476
* Called from loadStorageControllers for a controller's devices.
7478
* @param aStorageController
7480
* @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
7481
* @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
7484
HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
7485
const settings::StorageController &data,
7486
const Guid *puuidRegistry,
7487
const Guid *puuidSnapshot)
7491
/* paranoia: detect duplicate attachments */
7492
for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
7493
it != data.llAttachedDevices.end();
7496
const settings::AttachedDevice &ad = *it;
7498
for (settings::AttachedDevicesList::const_iterator it2 = it;
7499
it2 != data.llAttachedDevices.end();
7505
const settings::AttachedDevice &ad2 = *it2;
7507
if ( ad.lPort == ad2.lPort
7508
&& ad.lDevice == ad2.lDevice)
7510
return setError(E_FAIL,
7511
tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
7512
aStorageController->getName().c_str(),
7515
mUserData->s.strName.c_str());
7520
for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
7521
it != data.llAttachedDevices.end();
7524
const settings::AttachedDevice &dev = *it;
7525
ComObjPtr<Medium> medium;
7527
switch (dev.deviceType)
7529
case DeviceType_Floppy:
7530
case DeviceType_DVD:
7531
if (dev.strHostDriveSrc.isNotEmpty())
7532
rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
7534
rc = mParent->findRemoveableMedium(dev.deviceType,
7536
false /* fRefresh */,
7537
false /* aSetError */,
7539
if (rc == VBOX_E_OBJECT_NOT_FOUND)
7540
// This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
7544
case DeviceType_HardDisk:
7546
/* find a hard disk by UUID */
7547
rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
7550
if (isSnapshotMachine())
7552
// wrap another error message around the "cannot find hard disk" set by findHardDisk
7553
// so the user knows that the bad disk is in a snapshot somewhere
7554
com::ErrorInfo info;
7555
return setError(E_FAIL,
7556
tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
7557
puuidSnapshot->raw(),
7558
info.getText().raw());
7564
AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
7566
if (medium->getType() == MediumType_Immutable)
7568
if (isSnapshotMachine())
7569
return setError(E_FAIL,
7570
tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
7571
"of the virtual machine '%s' ('%s')"),
7572
medium->getLocationFull().c_str(),
7574
puuidSnapshot->raw(),
7575
mUserData->s.strName.c_str(),
7576
mData->m_strConfigFileFull.c_str());
7578
return setError(E_FAIL,
7579
tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
7580
medium->getLocationFull().c_str(),
7582
mUserData->s.strName.c_str(),
7583
mData->m_strConfigFileFull.c_str());
7586
if (medium->getType() == MediumType_MultiAttach)
7588
if (isSnapshotMachine())
7589
return setError(E_FAIL,
7590
tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
7591
"of the virtual machine '%s' ('%s')"),
7592
medium->getLocationFull().c_str(),
7594
puuidSnapshot->raw(),
7595
mUserData->s.strName.c_str(),
7596
mData->m_strConfigFileFull.c_str());
7598
return setError(E_FAIL,
7599
tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
7600
medium->getLocationFull().c_str(),
7602
mUserData->s.strName.c_str(),
7603
mData->m_strConfigFileFull.c_str());
7606
if ( !isSnapshotMachine()
7607
&& medium->getChildren().size() != 0
7609
return setError(E_FAIL,
7610
tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
7611
"because it has %d differencing child hard disks"),
7612
medium->getLocationFull().c_str(),
7614
mUserData->s.strName.c_str(),
7615
mData->m_strConfigFileFull.c_str(),
7616
medium->getChildren().size());
7618
if (findAttachment(mMediaData->mAttachments,
7620
return setError(E_FAIL,
7621
tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
7622
medium->getLocationFull().c_str(),
7624
mUserData->s.strName.c_str(),
7625
mData->m_strConfigFileFull.c_str());
7631
return setError(E_FAIL,
7632
tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
7633
medium->getLocationFull().c_str(),
7634
mUserData->s.strName.c_str(),
7635
mData->m_strConfigFileFull.c_str());
7641
/* Bandwidth groups are loaded at this point. */
7642
ComObjPtr<BandwidthGroup> pBwGroup;
7644
if (!dev.strBwGroup.isEmpty())
7646
rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
7648
return setError(E_FAIL,
7649
tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
7650
medium->getLocationFull().c_str(),
7651
dev.strBwGroup.c_str(),
7652
mUserData->s.strName.c_str(),
7653
mData->m_strConfigFileFull.c_str());
7656
const Bstr controllerName = aStorageController->getName();
7657
ComObjPtr<MediumAttachment> pAttachment;
7658
pAttachment.createObject();
7659
rc = pAttachment->init(this,
7667
if (FAILED(rc)) break;
7669
/* associate the medium with this machine and snapshot */
7670
if (!medium.isNull())
7672
if (isSnapshotMachine())
7673
rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
7675
rc = medium->addBackReference(mData->mUuid);
7678
// caller wants registry ID to be set on all attached media (OVF import case)
7679
medium->addRegistry(*puuidRegistry);
7685
/* back up mMediaData to let registeredInit() properly rollback on failure
7686
* (= limited accessibility) */
7687
setModified(IsModified_Storage);
7688
mMediaData.backup();
7689
mMediaData->mAttachments.push_back(pAttachment);
7696
* Returns the snapshot with the given UUID or fails of no such snapshot exists.
7698
* @param aId snapshot UUID to find (empty UUID refers the first snapshot)
7699
* @param aSnapshot where to return the found snapshot
7700
* @param aSetError true to set extended error info on failure
7702
HRESULT Machine::findSnapshotById(const Guid &aId,
7703
ComObjPtr<Snapshot> &aSnapshot,
7704
bool aSetError /* = false */)
7706
AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
7708
if (!mData->mFirstSnapshot)
7711
return setError(E_FAIL, tr("This machine does not have any snapshots"));
7716
aSnapshot = mData->mFirstSnapshot;
7718
aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
7723
return setError(E_FAIL,
7724
tr("Could not find a snapshot with UUID {%s}"),
7725
aId.toString().c_str());
7733
* Returns the snapshot with the given name or fails of no such snapshot.
7735
* @param aName snapshot name to find
7736
* @param aSnapshot where to return the found snapshot
7737
* @param aSetError true to set extended error info on failure
7739
HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
7740
ComObjPtr<Snapshot> &aSnapshot,
7741
bool aSetError /* = false */)
7743
AssertReturn(!strName.isEmpty(), E_INVALIDARG);
7745
AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
7747
if (!mData->mFirstSnapshot)
7750
return setError(VBOX_E_OBJECT_NOT_FOUND,
7751
tr("This machine does not have any snapshots"));
7752
return VBOX_E_OBJECT_NOT_FOUND;
7755
aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
7760
return setError(VBOX_E_OBJECT_NOT_FOUND,
7761
tr("Could not find a snapshot named '%s'"), strName.c_str());
7762
return VBOX_E_OBJECT_NOT_FOUND;
7769
* Returns a storage controller object with the given name.
7771
* @param aName storage controller name to find
7772
* @param aStorageController where to return the found storage controller
7773
* @param aSetError true to set extended error info on failure
7775
HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
7776
ComObjPtr<StorageController> &aStorageController,
7777
bool aSetError /* = false */)
7779
AssertReturn(!aName.isEmpty(), E_INVALIDARG);
7781
for (StorageControllerList::const_iterator it = mStorageControllers->begin();
7782
it != mStorageControllers->end();
7785
if ((*it)->getName() == aName)
7787
aStorageController = (*it);
7793
return setError(VBOX_E_OBJECT_NOT_FOUND,
7794
tr("Could not find a storage controller named '%s'"),
7796
return VBOX_E_OBJECT_NOT_FOUND;
7799
HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
7800
MediaData::AttachmentList &atts)
7802
AutoCaller autoCaller(this);
7803
if (FAILED(autoCaller.rc())) return autoCaller.rc();
7805
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7807
for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
7808
it != mMediaData->mAttachments.end();
7811
const ComObjPtr<MediumAttachment> &pAtt = *it;
7813
// should never happen, but deal with NULL pointers in the list.
7814
AssertStmt(!pAtt.isNull(), continue);
7816
// getControllerName() needs caller+read lock
7817
AutoCaller autoAttCaller(pAtt);
7818
if (FAILED(autoAttCaller.rc()))
7821
return autoAttCaller.rc();
7823
AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
7825
if (pAtt->getControllerName() == aName)
7826
atts.push_back(pAtt);
7833
* Helper for #saveSettings. Cares about renaming the settings directory and
7834
* file if the machine name was changed and about creating a new settings file
7835
* if this is a new machine.
7837
* @note Must be never called directly but only from #saveSettings().
7839
HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
7841
AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7845
bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
7847
/* attempt to rename the settings file if machine name is changed */
7848
if ( mUserData->s.fNameSync
7849
&& mUserData.isBackedUp()
7850
&& mUserData.backedUpData()->s.strName != mUserData->s.strName
7853
bool dirRenamed = false;
7854
bool fileRenamed = false;
7856
Utf8Str configFile, newConfigFile;
7857
Utf8Str configDir, newConfigDir;
7861
int vrc = VINF_SUCCESS;
7863
Utf8Str name = mUserData.backedUpData()->s.strName;
7864
Utf8Str newName = mUserData->s.strName;
7866
configFile = mData->m_strConfigFileFull;
7868
/* first, rename the directory if it matches the machine name */
7869
configDir = configFile;
7870
configDir.stripFilename();
7871
newConfigDir = configDir;
7872
if (!strcmp(RTPathFilename(configDir.c_str()), name.c_str()))
7874
newConfigDir.stripFilename();
7875
newConfigDir.append(RTPATH_DELIMITER);
7876
newConfigDir.append(newName);
7877
/* new dir and old dir cannot be equal here because of 'if'
7878
* above and because name != newName */
7879
Assert(configDir != newConfigDir);
7880
if (!fSettingsFileIsNew)
7882
/* perform real rename only if the machine is not new */
7883
vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
7884
if (RT_FAILURE(vrc))
7886
rc = setError(E_FAIL,
7887
tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
7889
newConfigDir.c_str(),
7897
newConfigFile = Utf8StrFmt("%s%c%s.vbox",
7898
newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
7900
/* then try to rename the settings file itself */
7901
if (newConfigFile != configFile)
7903
/* get the path to old settings file in renamed directory */
7904
configFile = Utf8StrFmt("%s%c%s",
7905
newConfigDir.c_str(),
7907
RTPathFilename(configFile.c_str()));
7908
if (!fSettingsFileIsNew)
7910
/* perform real rename only if the machine is not new */
7911
vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
7912
if (RT_FAILURE(vrc))
7914
rc = setError(E_FAIL,
7915
tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
7917
newConfigFile.c_str(),
7925
// update m_strConfigFileFull amd mConfigFile
7926
mData->m_strConfigFileFull = newConfigFile;
7927
// compute the relative path too
7928
mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
7930
// store the old and new so that VirtualBox::saveSettings() can update
7931
// the media registry
7932
if ( mData->mRegistered
7933
&& configDir != newConfigDir)
7935
mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
7937
if (pfNeedsGlobalSaveSettings)
7938
*pfNeedsGlobalSaveSettings = true;
7941
/* update the saved state file path */
7942
Utf8Str path = mSSData->mStateFilePath;
7943
if (RTPathStartsWith(path.c_str(), configDir.c_str()))
7944
mSSData->mStateFilePath = Utf8StrFmt("%s%s",
7945
newConfigDir.c_str(),
7946
path.c_str() + configDir.length());
7948
/* Update saved state file paths of all online snapshots.
7949
* Note that saveSettings() will recognize name change
7950
* and will save all snapshots in this case. */
7951
if (mData->mFirstSnapshot)
7952
mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
7953
newConfigDir.c_str());
7959
/* silently try to rename everything back */
7961
RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
7963
RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
7966
if (FAILED(rc)) return rc;
7969
if (fSettingsFileIsNew)
7971
/* create a virgin config file */
7972
int vrc = VINF_SUCCESS;
7974
/* ensure the settings directory exists */
7975
Utf8Str path(mData->m_strConfigFileFull);
7976
path.stripFilename();
7977
if (!RTDirExists(path.c_str()))
7979
vrc = RTDirCreateFullPath(path.c_str(), 0777);
7980
if (RT_FAILURE(vrc))
7982
return setError(E_FAIL,
7983
tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
7989
/* Note: open flags must correlate with RTFileOpen() in lockConfig() */
7990
path = Utf8Str(mData->m_strConfigFileFull);
7991
RTFILE f = NIL_RTFILE;
7992
vrc = RTFileOpen(&f, path.c_str(),
7993
RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
7994
if (RT_FAILURE(vrc))
7995
return setError(E_FAIL,
7996
tr("Could not create the settings file '%s' (%Rrc)"),
8006
* Saves and commits machine data, user data and hardware data.
8008
* Note that on failure, the data remains uncommitted.
8010
* @a aFlags may combine the following flags:
8012
* - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
8013
* Used when saving settings after an operation that makes them 100%
8014
* correspond to the settings from the current snapshot.
8015
* - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
8016
* #isReallyModified() returns false. This is necessary for cases when we
8017
* change machine data directly, not through the backup()/commit() mechanism.
8018
* - SaveS_Force: settings will be saved without doing a deep compare of the
8019
* settings structures. This is used when this is called because snapshots
8020
* have changed to avoid the overhead of the deep compare.
8022
* @note Must be called from under this object's write lock. Locks children for
8025
* @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
8026
* initialized to false and that will be set to true by this function if
8027
* the caller must invoke VirtualBox::saveSettings() because the global
8028
* settings have changed. This will happen if a machine rename has been
8029
* saved and the global machine and media registries will therefore need
8032
HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
8035
LogFlowThisFuncEnter();
8037
AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8039
/* make sure child objects are unable to modify the settings while we are
8041
ensureNoStateDependencies();
8043
AssertReturn(!isSnapshotMachine(),
8047
bool fNeedsWrite = false;
8049
/* First, prepare to save settings. It will care about renaming the
8050
* settings directory and file if the machine name was changed and about
8051
* creating a new settings file if this is a new machine. */
8052
rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
8053
if (FAILED(rc)) return rc;
8055
// keep a pointer to the current settings structures
8056
settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
8057
settings::MachineConfigFile *pNewConfig = NULL;
8061
// make a fresh one to have everyone write stuff into
8062
pNewConfig = new settings::MachineConfigFile(NULL);
8063
pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
8065
// now go and copy all the settings data from COM to the settings structures
8066
// (this calles saveSettings() on all the COM objects in the machine)
8067
copyMachineDataToSettings(*pNewConfig);
8069
if (aFlags & SaveS_ResetCurStateModified)
8071
// this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
8072
mData->mCurrentStateModified = FALSE;
8073
fNeedsWrite = true; // always, no need to compare
8075
else if (aFlags & SaveS_Force)
8077
fNeedsWrite = true; // always, no need to compare
8081
if (!mData->mCurrentStateModified)
8083
// do a deep compare of the settings that we just saved with the settings
8084
// previously stored in the config file; this invokes MachineConfigFile::operator==
8085
// which does a deep compare of all the settings, which is expensive but less expensive
8086
// than writing out XML in vain
8087
bool fAnySettingsChanged = (*pNewConfig == *pOldConfig);
8089
// could still be modified if any settings changed
8090
mData->mCurrentStateModified = fAnySettingsChanged;
8092
fNeedsWrite = fAnySettingsChanged;
8098
pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
8101
// now spit it all out!
8102
pNewConfig->write(mData->m_strConfigFileFull);
8104
mData->pMachineConfigFile = pNewConfig;
8108
// after saving settings, we are no longer different from the XML on disk
8109
mData->flModifications = 0;
8113
// we assume that error info is set by the thrower
8116
// restore old config
8118
mData->pMachineConfigFile = pOldConfig;
8122
rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
8125
if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
8127
/* Fire the data change event, even on failure (since we've already
8128
* committed all data). This is done only for SessionMachines because
8129
* mutable Machine instances are always not registered (i.e. private
8130
* to the client process that creates them) and thus don't need to
8131
* inform callbacks. */
8132
if (isSessionMachine())
8133
mParent->onMachineDataChange(mData->mUuid);
8136
LogFlowThisFunc(("rc=%08X\n", rc));
8137
LogFlowThisFuncLeave();
8142
* Implementation for saving the machine settings into the given
8143
* settings::MachineConfigFile instance. This copies machine extradata
8144
* from the previous machine config file in the instance data, if any.
8146
* This gets called from two locations:
8148
* -- Machine::saveSettings(), during the regular XML writing;
8150
* -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
8151
* exported to OVF and we write the VirtualBox proprietary XML
8152
* into a <vbox:Machine> tag.
8154
* This routine fills all the fields in there, including snapshots, *except*
8155
* for the following:
8157
* -- fCurrentStateModified. There is some special logic associated with that.
8159
* The caller can then call MachineConfigFile::write() or do something else
8162
* Caller must hold the machine lock!
8164
* This throws XML errors and HRESULT, so the caller must have a catch block!
8166
void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
8168
// deep copy extradata
8169
config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
8171
config.uuid = mData->mUuid;
8173
// copy name, description, OS type, teleport, UTC etc.
8174
config.machineUserData = mUserData->s;
8176
if ( mData->mMachineState == MachineState_Saved
8177
|| mData->mMachineState == MachineState_Restoring
8178
// when deleting a snapshot we may or may not have a saved state in the current state,
8179
// so let's not assert here please
8180
|| ( ( mData->mMachineState == MachineState_DeletingSnapshot
8181
|| mData->mMachineState == MachineState_DeletingSnapshotOnline
8182
|| mData->mMachineState == MachineState_DeletingSnapshotPaused)
8183
&& (!mSSData->mStateFilePath.isEmpty())
8187
Assert(!mSSData->mStateFilePath.isEmpty());
8188
/* try to make the file name relative to the settings file dir */
8189
copyPathRelativeToMachine(mSSData->mStateFilePath, config.strStateFile);
8193
Assert(mSSData->mStateFilePath.isEmpty());
8194
config.strStateFile.setNull();
8197
if (mData->mCurrentSnapshot)
8198
config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
8200
config.uuidCurrentSnapshot.clear();
8202
config.timeLastStateChange = mData->mLastStateChange;
8203
config.fAborted = (mData->mMachineState == MachineState_Aborted);
8204
/// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
8206
HRESULT rc = saveHardware(config.hardwareMachine);
8207
if (FAILED(rc)) throw rc;
8209
rc = saveStorageControllers(config.storageMachine);
8210
if (FAILED(rc)) throw rc;
8212
// save machine's media registry if this is VirtualBox 4.0 or later
8213
if (config.canHaveOwnMediaRegistry())
8215
// determine machine folder
8216
Utf8Str strMachineFolder = getSettingsFileFull();
8217
strMachineFolder.stripFilename();
8218
mParent->saveMediaRegistry(config.mediaRegistry,
8219
getId(), // only media with registry ID == machine UUID
8221
// this throws HRESULT
8225
rc = saveAllSnapshots(config);
8226
if (FAILED(rc)) throw rc;
8230
* Saves all snapshots of the machine into the given machine config file. Called
8231
* from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
8235
HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
8237
AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8243
config.llFirstSnapshot.clear();
8245
if (mData->mFirstSnapshot)
8247
settings::Snapshot snapNew;
8248
config.llFirstSnapshot.push_back(snapNew);
8250
// get reference to the fresh copy of the snapshot on the list and
8251
// work on that copy directly to avoid excessive copying later
8252
settings::Snapshot &snap = config.llFirstSnapshot.front();
8254
rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
8255
if (FAILED(rc)) throw rc;
8258
// if (mType == IsSessionMachine)
8259
// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
8264
/* we assume that error info is set by the thrower */
8269
rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
8276
* Saves the VM hardware configuration. It is assumed that the
8277
* given node is empty.
8279
* @param aNode <Hardware> node to save the VM hardware configuration to.
8281
HRESULT Machine::saveHardware(settings::Hardware &data)
8287
/* The hardware version attribute (optional).
8288
Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
8289
if ( mHWData->mHWVersion == "1"
8290
&& mSSData->mStateFilePath.isEmpty()
8292
mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some other point needs to be found where this can be done. */
8294
data.strVersion = mHWData->mHWVersion;
8295
data.uuid = mHWData->mHardwareUUID;
8298
data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
8299
data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
8300
data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
8301
data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
8302
data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
8303
data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
8304
data.fPAE = !!mHWData->mPAEEnabled;
8305
data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
8307
/* Standard and Extended CPUID leafs. */
8308
data.llCpuIdLeafs.clear();
8309
for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
8311
if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
8312
data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
8314
for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
8316
if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
8317
data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
8320
data.cCPUs = mHWData->mCPUCount;
8321
data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
8322
data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
8324
data.llCpus.clear();
8325
if (data.fCpuHotPlug)
8327
for (unsigned idx = 0; idx < data.cCPUs; idx++)
8329
if (mHWData->mCPUAttached[idx])
8333
data.llCpus.push_back(cpu);
8339
data.ulMemorySizeMB = mHWData->mMemorySize;
8340
data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
8343
data.firmwareType = mHWData->mFirmwareType;
8346
data.pointingHidType = mHWData->mPointingHidType;
8347
data.keyboardHidType = mHWData->mKeyboardHidType;
8350
data.chipsetType = mHWData->mChipsetType;
8353
data.fHpetEnabled = !!mHWData->mHpetEnabled;
8356
data.mapBootOrder.clear();
8358
i < RT_ELEMENTS(mHWData->mBootOrder);
8360
data.mapBootOrder[i] = mHWData->mBootOrder[i];
8363
data.ulVRAMSizeMB = mHWData->mVRAMSize;
8364
data.cMonitors = mHWData->mMonitorCount;
8365
data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
8366
data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
8368
/* VRDEServer settings (optional) */
8369
rc = mVRDEServer->saveSettings(data.vrdeSettings);
8370
if (FAILED(rc)) throw rc;
8372
/* BIOS (required) */
8373
rc = mBIOSSettings->saveSettings(data.biosSettings);
8374
if (FAILED(rc)) throw rc;
8376
/* USB Controller (required) */
8377
rc = mUSBController->saveSettings(data.usbController);
8378
if (FAILED(rc)) throw rc;
8380
/* Network adapters (required) */
8381
data.llNetworkAdapters.clear();
8382
for (ULONG slot = 0;
8383
slot < RT_ELEMENTS(mNetworkAdapters);
8386
settings::NetworkAdapter nic;
8388
rc = mNetworkAdapters[slot]->saveSettings(nic);
8389
if (FAILED(rc)) throw rc;
8391
data.llNetworkAdapters.push_back(nic);
8395
data.llSerialPorts.clear();
8396
for (ULONG slot = 0;
8397
slot < RT_ELEMENTS(mSerialPorts);
8400
settings::SerialPort s;
8402
rc = mSerialPorts[slot]->saveSettings(s);
8403
if (FAILED(rc)) return rc;
8405
data.llSerialPorts.push_back(s);
8408
/* Parallel ports */
8409
data.llParallelPorts.clear();
8410
for (ULONG slot = 0;
8411
slot < RT_ELEMENTS(mParallelPorts);
8414
settings::ParallelPort p;
8416
rc = mParallelPorts[slot]->saveSettings(p);
8417
if (FAILED(rc)) return rc;
8419
data.llParallelPorts.push_back(p);
8423
rc = mAudioAdapter->saveSettings(data.audioAdapter);
8424
if (FAILED(rc)) return rc;
8426
/* Shared folders */
8427
data.llSharedFolders.clear();
8428
for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8429
it != mHWData->mSharedFolders.end();
8432
ComObjPtr<SharedFolder> pFolder = *it;
8433
settings::SharedFolder sf;
8434
sf.strName = pFolder->getName();
8435
sf.strHostPath = pFolder->getHostPath();
8436
sf.fWritable = !!pFolder->isWritable();
8437
sf.fAutoMount = !!pFolder->isAutoMounted();
8439
data.llSharedFolders.push_back(sf);
8443
data.clipboardMode = mHWData->mClipboardMode;
8446
data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
8449
data.ioSettings.fIoCacheEnabled = !!mHWData->mIoCacheEnabled;
8450
data.ioSettings.ulIoCacheSize = mHWData->mIoCacheSize;
8452
/* BandwidthControl (required) */
8453
rc = mBandwidthControl->saveSettings(data.ioSettings);
8454
if (FAILED(rc)) throw rc;
8457
data.llGuestProperties.clear();
8458
#ifdef VBOX_WITH_GUEST_PROPS
8459
for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
8460
it != mHWData->mGuestProperties.end();
8463
HWData::GuestProperty property = *it;
8465
/* Remove transient guest properties at shutdown unless we
8466
* are saving state */
8467
if ( ( mData->mMachineState == MachineState_PoweredOff
8468
|| mData->mMachineState == MachineState_Aborted
8469
|| mData->mMachineState == MachineState_Teleported)
8470
&& property.mFlags & guestProp::TRANSIENT)
8472
settings::GuestProperty prop;
8473
prop.strName = property.strName;
8474
prop.strValue = property.strValue;
8475
prop.timestamp = property.mTimestamp;
8476
char szFlags[guestProp::MAX_FLAGS_LEN + 1];
8477
guestProp::writeFlags(property.mFlags, szFlags);
8478
prop.strFlags = szFlags;
8480
data.llGuestProperties.push_back(prop);
8483
data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
8484
/* I presume this doesn't require a backup(). */
8485
mData->mGuestPropertiesModified = FALSE;
8486
#endif /* VBOX_WITH_GUEST_PROPS defined */
8488
catch(std::bad_alloc &)
8490
return E_OUTOFMEMORY;
8498
* Saves the storage controller configuration.
8500
* @param aNode <StorageControllers> node to save the VM hardware configuration to.
8502
HRESULT Machine::saveStorageControllers(settings::Storage &data)
8504
data.llStorageControllers.clear();
8506
for (StorageControllerList::const_iterator it = mStorageControllers->begin();
8507
it != mStorageControllers->end();
8511
ComObjPtr<StorageController> pCtl = *it;
8513
settings::StorageController ctl;
8514
ctl.strName = pCtl->getName();
8515
ctl.controllerType = pCtl->getControllerType();
8516
ctl.storageBus = pCtl->getStorageBus();
8517
ctl.ulInstance = pCtl->getInstance();
8518
ctl.fBootable = pCtl->getBootable();
8520
/* Save the port count. */
8522
rc = pCtl->COMGETTER(PortCount)(&portCount);
8523
ComAssertComRCRet(rc, rc);
8524
ctl.ulPortCount = portCount;
8526
/* Save fUseHostIOCache */
8527
BOOL fUseHostIOCache;
8528
rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
8529
ComAssertComRCRet(rc, rc);
8530
ctl.fUseHostIOCache = !!fUseHostIOCache;
8532
/* Save IDE emulation settings. */
8533
if (ctl.controllerType == StorageControllerType_IntelAhci)
8535
if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
8536
|| (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
8537
|| (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
8538
|| (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
8540
ComAssertComRCRet(rc, rc);
8543
/* save the devices now. */
8544
rc = saveStorageDevices(pCtl, ctl);
8545
ComAssertComRCRet(rc, rc);
8547
data.llStorageControllers.push_back(ctl);
8554
* Saves the hard disk configuration.
8556
HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
8557
settings::StorageController &data)
8559
MediaData::AttachmentList atts;
8561
HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
8562
if (FAILED(rc)) return rc;
8564
data.llAttachedDevices.clear();
8565
for (MediaData::AttachmentList::const_iterator it = atts.begin();
8569
settings::AttachedDevice dev;
8571
MediumAttachment *pAttach = *it;
8572
Medium *pMedium = pAttach->getMedium();
8573
BandwidthGroup *pBwGroup = pAttach->getBandwidthGroup();
8575
dev.deviceType = pAttach->getType();
8576
dev.lPort = pAttach->getPort();
8577
dev.lDevice = pAttach->getDevice();
8580
if (pMedium->isHostDrive())
8581
dev.strHostDriveSrc = pMedium->getLocationFull();
8583
dev.uuid = pMedium->getId();
8584
dev.fPassThrough = pAttach->getPassthrough();
8589
dev.strBwGroup = pBwGroup->getName();
8592
data.llAttachedDevices.push_back(dev);
8599
* Saves machine state settings as defined by aFlags
8600
* (SaveSTS_* values).
8602
* @param aFlags Combination of SaveSTS_* flags.
8604
* @note Locks objects for writing.
8606
HRESULT Machine::saveStateSettings(int aFlags)
8611
AutoCaller autoCaller(this);
8612
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8614
/* This object's write lock is also necessary to serialize file access
8615
* (prevent concurrent reads and writes) */
8616
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8620
Assert(mData->pMachineConfigFile);
8624
if (aFlags & SaveSTS_CurStateModified)
8625
mData->pMachineConfigFile->fCurrentStateModified = true;
8627
if (aFlags & SaveSTS_StateFilePath)
8629
if (!mSSData->mStateFilePath.isEmpty())
8630
/* try to make the file name relative to the settings file dir */
8631
copyPathRelativeToMachine(mSSData->mStateFilePath, mData->pMachineConfigFile->strStateFile);
8633
mData->pMachineConfigFile->strStateFile.setNull();
8636
if (aFlags & SaveSTS_StateTimeStamp)
8638
Assert( mData->mMachineState != MachineState_Aborted
8639
|| mSSData->mStateFilePath.isEmpty());
8641
mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
8643
mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
8644
//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
8647
mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
8651
rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
8658
* Creates differencing hard disks for all normal hard disks attached to this
8659
* machine and a new set of attachments to refer to created disks.
8661
* Used when taking a snapshot or when deleting the current state. Gets called
8662
* from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
8664
* This method assumes that mMediaData contains the original hard disk attachments
8665
* it needs to create diffs for. On success, these attachments will be replaced
8666
* with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
8667
* called to delete created diffs which will also rollback mMediaData and restore
8668
* whatever was backed up before calling this method.
8670
* Attachments with non-normal hard disks are left as is.
8672
* If @a aOnline is @c false then the original hard disks that require implicit
8673
* diffs will be locked for reading. Otherwise it is assumed that they are
8674
* already locked for writing (when the VM was started). Note that in the latter
8675
* case it is responsibility of the caller to lock the newly created diffs for
8676
* writing if this method succeeds.
8678
* @param aProgress Progress object to run (must contain at least as
8679
* many operations left as the number of hard disks
8681
* @param aOnline Whether the VM was online prior to this operation.
8682
* @param pllRegistriesThatNeedSaving Optional pointer to a list of UUIDs to receive the registry IDs that need saving
8684
* @note The progress object is not marked as completed, neither on success nor
8685
* on failure. This is a responsibility of the caller.
8687
* @note Locks this object for writing.
8689
HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
8692
GuidList *pllRegistriesThatNeedSaving)
8694
LogFlowThisFunc(("aOnline=%d\n", aOnline));
8696
AutoCaller autoCaller(this);
8697
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8699
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8701
/* must be in a protective state because we leave the lock below */
8702
AssertReturn( mData->mMachineState == MachineState_Saving
8703
|| mData->mMachineState == MachineState_LiveSnapshotting
8704
|| mData->mMachineState == MachineState_RestoringSnapshot
8705
|| mData->mMachineState == MachineState_DeletingSnapshot
8710
MediumLockListMap lockedMediaOffline;
8711
MediumLockListMap *lockedMediaMap;
8713
lockedMediaMap = &mData->mSession.mLockedMedia;
8715
lockedMediaMap = &lockedMediaOffline;
8721
/* lock all attached hard disks early to detect "in use"
8722
* situations before creating actual diffs */
8723
for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8724
it != mMediaData->mAttachments.end();
8727
MediumAttachment* pAtt = *it;
8728
if (pAtt->getType() == DeviceType_HardDisk)
8730
Medium* pMedium = pAtt->getMedium();
8733
MediumLockList *pMediumLockList(new MediumLockList());
8734
rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
8735
false /* fMediumLockWrite */,
8740
delete pMediumLockList;
8743
rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
8747
tr("Collecting locking information for all attached media failed"));
8752
/* Now lock all media. If this fails, nothing is locked. */
8753
rc = lockedMediaMap->Lock();
8757
tr("Locking of attached media failed"));
8761
/* remember the current list (note that we don't use backup() since
8762
* mMediaData may be already backed up) */
8763
MediaData::AttachmentList atts = mMediaData->mAttachments;
8765
/* start from scratch */
8766
mMediaData->mAttachments.clear();
8768
/* go through remembered attachments and create diffs for normal hard
8769
* disks and attach them */
8770
for (MediaData::AttachmentList::const_iterator it = atts.begin();
8774
MediumAttachment* pAtt = *it;
8776
DeviceType_T devType = pAtt->getType();
8777
Medium* pMedium = pAtt->getMedium();
8779
if ( devType != DeviceType_HardDisk
8781
|| pMedium->getType() != MediumType_Normal)
8783
/* copy the attachment as is */
8785
/** @todo the progress object created in Console::TakeSnaphot
8786
* only expects operations for hard disks. Later other
8787
* device types need to show up in the progress as well. */
8788
if (devType == DeviceType_HardDisk)
8790
if (pMedium == NULL)
8791
aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
8794
aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
8795
pMedium->getBase()->getName().c_str()).raw(),
8799
mMediaData->mAttachments.push_back(pAtt);
8804
aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
8805
pMedium->getBase()->getName().c_str()).raw(),
8808
Utf8Str strFullSnapshotFolder;
8809
calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
8811
ComObjPtr<Medium> diff;
8812
diff.createObject();
8813
rc = diff->init(mParent,
8814
pMedium->getPreferredDiffFormat(),
8815
strFullSnapshotFolder.append(RTPATH_SLASH_STR),
8816
pMedium->getFirstRegistryMachineId(), // store the diff in the same registry as the parent
8817
pllRegistriesThatNeedSaving);
8818
if (FAILED(rc)) throw rc;
8820
/** @todo r=bird: How is the locking and diff image cleaned up if we fail before
8821
* the push_back? Looks like we're going to leave medium with the
8822
* wrong kind of lock (general issue with if we fail anywhere at all)
8823
* and an orphaned VDI in the snapshots folder. */
8825
/* update the appropriate lock list */
8826
MediumLockList *pMediumLockList;
8827
rc = lockedMediaMap->Get(pAtt, pMediumLockList);
8828
AssertComRCThrowRC(rc);
8831
rc = pMediumLockList->Update(pMedium, false);
8832
AssertComRCThrowRC(rc);
8835
/* leave the lock before the potentially lengthy operation */
8837
rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
8839
NULL /* aProgress */,
8841
pllRegistriesThatNeedSaving);
8843
if (FAILED(rc)) throw rc;
8845
rc = lockedMediaMap->Unlock();
8846
AssertComRCThrowRC(rc);
8847
rc = pMediumLockList->Append(diff, true);
8848
AssertComRCThrowRC(rc);
8849
rc = lockedMediaMap->Lock();
8850
AssertComRCThrowRC(rc);
8852
rc = diff->addBackReference(mData->mUuid);
8853
AssertComRCThrowRC(rc);
8855
/* add a new attachment */
8856
ComObjPtr<MediumAttachment> attachment;
8857
attachment.createObject();
8858
rc = attachment->init(this,
8860
pAtt->getControllerName(),
8863
DeviceType_HardDisk,
8864
true /* aImplicit */,
8865
pAtt->getBandwidthGroup());
8866
if (FAILED(rc)) throw rc;
8868
rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
8869
AssertComRCThrowRC(rc);
8870
mMediaData->mAttachments.push_back(attachment);
8873
catch (HRESULT aRC) { rc = aRC; }
8875
/* unlock all hard disks we locked */
8878
ErrorInfoKeeper eik;
8880
rc = lockedMediaMap->Clear();
8886
MultiResult mrc = rc;
8888
mrc = deleteImplicitDiffs(pllRegistriesThatNeedSaving);
8895
* Deletes implicit differencing hard disks created either by
8896
* #createImplicitDiffs() or by #AttachMedium() and rolls back mMediaData.
8898
* Note that to delete hard disks created by #AttachMedium() this method is
8899
* called from #fixupMedia() when the changes are rolled back.
8901
* @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
8902
* by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
8904
* @note Locks this object for writing.
8906
HRESULT Machine::deleteImplicitDiffs(GuidList *pllRegistriesThatNeedSaving)
8908
AutoCaller autoCaller(this);
8909
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8911
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8912
LogFlowThisFuncEnter();
8914
AssertReturn(mMediaData.isBackedUp(), E_FAIL);
8918
MediaData::AttachmentList implicitAtts;
8920
const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
8922
/* enumerate new attachments */
8923
for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8924
it != mMediaData->mAttachments.end();
8927
ComObjPtr<Medium> hd = (*it)->getMedium();
8931
if ((*it)->isImplicit())
8933
/* deassociate and mark for deletion */
8934
LogFlowThisFunc(("Detaching '%s', pending deletion\n", (*it)->getLogName()));
8935
rc = hd->removeBackReference(mData->mUuid);
8937
implicitAtts.push_back(*it);
8941
/* was this hard disk attached before? */
8942
if (!findAttachment(oldAtts, hd))
8944
/* no: de-associate */
8945
LogFlowThisFunc(("Detaching '%s', no deletion\n", (*it)->getLogName()));
8946
rc = hd->removeBackReference(mData->mUuid);
8950
LogFlowThisFunc(("Not detaching '%s'\n", (*it)->getLogName()));
8953
/* rollback hard disk changes */
8954
mMediaData.rollback();
8956
MultiResult mrc(S_OK);
8958
/* delete unused implicit diffs */
8959
if (implicitAtts.size() != 0)
8961
/* will leave the lock before the potentially lengthy
8962
* operation, so protect with the special state (unless already
8964
MachineState_T oldState = mData->mMachineState;
8965
if ( oldState != MachineState_Saving
8966
&& oldState != MachineState_LiveSnapshotting
8967
&& oldState != MachineState_RestoringSnapshot
8968
&& oldState != MachineState_DeletingSnapshot
8969
&& oldState != MachineState_DeletingSnapshotOnline
8970
&& oldState != MachineState_DeletingSnapshotPaused
8972
setMachineState(MachineState_SettingUp);
8976
for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
8977
it != implicitAtts.end();
8980
LogFlowThisFunc(("Deleting '%s'\n", (*it)->getLogName()));
8981
ComObjPtr<Medium> hd = (*it)->getMedium();
8983
rc = hd->deleteStorage(NULL /*aProgress*/, true /*aWait*/,
8984
pllRegistriesThatNeedSaving);
8985
AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() ));
8991
if (mData->mMachineState == MachineState_SettingUp)
8992
setMachineState(oldState);
8999
* Looks through the given list of media attachments for one with the given parameters
9000
* and returns it, or NULL if not found. The list is a parameter so that backup lists
9001
* can be searched as well if needed.
9004
* @param aControllerName
9005
* @param aControllerPort
9009
MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
9010
IN_BSTR aControllerName,
9011
LONG aControllerPort,
9014
for (MediaData::AttachmentList::const_iterator it = ll.begin();
9018
MediumAttachment *pAttach = *it;
9019
if (pAttach->matches(aControllerName, aControllerPort, aDevice))
9027
* Looks through the given list of media attachments for one with the given parameters
9028
* and returns it, or NULL if not found. The list is a parameter so that backup lists
9029
* can be searched as well if needed.
9032
* @param aControllerName
9033
* @param aControllerPort
9037
MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
9038
ComObjPtr<Medium> pMedium)
9040
for (MediaData::AttachmentList::const_iterator it = ll.begin();
9044
MediumAttachment *pAttach = *it;
9045
ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
9046
if (pMediumThis == pMedium)
9054
* Looks through the given list of media attachments for one with the given parameters
9055
* and returns it, or NULL if not found. The list is a parameter so that backup lists
9056
* can be searched as well if needed.
9059
* @param aControllerName
9060
* @param aControllerPort
9064
MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
9067
for (MediaData::AttachmentList::const_iterator it = ll.begin();
9071
MediumAttachment *pAttach = *it;
9072
ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
9073
if (pMediumThis->getId() == id)
9081
* Main implementation for Machine::DetachDevice. This also gets called
9082
* from Machine::prepareUnregister() so it has been taken out for simplicity.
9084
* @param pAttach Medium attachment to detach.
9085
* @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
9086
* @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
9087
* @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
9088
* by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
9091
HRESULT Machine::detachDevice(MediumAttachment *pAttach,
9092
AutoWriteLock &writeLock,
9093
Snapshot *pSnapshot,
9094
GuidList *pllRegistriesThatNeedSaving)
9096
ComObjPtr<Medium> oldmedium = pAttach->getMedium();
9097
DeviceType_T mediumType = pAttach->getType();
9099
LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
9101
if (pAttach->isImplicit())
9103
/* attempt to implicitly delete the implicitly created diff */
9105
/// @todo move the implicit flag from MediumAttachment to Medium
9106
/// and forbid any hard disk operation when it is implicit. Or maybe
9107
/// a special media state for it to make it even more simple.
9109
Assert(mMediaData.isBackedUp());
9111
/* will leave the lock before the potentially lengthy operation, so
9112
* protect with the special state */
9113
MachineState_T oldState = mData->mMachineState;
9114
setMachineState(MachineState_SettingUp);
9116
writeLock.release();
9118
HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/,
9119
pllRegistriesThatNeedSaving);
9121
writeLock.acquire();
9123
setMachineState(oldState);
9125
if (FAILED(rc)) return rc;
9128
setModified(IsModified_Storage);
9129
mMediaData.backup();
9131
// we cannot use erase (it) below because backup() above will create
9132
// a copy of the list and make this copy active, but the iterator
9133
// still refers to the original and is not valid for the copy
9134
mMediaData->mAttachments.remove(pAttach);
9136
if (!oldmedium.isNull())
9138
// if this is from a snapshot, do not defer detachment to commitMedia()
9140
oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
9141
// else if non-hard disk media, do not defer detachment to commitMedia() either
9142
else if (mediumType != DeviceType_HardDisk)
9143
oldmedium->removeBackReference(mData->mUuid);
9150
* Goes thru all medium attachments of the list and calls detachDevice() on each
9151
* of them and attaches all Medium objects found in the process to the given list,
9152
* depending on cleanupMode.
9154
* This gets called from Machine::Unregister, both for the actual Machine and
9155
* the SnapshotMachine objects that might be found in the snapshots.
9157
* Requires caller and locking.
9159
* @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
9160
* @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
9161
* @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
9162
* otherwise no media get added.
9163
* @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
9166
HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
9167
Snapshot *pSnapshot,
9168
CleanupMode_T cleanupMode,
9171
Assert(isWriteLockOnCurrentThread());
9175
// make a temporary list because detachDevice invalidates iterators into
9176
// mMediaData->mAttachments
9177
MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
9179
for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
9180
it != llAttachments2.end();
9183
ComObjPtr<MediumAttachment> pAttach = *it;
9184
ComObjPtr<Medium> pMedium = pAttach->getMedium();
9186
if (!pMedium.isNull())
9188
DeviceType_T devType = pMedium->getDeviceType();
9189
if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
9190
&& devType == DeviceType_HardDisk)
9191
|| (cleanupMode == CleanupMode_Full)
9193
llMedia.push_back(pMedium);
9196
// real machine: then we need to use the proper method
9197
rc = detachDevice(pAttach,
9200
NULL /* pfNeedsSaveSettings */);
9210
* Perform deferred hard disk detachments.
9212
* Does nothing if the hard disk attachment data (mMediaData) is not changed (not
9215
* If @a aOnline is @c true then this method will also unlock the old hard disks
9216
* for which the new implicit diffs were created and will lock these new diffs for
9219
* @param aOnline Whether the VM was online prior to this operation.
9221
* @note Locks this object for writing!
9223
void Machine::commitMedia(bool aOnline /*= false*/)
9225
AutoCaller autoCaller(this);
9226
AssertComRCReturnVoid(autoCaller.rc());
9228
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9230
LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
9234
/* no attach/detach operations -- nothing to do */
9235
if (!mMediaData.isBackedUp())
9238
MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
9239
bool fMediaNeedsLocking = false;
9241
/* enumerate new attachments */
9242
for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
9243
it != mMediaData->mAttachments.end();
9246
MediumAttachment *pAttach = *it;
9250
Medium* pMedium = pAttach->getMedium();
9251
bool fImplicit = pAttach->isImplicit();
9253
LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
9254
(pMedium) ? pMedium->getName().c_str() : "NULL",
9257
/** @todo convert all this Machine-based voodoo to MediumAttachment
9258
* based commit logic. */
9261
/* convert implicit attachment to normal */
9262
pAttach->setImplicit(false);
9266
&& pAttach->getType() == DeviceType_HardDisk
9269
ComObjPtr<Medium> parent = pMedium->getParent();
9270
AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
9272
/* update the appropriate lock list */
9273
MediumLockList *pMediumLockList;
9274
rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
9276
if (pMediumLockList)
9278
/* unlock if there's a need to change the locking */
9279
if (!fMediaNeedsLocking)
9281
rc = mData->mSession.mLockedMedia.Unlock();
9283
fMediaNeedsLocking = true;
9285
rc = pMediumLockList->Update(parent, false);
9287
rc = pMediumLockList->Append(pMedium, true);
9297
/* was this medium attached before? */
9298
for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
9299
oldIt != oldAtts.end();
9302
MediumAttachment *pOldAttach = *oldIt;
9303
if (pOldAttach->getMedium() == pMedium)
9305
LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
9307
/* yes: remove from old to avoid de-association */
9308
oldAtts.erase(oldIt);
9315
/* enumerate remaining old attachments and de-associate from the
9316
* current machine state */
9317
for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
9318
it != oldAtts.end();
9321
MediumAttachment *pAttach = *it;
9322
Medium* pMedium = pAttach->getMedium();
9324
/* Detach only hard disks, since DVD/floppy media is detached
9325
* instantly in MountMedium. */
9326
if (pAttach->getType() == DeviceType_HardDisk && pMedium)
9328
LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
9330
/* now de-associate from the current machine state */
9331
rc = pMedium->removeBackReference(mData->mUuid);
9336
/* unlock since medium is not used anymore */
9337
MediumLockList *pMediumLockList;
9338
rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
9340
if (pMediumLockList)
9342
rc = mData->mSession.mLockedMedia.Remove(pAttach);
9349
/* take media locks again so that the locking state is consistent */
9350
if (fMediaNeedsLocking)
9353
rc = mData->mSession.mLockedMedia.Lock();
9357
/* commit the hard disk changes */
9358
mMediaData.commit();
9360
if (isSessionMachine())
9362
/* attach new data to the primary machine and reshare it */
9363
mPeer->mMediaData.attach(mMediaData);
9370
* Perform deferred deletion of implicitly created diffs.
9372
* Does nothing if the hard disk attachment data (mMediaData) is not changed (not
9375
* @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
9376
* by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
9378
* @note Locks this object for writing!
9380
void Machine::rollbackMedia()
9382
AutoCaller autoCaller(this);
9383
AssertComRCReturnVoid (autoCaller.rc());
9385
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9387
LogFlowThisFunc(("Entering\n"));
9391
/* no attach/detach operations -- nothing to do */
9392
if (!mMediaData.isBackedUp())
9395
/* enumerate new attachments */
9396
for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
9397
it != mMediaData->mAttachments.end();
9400
MediumAttachment *pAttach = *it;
9401
/* Fix up the backrefs for DVD/floppy media. */
9402
if (pAttach->getType() != DeviceType_HardDisk)
9404
Medium* pMedium = pAttach->getMedium();
9407
rc = pMedium->removeBackReference(mData->mUuid);
9415
/* Fix up the backrefs for DVD/floppy media. */
9416
if (pAttach->getType() != DeviceType_HardDisk)
9418
Medium* pMedium = pAttach->getMedium();
9421
rc = pMedium->addBackReference(mData->mUuid);
9427
/** @todo convert all this Machine-based voodoo to MediumAttachment
9428
* based rollback logic. */
9429
// @todo r=dj the below totally fails if this gets called from Machine::rollback(),
9430
// which gets called if Machine::registeredInit() fails...
9431
deleteImplicitDiffs(NULL /*pfNeedsSaveSettings*/);
9437
* Returns true if the settings file is located in the directory named exactly
9438
* as the machine; this means, among other things, that the machine directory
9439
* should be auto-renamed.
9441
* @param aSettingsDir if not NULL, the full machine settings file directory
9442
* name will be assigned there.
9444
* @note Doesn't lock anything.
9445
* @note Not thread safe (must be called from this object's lock).
9447
bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
9449
Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
9450
strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
9452
*aSettingsDir = strMachineDirName;
9453
strMachineDirName.stripPath(); // vmname
9454
Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
9455
strConfigFileOnly.stripPath() // vmname.vbox
9456
.stripExt(); // vmname
9458
AssertReturn(!strMachineDirName.isEmpty(), false);
9459
AssertReturn(!strConfigFileOnly.isEmpty(), false);
9461
return strMachineDirName == strConfigFileOnly;
9465
* Discards all changes to machine settings.
9467
* @param aNotify Whether to notify the direct session about changes or not.
9469
* @note Locks objects for writing!
9471
void Machine::rollback(bool aNotify)
9473
AutoCaller autoCaller(this);
9474
AssertComRCReturn(autoCaller.rc(), (void)0);
9476
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9478
if (!mStorageControllers.isNull())
9480
if (mStorageControllers.isBackedUp())
9482
/* unitialize all new devices (absent in the backed up list). */
9483
StorageControllerList::const_iterator it = mStorageControllers->begin();
9484
StorageControllerList *backedList = mStorageControllers.backedUpData();
9485
while (it != mStorageControllers->end())
9487
if ( std::find(backedList->begin(), backedList->end(), *it)
9488
== backedList->end()
9496
/* restore the list */
9497
mStorageControllers.rollback();
9500
/* rollback any changes to devices after restoring the list */
9501
if (mData->flModifications & IsModified_Storage)
9503
StorageControllerList::const_iterator it = mStorageControllers->begin();
9504
while (it != mStorageControllers->end())
9512
mUserData.rollback();
9516
if (mData->flModifications & IsModified_Storage)
9520
mBIOSSettings->rollback();
9522
if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
9523
mVRDEServer->rollback();
9526
mAudioAdapter->rollback();
9528
if (mUSBController && (mData->flModifications & IsModified_USB))
9529
mUSBController->rollback();
9531
if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
9532
mBandwidthControl->rollback();
9534
ComPtr<INetworkAdapter> networkAdapters[RT_ELEMENTS(mNetworkAdapters)];
9535
ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
9536
ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
9538
if (mData->flModifications & IsModified_NetworkAdapters)
9539
for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
9540
if ( mNetworkAdapters[slot]
9541
&& mNetworkAdapters[slot]->isModified())
9543
mNetworkAdapters[slot]->rollback();
9544
networkAdapters[slot] = mNetworkAdapters[slot];
9547
if (mData->flModifications & IsModified_SerialPorts)
9548
for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
9549
if ( mSerialPorts[slot]
9550
&& mSerialPorts[slot]->isModified())
9552
mSerialPorts[slot]->rollback();
9553
serialPorts[slot] = mSerialPorts[slot];
9556
if (mData->flModifications & IsModified_ParallelPorts)
9557
for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
9558
if ( mParallelPorts[slot]
9559
&& mParallelPorts[slot]->isModified())
9561
mParallelPorts[slot]->rollback();
9562
parallelPorts[slot] = mParallelPorts[slot];
9567
/* inform the direct session about changes */
9569
ComObjPtr<Machine> that = this;
9570
uint32_t flModifications = mData->flModifications;
9573
if (flModifications & IsModified_SharedFolders)
9574
that->onSharedFolderChange();
9576
if (flModifications & IsModified_VRDEServer)
9577
that->onVRDEServerChange(/* aRestart */ TRUE);
9578
if (flModifications & IsModified_USB)
9579
that->onUSBControllerChange();
9581
for (ULONG slot = 0; slot < RT_ELEMENTS(networkAdapters); slot ++)
9582
if (networkAdapters[slot])
9583
that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
9584
for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot ++)
9585
if (serialPorts[slot])
9586
that->onSerialPortChange(serialPorts[slot]);
9587
for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot ++)
9588
if (parallelPorts[slot])
9589
that->onParallelPortChange(parallelPorts[slot]);
9591
if (flModifications & IsModified_Storage)
9592
that->onStorageControllerChange();
9595
if (flModifications & IsModified_BandwidthControl)
9596
that->onBandwidthControlChange();
9602
* Commits all the changes to machine settings.
9604
* Note that this operation is supposed to never fail.
9606
* @note Locks this object and children for writing.
9608
void Machine::commit()
9610
AutoCaller autoCaller(this);
9611
AssertComRCReturnVoid(autoCaller.rc());
9613
AutoCaller peerCaller(mPeer);
9614
AssertComRCReturnVoid(peerCaller.rc());
9616
AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
9619
* use safe commit to ensure Snapshot machines (that share mUserData)
9620
* will still refer to a valid memory location
9622
mUserData.commitCopy();
9626
if (mMediaData.isBackedUp())
9629
mBIOSSettings->commit();
9630
mVRDEServer->commit();
9631
mAudioAdapter->commit();
9632
mUSBController->commit();
9633
mBandwidthControl->commit();
9635
for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
9636
mNetworkAdapters[slot]->commit();
9637
for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
9638
mSerialPorts[slot]->commit();
9639
for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
9640
mParallelPorts[slot]->commit();
9642
bool commitStorageControllers = false;
9644
if (mStorageControllers.isBackedUp())
9646
mStorageControllers.commit();
9650
AutoWriteLock peerlock(mPeer COMMA_LOCKVAL_SRC_POS);
9652
/* Commit all changes to new controllers (this will reshare data with
9653
* peers for those who have peers) */
9654
StorageControllerList *newList = new StorageControllerList();
9655
StorageControllerList::const_iterator it = mStorageControllers->begin();
9656
while (it != mStorageControllers->end())
9660
/* look if this controller has a peer device */
9661
ComObjPtr<StorageController> peer = (*it)->getPeer();
9664
/* no peer means the device is a newly created one;
9665
* create a peer owning data this device share it with */
9666
peer.createObject();
9667
peer->init(mPeer, *it, true /* aReshare */);
9671
/* remove peer from the old list */
9672
mPeer->mStorageControllers->remove(peer);
9674
/* and add it to the new list */
9675
newList->push_back(peer);
9680
/* uninit old peer's controllers that are left */
9681
it = mPeer->mStorageControllers->begin();
9682
while (it != mPeer->mStorageControllers->end())
9688
/* attach new list of controllers to our peer */
9689
mPeer->mStorageControllers.attach(newList);
9693
/* we have no peer (our parent is the newly created machine);
9694
* just commit changes to devices */
9695
commitStorageControllers = true;
9700
/* the list of controllers itself is not changed,
9701
* just commit changes to controllers themselves */
9702
commitStorageControllers = true;
9705
if (commitStorageControllers)
9707
StorageControllerList::const_iterator it = mStorageControllers->begin();
9708
while (it != mStorageControllers->end())
9715
if (isSessionMachine())
9717
/* attach new data to the primary machine and reshare it */
9718
mPeer->mUserData.attach(mUserData);
9719
mPeer->mHWData.attach(mHWData);
9720
/* mMediaData is reshared by fixupMedia */
9721
// mPeer->mMediaData.attach(mMediaData);
9722
Assert(mPeer->mMediaData.data() == mMediaData.data());
9727
* Copies all the hardware data from the given machine.
9729
* Currently, only called when the VM is being restored from a snapshot. In
9730
* particular, this implies that the VM is not running during this method's
9733
* @note This method must be called from under this object's lock.
9735
* @note This method doesn't call #commit(), so all data remains backed up and
9738
void Machine::copyFrom(Machine *aThat)
9740
AssertReturnVoid(!isSnapshotMachine());
9741
AssertReturnVoid(aThat->isSnapshotMachine());
9743
AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
9745
mHWData.assignCopy(aThat->mHWData);
9747
// create copies of all shared folders (mHWData after attaching a copy
9748
// contains just references to original objects)
9749
for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
9750
it != mHWData->mSharedFolders.end();
9753
ComObjPtr<SharedFolder> folder;
9754
folder.createObject();
9755
HRESULT rc = folder->initCopy(getMachine(), *it);
9760
mBIOSSettings->copyFrom(aThat->mBIOSSettings);
9761
mVRDEServer->copyFrom(aThat->mVRDEServer);
9762
mAudioAdapter->copyFrom(aThat->mAudioAdapter);
9763
mUSBController->copyFrom(aThat->mUSBController);
9764
mBandwidthControl->copyFrom(aThat->mBandwidthControl);
9766
/* create private copies of all controllers */
9767
mStorageControllers.backup();
9768
mStorageControllers->clear();
9769
for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
9770
it != aThat->mStorageControllers->end();
9773
ComObjPtr<StorageController> ctrl;
9774
ctrl.createObject();
9775
ctrl->initCopy(this, *it);
9776
mStorageControllers->push_back(ctrl);
9779
for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
9780
mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
9781
for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
9782
mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
9783
for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
9784
mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
9787
#ifdef VBOX_WITH_RESOURCE_USAGE_API
9789
void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
9791
AssertReturnVoid(isWriteLockOnCurrentThread());
9792
AssertPtrReturnVoid(aCollector);
9794
pm::CollectorHAL *hal = aCollector->getHAL();
9795
/* Create sub metrics */
9796
pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
9797
"Percentage of processor time spent in user mode by the VM process.");
9798
pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
9799
"Percentage of processor time spent in kernel mode by the VM process.");
9800
pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
9801
"Size of resident portion of VM process in memory.");
9802
/* Create and register base metrics */
9803
pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
9804
cpuLoadUser, cpuLoadKernel);
9805
aCollector->registerBaseMetric(cpuLoad);
9806
pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
9808
aCollector->registerBaseMetric(ramUsage);
9810
aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
9811
aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
9812
new pm::AggregateAvg()));
9813
aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
9814
new pm::AggregateMin()));
9815
aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
9816
new pm::AggregateMax()));
9817
aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
9818
aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
9819
new pm::AggregateAvg()));
9820
aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
9821
new pm::AggregateMin()));
9822
aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
9823
new pm::AggregateMax()));
9825
aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
9826
aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
9827
new pm::AggregateAvg()));
9828
aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
9829
new pm::AggregateMin()));
9830
aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
9831
new pm::AggregateMax()));
9835
mGuestHAL = new pm::CollectorGuestHAL(this, hal);
9837
/* Create sub metrics */
9838
pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
9839
"Percentage of processor time spent in user mode as seen by the guest.");
9840
pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
9841
"Percentage of processor time spent in kernel mode as seen by the guest.");
9842
pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
9843
"Percentage of processor time spent idling as seen by the guest.");
9845
/* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
9846
pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
9847
pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
9848
pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
9849
pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
9850
pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
9852
pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
9854
/* Create and register base metrics */
9855
pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mGuestHAL, aMachine, guestLoadUser, guestLoadKernel, guestLoadIdle);
9856
aCollector->registerBaseMetric(guestCpuLoad);
9858
pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mGuestHAL, aMachine, guestMemTotal, guestMemFree, guestMemBalloon, guestMemShared,
9859
guestMemCache, guestPagedTotal);
9860
aCollector->registerBaseMetric(guestCpuMem);
9862
aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
9863
aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
9864
aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
9865
aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
9867
aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
9868
aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
9869
aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
9870
aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
9872
aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
9873
aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
9874
aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
9875
aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
9877
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
9878
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
9879
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
9880
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
9882
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
9883
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
9884
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
9885
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
9887
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
9888
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
9889
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
9890
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
9892
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
9893
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
9894
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
9895
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
9897
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
9898
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
9899
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
9900
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
9902
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
9903
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
9904
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
9905
aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
9908
void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
9910
AssertReturnVoid(isWriteLockOnCurrentThread());
9914
aCollector->unregisterMetricsFor(aMachine);
9915
aCollector->unregisterBaseMetricsFor(aMachine);
9925
#endif /* VBOX_WITH_RESOURCE_USAGE_API */
9928
////////////////////////////////////////////////////////////////////////////////
9930
DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
9932
HRESULT SessionMachine::FinalConstruct()
9934
LogFlowThisFunc(("\n"));
9936
#if defined(RT_OS_WINDOWS)
9938
#elif defined(RT_OS_OS2)
9939
mIPCSem = NULLHANDLE;
9940
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9949
void SessionMachine::FinalRelease()
9951
LogFlowThisFunc(("\n"));
9953
uninit(Uninit::Unexpected);
9957
* @note Must be called only by Machine::openSession() from its own write lock.
9959
HRESULT SessionMachine::init(Machine *aMachine)
9961
LogFlowThisFuncEnter();
9962
LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
9964
AssertReturn(aMachine, E_INVALIDARG);
9966
AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
9968
/* Enclose the state transition NotReady->InInit->Ready */
9969
AutoInitSpan autoInitSpan(this);
9970
AssertReturn(autoInitSpan.isOk(), E_FAIL);
9972
/* create the interprocess semaphore */
9973
#if defined(RT_OS_WINDOWS)
9974
mIPCSemName = aMachine->mData->m_strConfigFileFull;
9975
for (size_t i = 0; i < mIPCSemName.length(); i++)
9976
if (mIPCSemName.raw()[i] == '\\')
9977
mIPCSemName.raw()[i] = '/';
9978
mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
9979
ComAssertMsgRet(mIPCSem,
9980
("Cannot create IPC mutex '%ls', err=%d",
9981
mIPCSemName.raw(), ::GetLastError()),
9983
#elif defined(RT_OS_OS2)
9984
Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
9985
aMachine->mData->mUuid.raw());
9986
mIPCSemName = ipcSem;
9987
APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
9988
ComAssertMsgRet(arc == NO_ERROR,
9989
("Cannot create IPC mutex '%s', arc=%ld",
9990
ipcSem.c_str(), arc),
9992
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9993
# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
9994
# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
9995
/** @todo Check that this still works correctly. */
9996
AssertCompileSize(key_t, 8);
9998
AssertCompileSize(key_t, 4);
10003
for (uint32_t i = 0; i < 1 << 24; i++)
10005
key = ((uint32_t)'V' << 24) | i;
10006
int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
10007
if (sem >= 0 || (errno != EEXIST && errno != EACCES))
10011
mIPCKey = BstrFmt("%u", key);
10015
# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
10016
Utf8Str semName = aMachine->mData->m_strConfigFileFull;
10017
char *pszSemName = NULL;
10018
RTStrUtf8ToCurrentCP(&pszSemName, semName);
10019
key_t key = ::ftok(pszSemName, 'V');
10020
RTStrFree(pszSemName);
10022
mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
10023
# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
10025
int errnoSave = errno;
10026
if (mIPCSem < 0 && errnoSave == ENOSYS)
10029
tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
10030
"support for SysV IPC. Check the host kernel configuration for "
10031
"CONFIG_SYSVIPC=y"));
10034
/* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
10035
* the IPC semaphores */
10036
if (mIPCSem < 0 && errnoSave == ENOSPC)
10040
tr("Cannot create IPC semaphore because the system limit for the "
10041
"maximum number of semaphore sets (SEMMNI), or the system wide "
10042
"maximum number of semaphores (SEMMNS) would be exceeded. The "
10043
"current set of SysV IPC semaphores can be determined from "
10044
"the file /proc/sysvipc/sem"));
10047
tr("Cannot create IPC semaphore because the system-imposed limit "
10048
"on the maximum number of allowed semaphores or semaphore "
10049
"identifiers system-wide would be exceeded"));
10053
ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
10055
/* set the initial value to 1 */
10056
int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
10057
ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
10063
/* memorize the peer Machine */
10064
unconst(mPeer) = aMachine;
10065
/* share the parent pointer */
10066
unconst(mParent) = aMachine->mParent;
10068
/* take the pointers to data to share */
10069
mData.share(aMachine->mData);
10070
mSSData.share(aMachine->mSSData);
10072
mUserData.share(aMachine->mUserData);
10073
mHWData.share(aMachine->mHWData);
10074
mMediaData.share(aMachine->mMediaData);
10076
mStorageControllers.allocate();
10077
for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
10078
it != aMachine->mStorageControllers->end();
10081
ComObjPtr<StorageController> ctl;
10082
ctl.createObject();
10083
ctl->init(this, *it);
10084
mStorageControllers->push_back(ctl);
10087
unconst(mBIOSSettings).createObject();
10088
mBIOSSettings->init(this, aMachine->mBIOSSettings);
10089
/* create another VRDEServer object that will be mutable */
10090
unconst(mVRDEServer).createObject();
10091
mVRDEServer->init(this, aMachine->mVRDEServer);
10092
/* create another audio adapter object that will be mutable */
10093
unconst(mAudioAdapter).createObject();
10094
mAudioAdapter->init(this, aMachine->mAudioAdapter);
10095
/* create a list of serial ports that will be mutable */
10096
for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
10098
unconst(mSerialPorts[slot]).createObject();
10099
mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
10101
/* create a list of parallel ports that will be mutable */
10102
for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
10104
unconst(mParallelPorts[slot]).createObject();
10105
mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
10107
/* create another USB controller object that will be mutable */
10108
unconst(mUSBController).createObject();
10109
mUSBController->init(this, aMachine->mUSBController);
10111
/* create a list of network adapters that will be mutable */
10112
for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
10114
unconst(mNetworkAdapters[slot]).createObject();
10115
mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
10118
/* create another bandwidth control object that will be mutable */
10119
unconst(mBandwidthControl).createObject();
10120
mBandwidthControl->init(this, aMachine->mBandwidthControl);
10122
/* default is to delete saved state on Saved -> PoweredOff transition */
10123
mRemoveSavedState = true;
10125
/* Confirm a successful initialization when it's the case */
10126
autoInitSpan.setSucceeded();
10128
LogFlowThisFuncLeave();
10133
* Uninitializes this session object. If the reason is other than
10134
* Uninit::Unexpected, then this method MUST be called from #checkForDeath().
10136
* @param aReason uninitialization reason
10138
* @note Locks mParent + this object for writing.
10140
void SessionMachine::uninit(Uninit::Reason aReason)
10142
LogFlowThisFuncEnter();
10143
LogFlowThisFunc(("reason=%d\n", aReason));
10146
* Strongly reference ourselves to prevent this object deletion after
10147
* mData->mSession.mMachine.setNull() below (which can release the last
10148
* reference and call the destructor). Important: this must be done before
10149
* accessing any members (and before AutoUninitSpan that does it as well).
10150
* This self reference will be released as the very last step on return.
10152
ComObjPtr<SessionMachine> selfRef = this;
10154
/* Enclose the state transition Ready->InUninit->NotReady */
10155
AutoUninitSpan autoUninitSpan(this);
10156
if (autoUninitSpan.uninitDone())
10158
LogFlowThisFunc(("Already uninitialized\n"));
10159
LogFlowThisFuncLeave();
10163
if (autoUninitSpan.initFailed())
10165
/* We've been called by init() because it's failed. It's not really
10166
* necessary (nor it's safe) to perform the regular uninit sequence
10167
* below, the following is enough.
10169
LogFlowThisFunc(("Initialization failed.\n"));
10170
#if defined(RT_OS_WINDOWS)
10172
::CloseHandle(mIPCSem);
10174
#elif defined(RT_OS_OS2)
10175
if (mIPCSem != NULLHANDLE)
10176
::DosCloseMutexSem(mIPCSem);
10177
mIPCSem = NULLHANDLE;
10178
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
10180
::semctl(mIPCSem, 0, IPC_RMID);
10182
# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
10184
# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
10188
uninitDataAndChildObjects();
10190
unconst(mParent) = NULL;
10191
unconst(mPeer) = NULL;
10192
LogFlowThisFuncLeave();
10196
MachineState_T lastState;
10198
AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
10199
lastState = mData->mMachineState;
10203
#ifdef VBOX_WITH_USB
10204
// release all captured USB devices, but do this before requesting the locks below
10205
if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
10207
/* Console::captureUSBDevices() is called in the VM process only after
10208
* setting the machine state to Starting or Restoring.
10209
* Console::detachAllUSBDevices() will be called upon successful
10210
* termination. So, we need to release USB devices only if there was
10211
* an abnormal termination of a running VM.
10213
* This is identical to SessionMachine::DetachAllUSBDevices except
10214
* for the aAbnormal argument. */
10215
HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
10219
USBProxyService *service = mParent->host()->usbProxyService();
10221
service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
10223
#endif /* VBOX_WITH_USB */
10225
// we need to lock this object in uninit() because the lock is shared
10226
// with mPeer (as well as data we modify below). mParent->addProcessToReap()
10227
// and others need mParent lock, and USB needs host lock.
10228
AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
10230
// Trigger async cleanup tasks, avoid doing things here which are not
10231
// vital to be done immediately and maybe need more locks. This calls
10232
// Machine::unregisterMetrics().
10233
mParent->onMachineUninit(mPeer);
10235
if (aReason == Uninit::Abnormal)
10237
LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
10238
Global::IsOnlineOrTransient(lastState)));
10240
/* reset the state to Aborted */
10241
if (mData->mMachineState != MachineState_Aborted)
10242
setMachineState(MachineState_Aborted);
10245
// any machine settings modified?
10246
if (mData->flModifications)
10248
LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
10249
rollback(false /* aNotify */);
10252
Assert(mConsoleTaskData.mStateFilePath.isEmpty() || !mConsoleTaskData.mSnapshot);
10253
if (!mConsoleTaskData.mStateFilePath.isEmpty())
10255
LogWarningThisFunc(("canceling failed save state request!\n"));
10256
endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
10258
else if (!mConsoleTaskData.mSnapshot.isNull())
10260
LogWarningThisFunc(("canceling untaken snapshot!\n"));
10262
/* delete all differencing hard disks created (this will also attach
10263
* their parents back by rolling back mMediaData) */
10265
/* delete the saved state file (it might have been already created) */
10266
if (mConsoleTaskData.mSnapshot->stateFilePath().length())
10267
RTFileDelete(mConsoleTaskData.mSnapshot->stateFilePath().c_str());
10269
mConsoleTaskData.mSnapshot->uninit();
10272
if (!mData->mSession.mType.isEmpty())
10274
/* mType is not null when this machine's process has been started by
10275
* Machine::launchVMProcess(), therefore it is our child. We
10276
* need to queue the PID to reap the process (and avoid zombies on
10278
Assert(mData->mSession.mPid != NIL_RTPROCESS);
10279
mParent->addProcessToReap(mData->mSession.mPid);
10282
mData->mSession.mPid = NIL_RTPROCESS;
10284
if (aReason == Uninit::Unexpected)
10286
/* Uninitialization didn't come from #checkForDeath(), so tell the
10287
* client watcher thread to update the set of machines that have open
10289
mParent->updateClientWatcher();
10292
/* uninitialize all remote controls */
10293
if (mData->mSession.mRemoteControls.size())
10295
LogFlowThisFunc(("Closing remote sessions (%d):\n",
10296
mData->mSession.mRemoteControls.size()));
10298
Data::Session::RemoteControlList::iterator it =
10299
mData->mSession.mRemoteControls.begin();
10300
while (it != mData->mSession.mRemoteControls.end())
10302
LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
10303
HRESULT rc = (*it)->Uninitialize();
10304
LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
10306
LogWarningThisFunc(("Forgot to close the remote session?\n"));
10309
mData->mSession.mRemoteControls.clear();
10313
* An expected uninitialization can come only from #checkForDeath().
10314
* Otherwise it means that something's gone really wrong (for example,
10315
* the Session implementation has released the VirtualBox reference
10316
* before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
10317
* etc). However, it's also possible, that the client releases the IPC
10318
* semaphore correctly (i.e. before it releases the VirtualBox reference),
10319
* but the VirtualBox release event comes first to the server process.
10320
* This case is practically possible, so we should not assert on an
10321
* unexpected uninit, just log a warning.
10324
if ((aReason == Uninit::Unexpected))
10325
LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
10327
if (aReason != Uninit::Normal)
10329
mData->mSession.mDirectControl.setNull();
10333
/* this must be null here (see #OnSessionEnd()) */
10334
Assert(mData->mSession.mDirectControl.isNull());
10335
Assert(mData->mSession.mState == SessionState_Unlocking);
10336
Assert(!mData->mSession.mProgress.isNull());
10338
if (mData->mSession.mProgress)
10340
if (aReason == Uninit::Normal)
10341
mData->mSession.mProgress->notifyComplete(S_OK);
10343
mData->mSession.mProgress->notifyComplete(E_FAIL,
10344
COM_IIDOF(ISession),
10345
getComponentName(),
10346
tr("The VM session was aborted"));
10347
mData->mSession.mProgress.setNull();
10350
/* remove the association between the peer machine and this session machine */
10351
Assert( (SessionMachine*)mData->mSession.mMachine == this
10352
|| aReason == Uninit::Unexpected);
10354
/* reset the rest of session data */
10355
mData->mSession.mMachine.setNull();
10356
mData->mSession.mState = SessionState_Unlocked;
10357
mData->mSession.mType.setNull();
10359
/* close the interprocess semaphore before leaving the exclusive lock */
10360
#if defined(RT_OS_WINDOWS)
10362
::CloseHandle(mIPCSem);
10364
#elif defined(RT_OS_OS2)
10365
if (mIPCSem != NULLHANDLE)
10366
::DosCloseMutexSem(mIPCSem);
10367
mIPCSem = NULLHANDLE;
10368
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
10370
::semctl(mIPCSem, 0, IPC_RMID);
10372
# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
10374
# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
10379
/* fire an event */
10380
mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
10382
uninitDataAndChildObjects();
10384
/* free the essential data structure last */
10387
#if 1 /** @todo Please review this change! (bird) */
10388
/* drop the exclusive lock before setting the below two to NULL */
10389
multilock.release();
10391
/* leave the exclusive lock before setting the below two to NULL */
10395
unconst(mParent) = NULL;
10396
unconst(mPeer) = NULL;
10398
LogFlowThisFuncLeave();
10401
// util::Lockable interface
10402
////////////////////////////////////////////////////////////////////////////////
10405
* Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
10406
* with the primary Machine instance (mPeer).
10408
RWLockHandle *SessionMachine::lockHandle() const
10410
AssertReturn(mPeer != NULL, NULL);
10411
return mPeer->lockHandle();
10414
// IInternalMachineControl methods
10415
////////////////////////////////////////////////////////////////////////////////
10418
* @note Locks this object for writing.
10420
STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
10422
AutoCaller autoCaller(this);
10423
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10425
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10427
mRemoveSavedState = aRemove;
10433
* @note Locks the same as #setMachineState() does.
10435
STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
10437
return setMachineState(aMachineState);
10441
* @note Locks this object for reading.
10443
STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
10445
AutoCaller autoCaller(this);
10446
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10448
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10450
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
10451
mIPCSemName.cloneTo(aId);
10453
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
10454
# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
10455
mIPCKey.cloneTo(aId);
10456
# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
10457
mData->m_strConfigFileFull.cloneTo(aId);
10458
# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
10466
* @note Locks this object for writing.
10468
STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
10470
LogFlowThisFunc(("aProgress=%p\n", aProgress));
10471
AutoCaller autoCaller(this);
10472
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10474
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10476
if (mData->mSession.mState != SessionState_Locked)
10477
return VBOX_E_INVALID_OBJECT_STATE;
10479
if (!mData->mSession.mProgress.isNull())
10480
mData->mSession.mProgress->setOtherProgressObject(aProgress);
10482
LogFlowThisFunc(("returns S_OK.\n"));
10487
* @note Locks this object for writing.
10489
STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
10491
AutoCaller autoCaller(this);
10492
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10494
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10496
if (mData->mSession.mState != SessionState_Locked)
10497
return VBOX_E_INVALID_OBJECT_STATE;
10499
/* Finalize the openRemoteSession progress object. */
10500
if (mData->mSession.mProgress)
10502
mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
10503
mData->mSession.mProgress.setNull();
10506
if (SUCCEEDED((HRESULT)iResult))
10508
#ifdef VBOX_WITH_RESOURCE_USAGE_API
10509
/* The VM has been powered up successfully, so it makes sense
10510
* now to offer the performance metrics for a running machine
10511
* object. Doing it earlier wouldn't be safe. */
10512
registerMetrics(mParent->performanceCollector(), mPeer,
10513
mData->mSession.mPid);
10514
#endif /* VBOX_WITH_RESOURCE_USAGE_API */
10521
* @note Locks this object for writing.
10523
STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
10525
LogFlowThisFuncEnter();
10527
CheckComArgOutPointerValid(aProgress);
10529
AutoCaller autoCaller(this);
10530
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10532
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10534
AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
10537
/* create a progress object to track operation completion */
10538
ComObjPtr<Progress> pProgress;
10539
pProgress.createObject();
10540
pProgress->init(getVirtualBox(),
10541
static_cast<IMachine *>(this) /* aInitiator */,
10542
Bstr(tr("Stopping the virtual machine")).raw(),
10543
FALSE /* aCancelable */);
10545
/* fill in the console task data */
10546
mConsoleTaskData.mLastState = mData->mMachineState;
10547
mConsoleTaskData.mProgress = pProgress;
10549
/* set the state to Stopping (this is expected by Console::PowerDown()) */
10550
setMachineState(MachineState_Stopping);
10552
pProgress.queryInterfaceTo(aProgress);
10558
* @note Locks this object for writing.
10560
STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
10562
LogFlowThisFuncEnter();
10564
AutoCaller autoCaller(this);
10565
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10567
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10569
AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
10570
|| (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
10571
&& mConsoleTaskData.mLastState != MachineState_Null,
10575
* On failure, set the state to the state we had when BeginPoweringDown()
10576
* was called (this is expected by Console::PowerDown() and the associated
10577
* task). On success the VM process already changed the state to
10578
* MachineState_PoweredOff, so no need to do anything.
10580
if (FAILED(iResult))
10581
setMachineState(mConsoleTaskData.mLastState);
10583
/* notify the progress object about operation completion */
10584
Assert(mConsoleTaskData.mProgress);
10585
if (SUCCEEDED(iResult))
10586
mConsoleTaskData.mProgress->notifyComplete(S_OK);
10589
Utf8Str strErrMsg(aErrMsg);
10590
if (strErrMsg.length())
10591
mConsoleTaskData.mProgress->notifyComplete(iResult,
10592
COM_IIDOF(ISession),
10593
getComponentName(),
10594
strErrMsg.c_str());
10596
mConsoleTaskData.mProgress->notifyComplete(iResult);
10599
/* clear out the temporary saved state data */
10600
mConsoleTaskData.mLastState = MachineState_Null;
10601
mConsoleTaskData.mProgress.setNull();
10603
LogFlowThisFuncLeave();
10609
* Goes through the USB filters of the given machine to see if the given
10610
* device matches any filter or not.
10612
* @note Locks the same as USBController::hasMatchingFilter() does.
10614
STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
10618
LogFlowThisFunc(("\n"));
10620
CheckComArgNotNull(aUSBDevice);
10621
CheckComArgOutPointerValid(aMatched);
10623
AutoCaller autoCaller(this);
10624
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10626
#ifdef VBOX_WITH_USB
10627
*aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
10638
* @note Locks the same as Host::captureUSBDevice() does.
10640
STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
10642
LogFlowThisFunc(("\n"));
10644
AutoCaller autoCaller(this);
10645
AssertComRCReturnRC(autoCaller.rc());
10647
#ifdef VBOX_WITH_USB
10648
/* if captureDeviceForVM() fails, it must have set extended error info */
10649
MultiResult rc = mParent->host()->checkUSBProxyService();
10650
if (FAILED(rc)) return rc;
10652
USBProxyService *service = mParent->host()->usbProxyService();
10653
AssertReturn(service, E_FAIL);
10654
return service->captureDeviceForVM(this, Guid(aId).ref());
10662
* @note Locks the same as Host::detachUSBDevice() does.
10664
STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
10666
LogFlowThisFunc(("\n"));
10668
AutoCaller autoCaller(this);
10669
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10671
#ifdef VBOX_WITH_USB
10672
USBProxyService *service = mParent->host()->usbProxyService();
10673
AssertReturn(service, E_FAIL);
10674
return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
10683
* Inserts all machine filters to the USB proxy service and then calls
10684
* Host::autoCaptureUSBDevices().
10686
* Called by Console from the VM process upon VM startup.
10688
* @note Locks what called methods lock.
10690
STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
10692
LogFlowThisFunc(("\n"));
10694
AutoCaller autoCaller(this);
10695
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10697
#ifdef VBOX_WITH_USB
10698
HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
10702
USBProxyService *service = mParent->host()->usbProxyService();
10703
AssertReturn(service, E_FAIL);
10704
return service->autoCaptureDevicesForVM(this);
10711
* Removes all machine filters from the USB proxy service and then calls
10712
* Host::detachAllUSBDevices().
10714
* Called by Console from the VM process upon normal VM termination or by
10715
* SessionMachine::uninit() upon abnormal VM termination (from under the
10716
* Machine/SessionMachine lock).
10718
* @note Locks what called methods lock.
10720
STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
10722
LogFlowThisFunc(("\n"));
10724
AutoCaller autoCaller(this);
10725
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10727
#ifdef VBOX_WITH_USB
10728
HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
10732
USBProxyService *service = mParent->host()->usbProxyService();
10733
AssertReturn(service, E_FAIL);
10734
return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
10742
* @note Locks this object for writing.
10744
STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
10745
IProgress **aProgress)
10747
LogFlowThisFuncEnter();
10749
AssertReturn(aSession, E_INVALIDARG);
10750
AssertReturn(aProgress, E_INVALIDARG);
10752
AutoCaller autoCaller(this);
10754
LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
10756
* We don't assert below because it might happen that a non-direct session
10757
* informs us it is closed right after we've been uninitialized -- it's ok.
10759
if (FAILED(autoCaller.rc())) return autoCaller.rc();
10761
/* get IInternalSessionControl interface */
10762
ComPtr<IInternalSessionControl> control(aSession);
10764
ComAssertRet(!control.isNull(), E_INVALIDARG);
10766
/* Creating a Progress object requires the VirtualBox lock, and
10767
* thus locking it here is required by the lock order rules. */
10768
AutoMultiWriteLock2 alock(mParent->lockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS);
10770
if (control == mData->mSession.mDirectControl)
10772
ComAssertRet(aProgress, E_POINTER);
10774
/* The direct session is being normally closed by the client process
10775
* ----------------------------------------------------------------- */
10777
/* go to the closing state (essential for all open*Session() calls and
10778
* for #checkForDeath()) */
10779
Assert(mData->mSession.mState == SessionState_Locked);
10780
mData->mSession.mState = SessionState_Unlocking;
10782
/* set direct control to NULL to release the remote instance */
10783
mData->mSession.mDirectControl.setNull();
10784
LogFlowThisFunc(("Direct control is set to NULL\n"));
10786
if (mData->mSession.mProgress)
10788
/* finalize the progress, someone might wait if a frontend
10789
* closes the session before powering on the VM. */
10790
mData->mSession.mProgress->notifyComplete(E_FAIL,
10791
COM_IIDOF(ISession),
10792
getComponentName(),
10793
tr("The VM session was closed before any attempt to power it on"));
10794
mData->mSession.mProgress.setNull();
10797
/* Create the progress object the client will use to wait until
10798
* #checkForDeath() is called to uninitialize this session object after
10799
* it releases the IPC semaphore.
10800
* Note! Because we're "reusing" mProgress here, this must be a proxy
10801
* object just like for openRemoteSession. */
10802
Assert(mData->mSession.mProgress.isNull());
10803
ComObjPtr<ProgressProxy> progress;
10804
progress.createObject();
10805
ComPtr<IUnknown> pPeer(mPeer);
10806
progress->init(mParent, pPeer,
10807
Bstr(tr("Closing session")).raw(),
10808
FALSE /* aCancelable */);
10809
progress.queryInterfaceTo(aProgress);
10810
mData->mSession.mProgress = progress;
10814
/* the remote session is being normally closed */
10815
Data::Session::RemoteControlList::iterator it =
10816
mData->mSession.mRemoteControls.begin();
10817
while (it != mData->mSession.mRemoteControls.end())
10819
if (control == *it)
10823
BOOL found = it != mData->mSession.mRemoteControls.end();
10824
ComAssertMsgRet(found, ("The session is not found in the session list!"),
10826
mData->mSession.mRemoteControls.remove(*it);
10829
LogFlowThisFuncLeave();
10834
* @note Locks this object for writing.
10836
STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
10838
LogFlowThisFuncEnter();
10840
CheckComArgOutPointerValid(aProgress);
10841
CheckComArgOutPointerValid(aStateFilePath);
10843
AutoCaller autoCaller(this);
10844
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10846
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10848
AssertReturn( mData->mMachineState == MachineState_Paused
10849
&& mConsoleTaskData.mLastState == MachineState_Null
10850
&& mConsoleTaskData.mStateFilePath.isEmpty(),
10853
/* create a progress object to track operation completion */
10854
ComObjPtr<Progress> pProgress;
10855
pProgress.createObject();
10856
pProgress->init(getVirtualBox(),
10857
static_cast<IMachine *>(this) /* aInitiator */,
10858
Bstr(tr("Saving the execution state of the virtual machine")).raw(),
10859
FALSE /* aCancelable */);
10861
Bstr stateFilePath;
10862
/* stateFilePath is null when the machine is not running */
10863
if (mData->mMachineState == MachineState_Paused)
10865
Utf8Str strFullSnapshotFolder;
10866
calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10867
stateFilePath = Utf8StrFmt("%s%c{%RTuuid}.sav",
10868
strFullSnapshotFolder.c_str(),
10870
mData->mUuid.raw());
10873
/* fill in the console task data */
10874
mConsoleTaskData.mLastState = mData->mMachineState;
10875
mConsoleTaskData.mStateFilePath = stateFilePath;
10876
mConsoleTaskData.mProgress = pProgress;
10878
/* set the state to Saving (this is expected by Console::SaveState()) */
10879
setMachineState(MachineState_Saving);
10881
stateFilePath.cloneTo(aStateFilePath);
10882
pProgress.queryInterfaceTo(aProgress);
10888
* @note Locks mParent + this object for writing.
10890
STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
10892
LogFlowThisFunc(("\n"));
10894
AutoCaller autoCaller(this);
10895
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10897
/* endSavingState() need mParent lock */
10898
AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
10900
AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
10901
|| (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
10902
&& mConsoleTaskData.mLastState != MachineState_Null
10903
&& !mConsoleTaskData.mStateFilePath.isEmpty(),
10907
* On failure, set the state to the state we had when BeginSavingState()
10908
* was called (this is expected by Console::SaveState() and the associated
10909
* task). On success the VM process already changed the state to
10910
* MachineState_Saved, so no need to do anything.
10912
if (FAILED(iResult))
10913
setMachineState(mConsoleTaskData.mLastState);
10915
return endSavingState(iResult, aErrMsg);
10919
* @note Locks this object for writing.
10921
STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
10923
LogFlowThisFunc(("\n"));
10925
CheckComArgStrNotEmptyOrNull(aSavedStateFile);
10927
AutoCaller autoCaller(this);
10928
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10930
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10932
AssertReturn( mData->mMachineState == MachineState_PoweredOff
10933
|| mData->mMachineState == MachineState_Teleported
10934
|| mData->mMachineState == MachineState_Aborted
10935
, E_FAIL); /** @todo setError. */
10937
Utf8Str stateFilePathFull = aSavedStateFile;
10938
int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
10939
if (RT_FAILURE(vrc))
10940
return setError(VBOX_E_FILE_ERROR,
10941
tr("Invalid saved state file path '%ls' (%Rrc)"),
10945
mSSData->mStateFilePath = stateFilePathFull;
10947
/* The below setMachineState() will detect the state transition and will
10948
* update the settings file */
10950
return setMachineState(MachineState_Saved);
10953
STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
10954
ComSafeArrayOut(BSTR, aValues),
10955
ComSafeArrayOut(LONG64, aTimestamps),
10956
ComSafeArrayOut(BSTR, aFlags))
10958
LogFlowThisFunc(("\n"));
10960
#ifdef VBOX_WITH_GUEST_PROPS
10961
using namespace guestProp;
10963
AutoCaller autoCaller(this);
10964
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10966
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10968
AssertReturn(!ComSafeArrayOutIsNull(aNames), E_POINTER);
10969
AssertReturn(!ComSafeArrayOutIsNull(aValues), E_POINTER);
10970
AssertReturn(!ComSafeArrayOutIsNull(aTimestamps), E_POINTER);
10971
AssertReturn(!ComSafeArrayOutIsNull(aFlags), E_POINTER);
10973
size_t cEntries = mHWData->mGuestProperties.size();
10974
com::SafeArray<BSTR> names(cEntries);
10975
com::SafeArray<BSTR> values(cEntries);
10976
com::SafeArray<LONG64> timestamps(cEntries);
10977
com::SafeArray<BSTR> flags(cEntries);
10979
for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
10980
it != mHWData->mGuestProperties.end();
10983
char szFlags[MAX_FLAGS_LEN + 1];
10984
it->strName.cloneTo(&names[i]);
10985
it->strValue.cloneTo(&values[i]);
10986
timestamps[i] = it->mTimestamp;
10987
/* If it is NULL, keep it NULL. */
10990
writeFlags(it->mFlags, szFlags);
10991
Bstr(szFlags).cloneTo(&flags[i]);
10997
names.detachTo(ComSafeArrayOutArg(aNames));
10998
values.detachTo(ComSafeArrayOutArg(aValues));
10999
timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
11000
flags.detachTo(ComSafeArrayOutArg(aFlags));
11003
ReturnComNotImplemented();
11007
STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
11012
LogFlowThisFunc(("\n"));
11014
#ifdef VBOX_WITH_GUEST_PROPS
11015
using namespace guestProp;
11017
CheckComArgStrNotEmptyOrNull(aName);
11018
if (aValue != NULL && (!VALID_PTR(aValue) || !VALID_PTR(aFlags)))
11019
return E_POINTER; /* aValue can be NULL to indicate deletion */
11024
* Convert input up front.
11026
Utf8Str utf8Name(aName);
11027
uint32_t fFlags = NILFLAG;
11030
Utf8Str utf8Flags(aFlags);
11031
int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
11032
AssertRCReturn(vrc, E_INVALIDARG);
11036
* Now grab the object lock, validate the state and do the update.
11038
AutoCaller autoCaller(this);
11039
if (FAILED(autoCaller.rc())) return autoCaller.rc();
11041
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11043
switch (mData->mMachineState)
11045
case MachineState_Paused:
11046
case MachineState_Running:
11047
case MachineState_Teleporting:
11048
case MachineState_TeleportingPausedVM:
11049
case MachineState_LiveSnapshotting:
11050
case MachineState_DeletingSnapshotOnline:
11051
case MachineState_DeletingSnapshotPaused:
11052
case MachineState_Saving:
11056
#ifndef DEBUG_sunlover
11057
AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
11058
VBOX_E_INVALID_VM_STATE);
11060
return VBOX_E_INVALID_VM_STATE;
11064
setModified(IsModified_MachineData);
11067
/** @todo r=bird: The careful memory handling doesn't work out here because
11068
* the catch block won't undo any damage we've done. So, if push_back throws
11069
* bad_alloc then you've lost the value.
11071
* Another thing. Doing a linear search here isn't extremely efficient, esp.
11072
* since values that changes actually bubbles to the end of the list. Using
11073
* something that has an efficient lookup and can tolerate a bit of updates
11074
* would be nice. RTStrSpace is one suggestion (it's not perfect). Some
11075
* combination of RTStrCache (for sharing names and getting uniqueness into
11076
* the bargain) and hash/tree is another. */
11077
for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
11078
iter != mHWData->mGuestProperties.end();
11080
if (utf8Name == iter->strName)
11082
mHWData->mGuestProperties.erase(iter);
11083
mData->mGuestPropertiesModified = TRUE;
11086
if (aValue != NULL)
11088
HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
11089
mHWData->mGuestProperties.push_back(property);
11090
mData->mGuestPropertiesModified = TRUE;
11094
* Send a callback notification if appropriate
11096
if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
11097
|| RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
11105
mParent->onGuestPropertyChange(mData->mUuid,
11113
return VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
11117
ReturnComNotImplemented();
11121
// public methods only for internal purposes
11122
/////////////////////////////////////////////////////////////////////////////
11125
* Called from the client watcher thread to check for expected or unexpected
11126
* death of the client process that has a direct session to this machine.
11128
* On Win32 and on OS/2, this method is called only when we've got the
11129
* mutex (i.e. the client has either died or terminated normally) so it always
11130
* returns @c true (the client is terminated, the session machine is
11133
* On other platforms, the method returns @c true if the client process has
11134
* terminated normally or abnormally and the session machine was uninitialized,
11135
* and @c false if the client process is still alive.
11137
* @note Locks this object for writing.
11139
bool SessionMachine::checkForDeath()
11141
Uninit::Reason reason;
11142
bool terminated = false;
11144
/* Enclose autoCaller with a block because calling uninit() from under it
11145
* will deadlock. */
11147
AutoCaller autoCaller(this);
11148
if (!autoCaller.isOk())
11150
/* return true if not ready, to cause the client watcher to exclude
11151
* the corresponding session from watching */
11152
LogFlowThisFunc(("Already uninitialized!\n"));
11156
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11158
/* Determine the reason of death: if the session state is Closing here,
11159
* everything is fine. Otherwise it means that the client did not call
11160
* OnSessionEnd() before it released the IPC semaphore. This may happen
11161
* either because the client process has abnormally terminated, or
11162
* because it simply forgot to call ISession::Close() before exiting. We
11163
* threat the latter also as an abnormal termination (see
11164
* Session::uninit() for details). */
11165
reason = mData->mSession.mState == SessionState_Unlocking ?
11169
#if defined(RT_OS_WINDOWS)
11171
AssertMsg(mIPCSem, ("semaphore must be created"));
11173
/* release the IPC mutex */
11174
::ReleaseMutex(mIPCSem);
11178
#elif defined(RT_OS_OS2)
11180
AssertMsg(mIPCSem, ("semaphore must be created"));
11182
/* release the IPC mutex */
11183
::DosReleaseMutexSem(mIPCSem);
11187
#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11189
AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
11191
int val = ::semctl(mIPCSem, 0, GETVAL);
11194
/* the semaphore is signaled, meaning the session is terminated */
11202
} /* AutoCaller block */
11211
* @note Locks this object for reading.
11213
HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
11215
LogFlowThisFunc(("\n"));
11217
AutoCaller autoCaller(this);
11218
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11220
ComPtr<IInternalSessionControl> directControl;
11222
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11223
directControl = mData->mSession.mDirectControl;
11226
/* ignore notifications sent after #OnSessionEnd() is called */
11227
if (!directControl)
11230
return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
11234
* @note Locks this object for reading.
11236
HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
11237
NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
11239
LogFlowThisFunc(("\n"));
11241
AutoCaller autoCaller(this);
11242
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11244
ComPtr<IInternalSessionControl> directControl;
11246
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11247
directControl = mData->mSession.mDirectControl;
11250
/* ignore notifications sent after #OnSessionEnd() is called */
11251
if (!directControl)
11254
* instead acting like callback we ask IVirtualBox deliver corresponding event
11257
mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, aHostPort, aGuestIp, aGuestPort);
11262
* @note Locks this object for reading.
11264
HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
11266
LogFlowThisFunc(("\n"));
11268
AutoCaller autoCaller(this);
11269
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11271
ComPtr<IInternalSessionControl> directControl;
11273
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11274
directControl = mData->mSession.mDirectControl;
11277
/* ignore notifications sent after #OnSessionEnd() is called */
11278
if (!directControl)
11281
return directControl->OnSerialPortChange(serialPort);
11285
* @note Locks this object for reading.
11287
HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
11289
LogFlowThisFunc(("\n"));
11291
AutoCaller autoCaller(this);
11292
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11294
ComPtr<IInternalSessionControl> directControl;
11296
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11297
directControl = mData->mSession.mDirectControl;
11300
/* ignore notifications sent after #OnSessionEnd() is called */
11301
if (!directControl)
11304
return directControl->OnParallelPortChange(parallelPort);
11308
* @note Locks this object for reading.
11310
HRESULT SessionMachine::onStorageControllerChange()
11312
LogFlowThisFunc(("\n"));
11314
AutoCaller autoCaller(this);
11315
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11317
ComPtr<IInternalSessionControl> directControl;
11319
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11320
directControl = mData->mSession.mDirectControl;
11323
/* ignore notifications sent after #OnSessionEnd() is called */
11324
if (!directControl)
11327
return directControl->OnStorageControllerChange();
11331
* @note Locks this object for reading.
11333
HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
11335
LogFlowThisFunc(("\n"));
11337
AutoCaller autoCaller(this);
11338
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11340
ComPtr<IInternalSessionControl> directControl;
11342
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11343
directControl = mData->mSession.mDirectControl;
11346
/* ignore notifications sent after #OnSessionEnd() is called */
11347
if (!directControl)
11350
return directControl->OnMediumChange(aAttachment, aForce);
11354
* @note Locks this object for reading.
11356
HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
11358
LogFlowThisFunc(("\n"));
11360
AutoCaller autoCaller(this);
11361
AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
11363
ComPtr<IInternalSessionControl> directControl;
11365
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11366
directControl = mData->mSession.mDirectControl;
11369
/* ignore notifications sent after #OnSessionEnd() is called */
11370
if (!directControl)
11373
return directControl->OnCPUChange(aCPU, aRemove);
11376
HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
11378
LogFlowThisFunc(("\n"));
11380
AutoCaller autoCaller(this);
11381
AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
11383
ComPtr<IInternalSessionControl> directControl;
11385
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11386
directControl = mData->mSession.mDirectControl;
11389
/* ignore notifications sent after #OnSessionEnd() is called */
11390
if (!directControl)
11393
return directControl->OnCPUExecutionCapChange(aExecutionCap);
11397
* @note Locks this object for reading.
11399
HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
11401
LogFlowThisFunc(("\n"));
11403
AutoCaller autoCaller(this);
11404
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11406
ComPtr<IInternalSessionControl> directControl;
11408
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11409
directControl = mData->mSession.mDirectControl;
11412
/* ignore notifications sent after #OnSessionEnd() is called */
11413
if (!directControl)
11416
return directControl->OnVRDEServerChange(aRestart);
11420
* @note Locks this object for reading.
11422
HRESULT SessionMachine::onUSBControllerChange()
11424
LogFlowThisFunc(("\n"));
11426
AutoCaller autoCaller(this);
11427
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11429
ComPtr<IInternalSessionControl> directControl;
11431
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11432
directControl = mData->mSession.mDirectControl;
11435
/* ignore notifications sent after #OnSessionEnd() is called */
11436
if (!directControl)
11439
return directControl->OnUSBControllerChange();
11443
* @note Locks this object for reading.
11445
HRESULT SessionMachine::onSharedFolderChange()
11447
LogFlowThisFunc(("\n"));
11449
AutoCaller autoCaller(this);
11450
AssertComRCReturnRC(autoCaller.rc());
11452
ComPtr<IInternalSessionControl> directControl;
11454
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11455
directControl = mData->mSession.mDirectControl;
11458
/* ignore notifications sent after #OnSessionEnd() is called */
11459
if (!directControl)
11462
return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
11466
* @note Locks this object for reading.
11468
HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
11470
LogFlowThisFunc(("\n"));
11472
AutoCaller autoCaller(this);
11473
AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
11475
ComPtr<IInternalSessionControl> directControl;
11477
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11478
directControl = mData->mSession.mDirectControl;
11481
/* ignore notifications sent after #OnSessionEnd() is called */
11482
if (!directControl)
11485
return directControl->OnBandwidthGroupChange(aBandwidthGroup);
11489
* Returns @c true if this machine's USB controller reports it has a matching
11490
* filter for the given USB device and @c false otherwise.
11492
* @note Caller must have requested machine read lock.
11494
bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
11496
AutoCaller autoCaller(this);
11497
/* silently return if not ready -- this method may be called after the
11498
* direct machine session has been called */
11499
if (!autoCaller.isOk())
11503
#ifdef VBOX_WITH_USB
11504
switch (mData->mMachineState)
11506
case MachineState_Starting:
11507
case MachineState_Restoring:
11508
case MachineState_TeleportingIn:
11509
case MachineState_Paused:
11510
case MachineState_Running:
11511
/** @todo Live Migration: snapshoting & teleporting. Need to fend things of
11513
return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
11524
* @note The calls shall hold no locks. Will temporarily lock this object for reading.
11526
HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
11527
IVirtualBoxErrorInfo *aError,
11530
LogFlowThisFunc(("\n"));
11532
AutoCaller autoCaller(this);
11534
/* This notification may happen after the machine object has been
11535
* uninitialized (the session was closed), so don't assert. */
11536
if (FAILED(autoCaller.rc())) return autoCaller.rc();
11538
ComPtr<IInternalSessionControl> directControl;
11540
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11541
directControl = mData->mSession.mDirectControl;
11544
/* fail on notifications sent after #OnSessionEnd() is called, it is
11545
* expected by the caller */
11546
if (!directControl)
11549
/* No locks should be held at this point. */
11550
AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
11551
AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
11553
return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
11557
* @note The calls shall hold no locks. Will temporarily lock this object for reading.
11559
HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
11560
IVirtualBoxErrorInfo *aError)
11562
LogFlowThisFunc(("\n"));
11564
AutoCaller autoCaller(this);
11566
/* This notification may happen after the machine object has been
11567
* uninitialized (the session was closed), so don't assert. */
11568
if (FAILED(autoCaller.rc())) return autoCaller.rc();
11570
ComPtr<IInternalSessionControl> directControl;
11572
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11573
directControl = mData->mSession.mDirectControl;
11576
/* fail on notifications sent after #OnSessionEnd() is called, it is
11577
* expected by the caller */
11578
if (!directControl)
11581
/* No locks should be held at this point. */
11582
AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
11583
AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
11585
return directControl->OnUSBDeviceDetach(aId, aError);
11588
// protected methods
11589
/////////////////////////////////////////////////////////////////////////////
11592
* Helper method to finalize saving the state.
11594
* @note Must be called from under this object's lock.
11596
* @param aRc S_OK if the snapshot has been taken successfully
11597
* @param aErrMsg human readable error message for failure
11599
* @note Locks mParent + this objects for writing.
11601
HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
11603
LogFlowThisFuncEnter();
11605
AutoCaller autoCaller(this);
11606
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11608
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11612
if (SUCCEEDED(aRc))
11614
mSSData->mStateFilePath = mConsoleTaskData.mStateFilePath;
11616
/* save all VM settings */
11617
rc = saveSettings(NULL);
11618
// no need to check whether VirtualBox.xml needs saving also since
11619
// we can't have a name change pending at this point
11623
/* delete the saved state file (it might have been already created) */
11624
RTFileDelete(mConsoleTaskData.mStateFilePath.c_str());
11627
/* notify the progress object about operation completion */
11628
Assert(mConsoleTaskData.mProgress);
11629
if (SUCCEEDED(aRc))
11630
mConsoleTaskData.mProgress->notifyComplete(S_OK);
11633
if (aErrMsg.length())
11634
mConsoleTaskData.mProgress->notifyComplete(aRc,
11635
COM_IIDOF(ISession),
11636
getComponentName(),
11639
mConsoleTaskData.mProgress->notifyComplete(aRc);
11642
/* clear out the temporary saved state data */
11643
mConsoleTaskData.mLastState = MachineState_Null;
11644
mConsoleTaskData.mStateFilePath.setNull();
11645
mConsoleTaskData.mProgress.setNull();
11647
LogFlowThisFuncLeave();
11652
* Locks the attached media.
11654
* All attached hard disks are locked for writing and DVD/floppy are locked for
11655
* reading. Parents of attached hard disks (if any) are locked for reading.
11657
* This method also performs accessibility check of all media it locks: if some
11658
* media is inaccessible, the method will return a failure and a bunch of
11659
* extended error info objects per each inaccessible medium.
11661
* Note that this method is atomic: if it returns a success, all media are
11662
* locked as described above; on failure no media is locked at all (all
11663
* succeeded individual locks will be undone).
11665
* This method is intended to be called when the machine is in Starting or
11666
* Restoring state and asserts otherwise.
11668
* The locks made by this method must be undone by calling #unlockMedia() when
11671
HRESULT SessionMachine::lockMedia()
11673
AutoCaller autoCaller(this);
11674
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11676
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11678
AssertReturn( mData->mMachineState == MachineState_Starting
11679
|| mData->mMachineState == MachineState_Restoring
11680
|| mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
11681
/* bail out if trying to lock things with already set up locking */
11682
AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
11684
MultiResult mrc(S_OK);
11686
/* Collect locking information for all medium objects attached to the VM. */
11687
for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11688
it != mMediaData->mAttachments.end();
11691
MediumAttachment* pAtt = *it;
11692
DeviceType_T devType = pAtt->getType();
11693
Medium *pMedium = pAtt->getMedium();
11695
MediumLockList *pMediumLockList(new MediumLockList());
11696
// There can be attachments without a medium (floppy/dvd), and thus
11697
// it's impossible to create a medium lock list. It still makes sense
11698
// to have the empty medium lock list in the map in case a medium is
11700
if (pMedium != NULL)
11702
MediumType_T mediumType = pMedium->getType();
11703
bool fIsReadOnlyLock = mediumType == MediumType_Readonly
11704
|| mediumType == MediumType_Shareable;
11705
bool fIsVitalImage = (devType == DeviceType_HardDisk);
11707
mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
11708
!fIsReadOnlyLock /* fMediumLockWrite */,
11713
delete pMediumLockList;
11714
mData->mSession.mLockedMedia.Clear();
11719
HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
11722
mData->mSession.mLockedMedia.Clear();
11724
tr("Collecting locking information for all attached media failed"));
11729
if (SUCCEEDED(mrc))
11731
/* Now lock all media. If this fails, nothing is locked. */
11732
HRESULT rc = mData->mSession.mLockedMedia.Lock();
11736
tr("Locking of attached media failed"));
11744
* Undoes the locks made by by #lockMedia().
11746
void SessionMachine::unlockMedia()
11748
AutoCaller autoCaller(this);
11749
AssertComRCReturnVoid(autoCaller.rc());
11751
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11753
/* we may be holding important error info on the current thread;
11755
ErrorInfoKeeper eik;
11757
HRESULT rc = mData->mSession.mLockedMedia.Clear();
11762
* Helper to change the machine state (reimplementation).
11764
* @note Locks this object for writing.
11766
HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
11768
LogFlowThisFuncEnter();
11769
LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
11771
AutoCaller autoCaller(this);
11772
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11774
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11776
MachineState_T oldMachineState = mData->mMachineState;
11778
AssertMsgReturn(oldMachineState != aMachineState,
11779
("oldMachineState=%s, aMachineState=%s\n",
11780
Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
11786
bool deleteSavedState = false;
11788
/* detect some state transitions */
11790
if ( ( oldMachineState == MachineState_Saved
11791
&& aMachineState == MachineState_Restoring)
11792
|| ( ( oldMachineState == MachineState_PoweredOff
11793
|| oldMachineState == MachineState_Teleported
11794
|| oldMachineState == MachineState_Aborted
11796
&& ( aMachineState == MachineState_TeleportingIn
11797
|| aMachineState == MachineState_Starting
11802
/* The EMT thread is about to start */
11804
/* Nothing to do here for now... */
11806
/// @todo NEWMEDIA don't let mDVDDrive and other children
11807
/// change anything when in the Starting/Restoring state
11809
else if ( ( oldMachineState == MachineState_Running
11810
|| oldMachineState == MachineState_Paused
11811
|| oldMachineState == MachineState_Teleporting
11812
|| oldMachineState == MachineState_LiveSnapshotting
11813
|| oldMachineState == MachineState_Stuck
11814
|| oldMachineState == MachineState_Starting
11815
|| oldMachineState == MachineState_Stopping
11816
|| oldMachineState == MachineState_Saving
11817
|| oldMachineState == MachineState_Restoring
11818
|| oldMachineState == MachineState_TeleportingPausedVM
11819
|| oldMachineState == MachineState_TeleportingIn
11821
&& ( aMachineState == MachineState_PoweredOff
11822
|| aMachineState == MachineState_Saved
11823
|| aMachineState == MachineState_Teleported
11824
|| aMachineState == MachineState_Aborted
11826
/* ignore PoweredOff->Saving->PoweredOff transition when taking a
11828
&& ( mConsoleTaskData.mSnapshot.isNull()
11829
|| mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
11833
/* The EMT thread has just stopped, unlock attached media. Note that as
11834
* opposed to locking that is done from Console, we do unlocking here
11835
* because the VM process may have aborted before having a chance to
11836
* properly unlock all media it locked. */
11841
if (oldMachineState == MachineState_Restoring)
11843
if (aMachineState != MachineState_Saved)
11846
* delete the saved state file once the machine has finished
11847
* restoring from it (note that Console sets the state from
11848
* Restoring to Saved if the VM couldn't restore successfully,
11849
* to give the user an ability to fix an error and retry --
11850
* we keep the saved state file in this case)
11852
deleteSavedState = true;
11855
else if ( oldMachineState == MachineState_Saved
11856
&& ( aMachineState == MachineState_PoweredOff
11857
|| aMachineState == MachineState_Aborted
11858
|| aMachineState == MachineState_Teleported
11863
* delete the saved state after Console::ForgetSavedState() is called
11864
* or if the VM process (owning a direct VM session) crashed while the
11869
// Not sure that deleting the saved state file just because of the
11870
// client death before it attempted to restore the VM is a good
11871
// thing. But when it crashes we need to go to the Aborted state
11872
// which cannot have the saved state file associated... The only
11873
// way to fix this is to make the Aborted condition not a VM state
11874
// but a bool flag: i.e., when a crash occurs, set it to true and
11875
// change the state to PoweredOff or Saved depending on the
11876
// saved state presence.
11878
deleteSavedState = true;
11879
mData->mCurrentStateModified = TRUE;
11880
stsFlags |= SaveSTS_CurStateModified;
11883
if ( aMachineState == MachineState_Starting
11884
|| aMachineState == MachineState_Restoring
11885
|| aMachineState == MachineState_TeleportingIn
11888
/* set the current state modified flag to indicate that the current
11889
* state is no more identical to the state in the
11890
* current snapshot */
11891
if (!mData->mCurrentSnapshot.isNull())
11893
mData->mCurrentStateModified = TRUE;
11894
stsFlags |= SaveSTS_CurStateModified;
11898
if (deleteSavedState)
11900
if (mRemoveSavedState)
11902
Assert(!mSSData->mStateFilePath.isEmpty());
11903
RTFileDelete(mSSData->mStateFilePath.c_str());
11905
mSSData->mStateFilePath.setNull();
11906
stsFlags |= SaveSTS_StateFilePath;
11909
/* redirect to the underlying peer machine */
11910
mPeer->setMachineState(aMachineState);
11912
if ( aMachineState == MachineState_PoweredOff
11913
|| aMachineState == MachineState_Teleported
11914
|| aMachineState == MachineState_Aborted
11915
|| aMachineState == MachineState_Saved)
11917
/* the machine has stopped execution
11918
* (or the saved state file was adopted) */
11919
stsFlags |= SaveSTS_StateTimeStamp;
11922
if ( ( oldMachineState == MachineState_PoweredOff
11923
|| oldMachineState == MachineState_Aborted
11924
|| oldMachineState == MachineState_Teleported
11926
&& aMachineState == MachineState_Saved)
11928
/* the saved state file was adopted */
11929
Assert(!mSSData->mStateFilePath.isEmpty());
11930
stsFlags |= SaveSTS_StateFilePath;
11933
#ifdef VBOX_WITH_GUEST_PROPS
11934
if ( aMachineState == MachineState_PoweredOff
11935
|| aMachineState == MachineState_Aborted
11936
|| aMachineState == MachineState_Teleported)
11938
/* Make sure any transient guest properties get removed from the
11939
* property store on shutdown. */
11941
HWData::GuestPropertyList::iterator it;
11942
BOOL fNeedsSaving = mData->mGuestPropertiesModified;
11944
for (it = mHWData->mGuestProperties.begin();
11945
it != mHWData->mGuestProperties.end(); ++it)
11946
if (it->mFlags & guestProp::TRANSIENT)
11948
fNeedsSaving = true;
11953
mData->mCurrentStateModified = TRUE;
11954
stsFlags |= SaveSTS_CurStateModified;
11955
SaveSettings(); // @todo r=dj why the public method? why first SaveSettings and then saveStateSettings?
11960
rc = saveStateSettings(stsFlags);
11962
if ( ( oldMachineState != MachineState_PoweredOff
11963
&& oldMachineState != MachineState_Aborted
11964
&& oldMachineState != MachineState_Teleported
11966
&& ( aMachineState == MachineState_PoweredOff
11967
|| aMachineState == MachineState_Aborted
11968
|| aMachineState == MachineState_Teleported
11972
/* we've been shut down for any reason */
11973
/* no special action so far */
11976
LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
11977
LogFlowThisFuncLeave();
11982
* Sends the current machine state value to the VM process.
11984
* @note Locks this object for reading, then calls a client process.
11986
HRESULT SessionMachine::updateMachineStateOnClient()
11988
AutoCaller autoCaller(this);
11989
AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11991
ComPtr<IInternalSessionControl> directControl;
11993
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11994
AssertReturn(!!mData, E_FAIL);
11995
directControl = mData->mSession.mDirectControl;
11997
/* directControl may be already set to NULL here in #OnSessionEnd()
11998
* called too early by the direct session process while there is still
11999
* some operation (like deleting the snapshot) in progress. The client
12000
* process in this case is waiting inside Session::close() for the
12001
* "end session" process object to complete, while #uninit() called by
12002
* #checkForDeath() on the Watcher thread is waiting for the pending
12003
* operation to complete. For now, we accept this inconsistent behavior
12004
* and simply do nothing here. */
12006
if (mData->mSession.mState == SessionState_Unlocking)
12009
AssertReturn(!directControl.isNull(), E_FAIL);
12012
return directControl->UpdateMachineState(mData->mMachineState);