VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/MachineImpl.cpp@ 94778

最後變更 在這個檔案從94778是 94778,由 vboxsync 提交於 3 年 前

Main/{DisplayUtils,MachineImpl}: Prepare the various screenshot getters to handle encrypted SSM streams when accessing the saved state file, bugref:9955

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 564.8 KB
 
1/* $Id: MachineImpl.cpp 94778 2022-05-02 10:51:48Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "SnapshotImpl.h"
32#include "ClientToken.h"
33#include "ProgressImpl.h"
34#include "ProgressProxyImpl.h"
35#include "MediumAttachmentImpl.h"
36#include "MediumImpl.h"
37#include "MediumLock.h"
38#include "USBControllerImpl.h"
39#include "USBDeviceFiltersImpl.h"
40#include "HostImpl.h"
41#include "SharedFolderImpl.h"
42#include "GuestOSTypeImpl.h"
43#include "VirtualBoxErrorInfoImpl.h"
44#include "StorageControllerImpl.h"
45#include "DisplayImpl.h"
46#include "DisplayUtils.h"
47#include "MachineImplCloneVM.h"
48#include "AutostartDb.h"
49#include "SystemPropertiesImpl.h"
50#include "MachineImplMoveVM.h"
51#include "ExtPackManagerImpl.h"
52#include "MachineLaunchVMCommonWorker.h"
53#include "CryptoUtils.h"
54
55// generated header
56#include "VBoxEvents.h"
57
58#ifdef VBOX_WITH_USB
59# include "USBProxyService.h"
60#endif
61
62#include "AutoCaller.h"
63#include "HashedPw.h"
64#include "Performance.h"
65#include "StringifyEnums.h"
66
67#include <iprt/asm.h>
68#include <iprt/path.h>
69#include <iprt/dir.h>
70#include <iprt/env.h>
71#include <iprt/lockvalidator.h>
72#include <iprt/memsafer.h>
73#include <iprt/process.h>
74#include <iprt/cpp/utils.h>
75#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
76#include <iprt/sha.h>
77#include <iprt/string.h>
78#include <iprt/ctype.h>
79
80#include <VBox/com/array.h>
81#include <VBox/com/list.h>
82#include <VBox/VBoxCryptoIf.h>
83
84#include <VBox/err.h>
85#include <VBox/param.h>
86#include <VBox/settings.h>
87#include <VBox/VMMDev.h>
88#include <VBox/vmm/ssm.h>
89
90#ifdef VBOX_WITH_GUEST_PROPS
91# include <VBox/HostServices/GuestPropertySvc.h>
92# include <VBox/com/array.h>
93#endif
94
95#ifdef VBOX_WITH_SHARED_CLIPBOARD
96# include <VBox/HostServices/VBoxClipboardSvc.h>
97#endif
98
99#include "VBox/com/MultiResult.h"
100
101#include <algorithm>
102
103#ifdef VBOX_WITH_DTRACE_R3_MAIN
104# include "dtrace/VBoxAPI.h"
105#endif
106
107#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
108# define HOSTSUFF_EXE ".exe"
109#else /* !RT_OS_WINDOWS */
110# define HOSTSUFF_EXE ""
111#endif /* !RT_OS_WINDOWS */
112
113// defines / prototypes
114/////////////////////////////////////////////////////////////////////////////
115
116#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
117# define BUF_DATA_SIZE _64K
118
119enum CipherMode
120{
121 CipherModeGcm = 0,
122 CipherModeCtr,
123 CipherModeXts,
124 CipherModeMax
125};
126
127enum AesSize
128{
129 Aes128 = 0,
130 Aes256,
131 AesMax
132};
133
134const char *g_apszCipher[AesMax][CipherModeMax] =
135{
136 {"AES-GCM128", "AES-CTR128", "AES-XTS128-PLAIN64"},
137 {"AES-GCM256", "AES-CTR256", "AES-XTS256-PLAIN64"}
138};
139const char *g_apszCipherAlgo[AesMax] = {"AES-128", "AES-256"};
140
141static const char *getCipherString(const char *pszAlgo, const int iMode)
142{
143 if (iMode >= CipherModeMax)
144 return pszAlgo;
145
146 for (int i = 0; i < AesMax; i++)
147 {
148 if (strcmp(pszAlgo, g_apszCipherAlgo[i]) == 0)
149 return g_apszCipher[i][iMode];
150 }
151 return pszAlgo;
152}
153
154static const char *getCipherStringWithoutMode(const char *pszAlgo)
155{
156 for (int i = 0; i < AesMax; i++)
157 {
158 for (int j = 0; j < CipherModeMax; j++)
159 {
160 if (strcmp(pszAlgo, g_apszCipher[i][j]) == 0)
161 return g_apszCipherAlgo[i];
162 }
163 }
164 return pszAlgo;
165}
166#endif
167
168/////////////////////////////////////////////////////////////////////////////
169// Machine::Data structure
170/////////////////////////////////////////////////////////////////////////////
171
172Machine::Data::Data()
173{
174 mRegistered = FALSE;
175 pMachineConfigFile = NULL;
176 /* Contains hints on what has changed when the user is using the VM (config
177 * changes, running the VM, ...). This is used to decide if a config needs
178 * to be written to disk. */
179 flModifications = 0;
180 /* VM modification usually also trigger setting the current state to
181 * "Modified". Although this is not always the case. An e.g. is the VM
182 * initialization phase or when snapshot related data is changed. The
183 * actually behavior is controlled by the following flag. */
184 m_fAllowStateModification = false;
185 mAccessible = FALSE;
186 /* mUuid is initialized in Machine::init() */
187
188 mMachineState = MachineState_PoweredOff;
189 RTTimeNow(&mLastStateChange);
190
191 mMachineStateDeps = 0;
192 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
193 mMachineStateChangePending = 0;
194
195 mCurrentStateModified = TRUE;
196 mGuestPropertiesModified = FALSE;
197
198 mSession.mPID = NIL_RTPROCESS;
199 mSession.mLockType = LockType_Null;
200 mSession.mState = SessionState_Unlocked;
201
202#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
203 mpKeyStore = NULL;
204#endif
205}
206
207Machine::Data::~Data()
208{
209 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
210 {
211 RTSemEventMultiDestroy(mMachineStateDepsSem);
212 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
213 }
214 if (pMachineConfigFile)
215 {
216 delete pMachineConfigFile;
217 pMachineConfigFile = NULL;
218 }
219}
220
221/////////////////////////////////////////////////////////////////////////////
222// Machine::HWData structure
223/////////////////////////////////////////////////////////////////////////////
224
225Machine::HWData::HWData()
226{
227 /* default values for a newly created machine */
228 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
229 mMemorySize = 128;
230 mCPUCount = 1;
231 mCPUHotPlugEnabled = false;
232 mMemoryBalloonSize = 0;
233 mPageFusionEnabled = false;
234 mHWVirtExEnabled = true;
235 mHWVirtExNestedPagingEnabled = true;
236 mHWVirtExLargePagesEnabled = HC_ARCH_BITS == 64; /* Not supported on 32 bits hosts. */
237 mHWVirtExVPIDEnabled = true;
238 mHWVirtExUXEnabled = true;
239 mHWVirtExForceEnabled = false;
240 mHWVirtExUseNativeApi = false;
241 mHWVirtExVirtVmsaveVmload = true;
242#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
243 mPAEEnabled = true;
244#else
245 mPAEEnabled = false;
246#endif
247 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
248 mTripleFaultReset = false;
249 mAPIC = true;
250 mX2APIC = false;
251 mIBPBOnVMExit = false;
252 mIBPBOnVMEntry = false;
253 mSpecCtrl = false;
254 mSpecCtrlByHost = false;
255 mL1DFlushOnSched = true;
256 mL1DFlushOnVMEntry = false;
257 mMDSClearOnSched = true;
258 mMDSClearOnVMEntry = false;
259 mNestedHWVirt = false;
260 mHPETEnabled = false;
261 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
262 mCpuIdPortabilityLevel = 0;
263 mCpuProfile = "host";
264
265 /* default boot order: floppy - DVD - HDD */
266 mBootOrder[0] = DeviceType_Floppy;
267 mBootOrder[1] = DeviceType_DVD;
268 mBootOrder[2] = DeviceType_HardDisk;
269 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
270 mBootOrder[i] = DeviceType_Null;
271
272 mClipboardMode = ClipboardMode_Disabled;
273 mClipboardFileTransfersEnabled = FALSE;
274
275 mDnDMode = DnDMode_Disabled;
276
277 mFirmwareType = FirmwareType_BIOS;
278 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
279 mPointingHIDType = PointingHIDType_PS2Mouse;
280 mChipsetType = ChipsetType_PIIX3;
281 mIommuType = IommuType_None;
282 mParavirtProvider = ParavirtProvider_Default;
283 mEmulatedUSBCardReaderEnabled = FALSE;
284
285 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
286 mCPUAttached[i] = false;
287
288 mIOCacheEnabled = true;
289 mIOCacheSize = 5; /* 5MB */
290}
291
292Machine::HWData::~HWData()
293{
294}
295
296/////////////////////////////////////////////////////////////////////////////
297// Machine class
298/////////////////////////////////////////////////////////////////////////////
299
300// constructor / destructor
301/////////////////////////////////////////////////////////////////////////////
302
303Machine::Machine() :
304#ifdef VBOX_WITH_RESOURCE_USAGE_API
305 mCollectorGuest(NULL),
306#endif
307 mPeer(NULL),
308 mParent(NULL),
309 mSerialPorts(),
310 mParallelPorts(),
311 uRegistryNeedsSaving(0)
312{}
313
314Machine::~Machine()
315{}
316
317HRESULT Machine::FinalConstruct()
318{
319 LogFlowThisFunc(("\n"));
320 return BaseFinalConstruct();
321}
322
323void Machine::FinalRelease()
324{
325 LogFlowThisFunc(("\n"));
326 uninit();
327 BaseFinalRelease();
328}
329
330/**
331 * Initializes a new machine instance; this init() variant creates a new, empty machine.
332 * This gets called from VirtualBox::CreateMachine().
333 *
334 * @param aParent Associated parent object
335 * @param strConfigFile Local file system path to the VM settings file (can
336 * be relative to the VirtualBox config directory).
337 * @param strName name for the machine
338 * @param llGroups list of groups for the machine
339 * @param strOsType OS Type string (stored as is if aOsType is NULL).
340 * @param aOsType OS Type of this machine or NULL.
341 * @param aId UUID for the new machine.
342 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
343 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
344 * scheme (includes the UUID).
345 * @param aCipher The cipher to encrypt the VM with.
346 * @param aPasswordId The password ID, empty if the VM should not be encrypted.
347 * @param aPassword The password to encrypt the VM with.
348 *
349 * @return Success indicator. if not S_OK, the machine object is invalid
350 */
351HRESULT Machine::init(VirtualBox *aParent,
352 const Utf8Str &strConfigFile,
353 const Utf8Str &strName,
354 const StringsList &llGroups,
355 const Utf8Str &strOsType,
356 GuestOSType *aOsType,
357 const Guid &aId,
358 bool fForceOverwrite,
359 bool fDirectoryIncludesUUID,
360 const com::Utf8Str &aCipher,
361 const com::Utf8Str &aPasswordId,
362 const com::Utf8Str &aPassword)
363{
364 LogFlowThisFuncEnter();
365 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
366
367#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
368 RT_NOREF(aCipher);
369 if (aPassword.isNotEmpty() || aPasswordId.isNotEmpty())
370 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
371#endif
372
373 /* Enclose the state transition NotReady->InInit->Ready */
374 AutoInitSpan autoInitSpan(this);
375 AssertReturn(autoInitSpan.isOk(), E_FAIL);
376
377 HRESULT rc = initImpl(aParent, strConfigFile);
378 if (FAILED(rc)) return rc;
379
380#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
381 com::Utf8Str strSsmKeyId;
382 com::Utf8Str strSsmKeyStore;
383 com::Utf8Str strNVRAMKeyId;
384 com::Utf8Str strNVRAMKeyStore;
385
386 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
387 {
388 /* Resolve the cryptographic interface. */
389 PCVBOXCRYPTOIF pCryptoIf = NULL;
390 HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
391 if (SUCCEEDED(hrc))
392 {
393 CipherMode aenmMode[] = {CipherModeGcm, CipherModeGcm, CipherModeGcm, CipherModeCtr};
394 com::Utf8Str *astrKeyId[] = {&mData->mstrKeyId, &strSsmKeyId, &strNVRAMKeyId, &mData->mstrLogKeyId};
395 com::Utf8Str *astrKeyStore[] = {&mData->mstrKeyStore, &strSsmKeyStore, &strNVRAMKeyStore, &mData->mstrLogKeyStore};
396
397 for (uint32_t i = 0; i < RT_ELEMENTS(astrKeyId); i++)
398 {
399 const char *pszCipher = getCipherString(aCipher.c_str(), aenmMode[i]);
400 if (!pszCipher)
401 {
402 hrc = setError(VBOX_E_NOT_SUPPORTED,
403 tr("The cipher '%s' is not supported"), aCipher.c_str());
404 break;
405 }
406
407 VBOXCRYPTOCTX hCryptoCtx;
408 int vrc = pCryptoIf->pfnCryptoCtxCreate(pszCipher, aPassword.c_str(), &hCryptoCtx);
409 if (RT_FAILURE(vrc))
410 {
411 hrc = setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
412 break;
413 }
414
415 char *pszKeyStore;
416 vrc = pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
417 int vrc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
418 AssertRC(vrc2);
419
420 if (RT_FAILURE(vrc))
421 {
422 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
423 break;
424 }
425
426 *(astrKeyStore[i]) = pszKeyStore;
427 RTMemFree(pszKeyStore);
428 *(astrKeyId[i]) = aPasswordId;
429 }
430
431 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
432 Assert(hrc2 == S_OK);
433
434 if (FAILED(hrc))
435 return hrc; /* Error is set. */
436 }
437 else
438 return hrc; /* Error is set. */
439 }
440#endif
441
442 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
443 if (FAILED(rc)) return rc;
444
445 if (SUCCEEDED(rc))
446 {
447 // create an empty machine config
448 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
449
450 rc = initDataAndChildObjects();
451 }
452
453 if (SUCCEEDED(rc))
454 {
455#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
456 mSSData->strStateKeyId = strSsmKeyId;
457 mSSData->strStateKeyStore = strSsmKeyStore;
458#endif
459
460 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
461 mData->mAccessible = TRUE;
462
463 unconst(mData->mUuid) = aId;
464
465 mUserData->s.strName = strName;
466
467 if (llGroups.size())
468 mUserData->s.llGroups = llGroups;
469
470 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
471 // the "name sync" flag determines whether the machine directory gets renamed along
472 // with the machine file; say so if the settings file name is the same as the
473 // settings file parent directory (machine directory)
474 mUserData->s.fNameSync = i_isInOwnDir();
475
476 // initialize the default snapshots folder
477 rc = COMSETTER(SnapshotFolder)(NULL);
478 AssertComRC(rc);
479
480 if (aOsType)
481 {
482 /* Store OS type */
483 mUserData->s.strOsType = aOsType->i_id();
484
485 /* Let the OS type select 64-bit ness. */
486 mHWData->mLongMode = aOsType->i_is64Bit()
487 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
488
489 /* Let the OS type enable the X2APIC */
490 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
491
492 rc = aOsType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
493 AssertComRC(rc);
494 }
495 else if (!strOsType.isEmpty())
496 {
497 /* Store OS type */
498 mUserData->s.strOsType = strOsType;
499
500 /* No guest OS type object. Pick some plausible defaults which the
501 * host can handle. There's no way to know or validate anything. */
502 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
503 mHWData->mX2APIC = false;
504 }
505
506 /* Apply BIOS defaults. */
507 mBIOSSettings->i_applyDefaults(aOsType);
508
509 /* Apply TPM defaults. */
510 mTrustedPlatformModule->i_applyDefaults(aOsType);
511
512 /* Apply record defaults. */
513 mRecordingSettings->i_applyDefaults();
514
515 /* Apply network adapters defaults */
516 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
517 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
518
519 /* Apply serial port defaults */
520 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
521 mSerialPorts[slot]->i_applyDefaults(aOsType);
522
523 /* Apply parallel port defaults */
524 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
525 mParallelPorts[slot]->i_applyDefaults();
526
527 /* Enable the VMMDev testing feature for bootsector VMs: */
528 if (aOsType && aOsType->i_id() == "VBoxBS_64")
529 mData->pMachineConfigFile->mapExtraDataItems["VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled"] = "1";
530
531 /* At this point the changing of the current state modification
532 * flag is allowed. */
533 i_allowStateModification();
534
535 /* commit all changes made during the initialization */
536 i_commit();
537 }
538
539 /* Confirm a successful initialization when it's the case */
540 if (SUCCEEDED(rc))
541 {
542#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
543 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
544 {
545 size_t cbPassword = aPassword.length() + 1;
546 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
547 mData->mpKeyStore->addSecretKey(aPasswordId, pbPassword, cbPassword);
548 }
549#endif
550
551 if (mData->mAccessible)
552 autoInitSpan.setSucceeded();
553 else
554 autoInitSpan.setLimited();
555 }
556
557 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
558 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
559 mData->mRegistered,
560 mData->mAccessible,
561 rc));
562
563 LogFlowThisFuncLeave();
564
565 return rc;
566}
567
568/**
569 * Initializes a new instance with data from machine XML (formerly Init_Registered).
570 * Gets called in two modes:
571 *
572 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
573 * UUID is specified and we mark the machine as "registered";
574 *
575 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
576 * and the machine remains unregistered until RegisterMachine() is called.
577 *
578 * @param aParent Associated parent object
579 * @param strConfigFile Local file system path to the VM settings file (can
580 * be relative to the VirtualBox config directory).
581 * @param aId UUID of the machine or NULL (see above).
582 * @param strPassword Password for decrypting the config
583 *
584 * @return Success indicator. if not S_OK, the machine object is invalid
585 */
586HRESULT Machine::initFromSettings(VirtualBox *aParent,
587 const Utf8Str &strConfigFile,
588 const Guid *aId,
589 const com::Utf8Str &strPassword)
590{
591 LogFlowThisFuncEnter();
592 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
593
594 PCVBOXCRYPTOIF pCryptoIf = NULL;
595#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
596 if (strPassword.isNotEmpty())
597 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
598#else
599 if (strPassword.isNotEmpty())
600 {
601 /* Get at the crpytographic interface. */
602 HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
603 if (FAILED(hrc))
604 return hrc; /* Error is set. */
605 }
606#endif
607
608 /* Enclose the state transition NotReady->InInit->Ready */
609 AutoInitSpan autoInitSpan(this);
610 AssertReturn(autoInitSpan.isOk(), E_FAIL);
611
612 HRESULT rc = initImpl(aParent, strConfigFile);
613 if (FAILED(rc)) return rc;
614
615 if (aId)
616 {
617 // loading a registered VM:
618 unconst(mData->mUuid) = *aId;
619 mData->mRegistered = TRUE;
620 // now load the settings from XML:
621 rc = i_registeredInit();
622 // this calls initDataAndChildObjects() and loadSettings()
623 }
624 else
625 {
626 // opening an unregistered VM (VirtualBox::OpenMachine()):
627 rc = initDataAndChildObjects();
628
629 if (SUCCEEDED(rc))
630 {
631 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
632 mData->mAccessible = TRUE;
633
634 try
635 {
636 // load and parse machine XML; this will throw on XML or logic errors
637 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
638 pCryptoIf,
639 strPassword.c_str());
640
641 // reject VM UUID duplicates, they can happen if someone
642 // tries to register an already known VM config again
643 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
644 true /* fPermitInaccessible */,
645 false /* aDoSetError */,
646 NULL) != VBOX_E_OBJECT_NOT_FOUND)
647 {
648 throw setError(E_FAIL,
649 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
650 mData->m_strConfigFile.c_str());
651 }
652
653 // use UUID from machine config
654 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
655
656#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
657 // No exception is thrown if config is encrypted, allowing us to get the uuid and the encryption fields.
658 // We fill in the encryptions fields, and the rest will be filled in if all data parsed.
659 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
660 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
661#endif
662
663 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
664 {
665 // We just set the inaccessible state and fill the error info allowing the caller
666 // to register the machine with encrypted config even if the password is incorrect
667 mData->mAccessible = FALSE;
668
669 /* fetch the current error info */
670 mData->mAccessError = com::ErrorInfo();
671
672 throw setError(VBOX_E_PASSWORD_INCORRECT,
673 tr("Decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
674 mData->pMachineConfigFile->uuid.raw());
675 }
676 else
677 {
678#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
679 if (strPassword.isNotEmpty())
680 {
681 size_t cbKey = strPassword.length() + 1; /* Include terminator */
682 const uint8_t *pbKey = (const uint8_t *)strPassword.c_str();
683 mData->mpKeyStore->addSecretKey(mData->mstrKeyId, pbKey, cbKey);
684 }
685#endif
686
687 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
688 NULL /* puuidRegistry */);
689 if (FAILED(rc)) throw rc;
690
691 /* At this point the changing of the current state modification
692 * flag is allowed. */
693 i_allowStateModification();
694
695 i_commit();
696 }
697 }
698 catch (HRESULT err)
699 {
700 /* we assume that error info is set by the thrower */
701 rc = err;
702 }
703 catch (...)
704 {
705 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
706 }
707 }
708 }
709
710 /* Confirm a successful initialization when it's the case */
711 if (SUCCEEDED(rc))
712 {
713 if (mData->mAccessible)
714 autoInitSpan.setSucceeded();
715 else
716 {
717 autoInitSpan.setLimited();
718
719 // uninit media from this machine's media registry, or else
720 // reloading the settings will fail
721 mParent->i_unregisterMachineMedia(i_getId());
722 }
723 }
724
725#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
726 if (pCryptoIf)
727 {
728 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
729 Assert(hrc2 == S_OK);
730 }
731#endif
732
733 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
734 "rc=%08X\n",
735 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
736 mData->mRegistered, mData->mAccessible, rc));
737
738 LogFlowThisFuncLeave();
739
740 return rc;
741}
742
743/**
744 * Initializes a new instance from a machine config that is already in memory
745 * (import OVF case). Since we are importing, the UUID in the machine
746 * config is ignored and we always generate a fresh one.
747 *
748 * @param aParent Associated parent object.
749 * @param strName Name for the new machine; this overrides what is specified in config.
750 * @param strSettingsFilename File name of .vbox file.
751 * @param config Machine configuration loaded and parsed from XML.
752 *
753 * @return Success indicator. if not S_OK, the machine object is invalid
754 */
755HRESULT Machine::init(VirtualBox *aParent,
756 const Utf8Str &strName,
757 const Utf8Str &strSettingsFilename,
758 const settings::MachineConfigFile &config)
759{
760 LogFlowThisFuncEnter();
761
762 /* Enclose the state transition NotReady->InInit->Ready */
763 AutoInitSpan autoInitSpan(this);
764 AssertReturn(autoInitSpan.isOk(), E_FAIL);
765
766 HRESULT rc = initImpl(aParent, strSettingsFilename);
767 if (FAILED(rc)) return rc;
768
769 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
770 if (FAILED(rc)) return rc;
771
772 rc = initDataAndChildObjects();
773
774 if (SUCCEEDED(rc))
775 {
776 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
777 mData->mAccessible = TRUE;
778
779 // create empty machine config for instance data
780 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
781
782 // generate fresh UUID, ignore machine config
783 unconst(mData->mUuid).create();
784
785 rc = i_loadMachineDataFromSettings(config,
786 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
787
788 // override VM name as well, it may be different
789 mUserData->s.strName = strName;
790
791 if (SUCCEEDED(rc))
792 {
793 /* At this point the changing of the current state modification
794 * flag is allowed. */
795 i_allowStateModification();
796
797 /* commit all changes made during the initialization */
798 i_commit();
799 }
800 }
801
802 /* Confirm a successful initialization when it's the case */
803 if (SUCCEEDED(rc))
804 {
805 if (mData->mAccessible)
806 autoInitSpan.setSucceeded();
807 else
808 {
809 /* Ignore all errors from unregistering, they would destroy
810- * the more interesting error information we already have,
811- * pinpointing the issue with the VM config. */
812 ErrorInfoKeeper eik;
813
814 autoInitSpan.setLimited();
815
816 // uninit media from this machine's media registry, or else
817 // reloading the settings will fail
818 mParent->i_unregisterMachineMedia(i_getId());
819 }
820 }
821
822 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
823 "rc=%08X\n",
824 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
825 mData->mRegistered, mData->mAccessible, rc));
826
827 LogFlowThisFuncLeave();
828
829 return rc;
830}
831
832/**
833 * Shared code between the various init() implementations.
834 * @param aParent The VirtualBox object.
835 * @param strConfigFile Settings file.
836 * @return
837 */
838HRESULT Machine::initImpl(VirtualBox *aParent,
839 const Utf8Str &strConfigFile)
840{
841 LogFlowThisFuncEnter();
842
843 AssertReturn(aParent, E_INVALIDARG);
844 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
845
846 HRESULT rc = S_OK;
847
848 /* share the parent weakly */
849 unconst(mParent) = aParent;
850
851 /* allocate the essential machine data structure (the rest will be
852 * allocated later by initDataAndChildObjects() */
853 mData.allocate();
854
855 /* memorize the config file name (as provided) */
856 mData->m_strConfigFile = strConfigFile;
857
858 /* get the full file name */
859 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
860 if (RT_FAILURE(vrc1))
861 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
862 tr("Invalid machine settings file name '%s' (%Rrc)"),
863 strConfigFile.c_str(),
864 vrc1);
865
866#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
867 /** @todo Only create when the machine is going to be encrypted. */
868 /* Non-pageable memory is not accessible for non-VM process */
869 mData->mpKeyStore = new SecretKeyStore(false /* fKeyBufNonPageable */);
870 AssertReturn(mData->mpKeyStore, VERR_NO_MEMORY);
871#endif
872
873 LogFlowThisFuncLeave();
874
875 return rc;
876}
877
878/**
879 * Tries to create a machine settings file in the path stored in the machine
880 * instance data. Used when a new machine is created to fail gracefully if
881 * the settings file could not be written (e.g. because machine dir is read-only).
882 * @return
883 */
884HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
885{
886 HRESULT rc = S_OK;
887
888 // when we create a new machine, we must be able to create the settings file
889 RTFILE f = NIL_RTFILE;
890 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
891 if ( RT_SUCCESS(vrc)
892 || vrc == VERR_SHARING_VIOLATION
893 )
894 {
895 if (RT_SUCCESS(vrc))
896 RTFileClose(f);
897 if (!fForceOverwrite)
898 rc = setError(VBOX_E_FILE_ERROR,
899 tr("Machine settings file '%s' already exists"),
900 mData->m_strConfigFileFull.c_str());
901 else
902 {
903 /* try to delete the config file, as otherwise the creation
904 * of a new settings file will fail. */
905 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
906 if (RT_FAILURE(vrc2))
907 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
908 tr("Could not delete the existing settings file '%s' (%Rrc)"),
909 mData->m_strConfigFileFull.c_str(), vrc2);
910 }
911 }
912 else if ( vrc != VERR_FILE_NOT_FOUND
913 && vrc != VERR_PATH_NOT_FOUND
914 )
915 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
916 tr("Invalid machine settings file name '%s' (%Rrc)"),
917 mData->m_strConfigFileFull.c_str(),
918 vrc);
919 return rc;
920}
921
922/**
923 * Initializes the registered machine by loading the settings file.
924 * This method is separated from #init() in order to make it possible to
925 * retry the operation after VirtualBox startup instead of refusing to
926 * startup the whole VirtualBox server in case if the settings file of some
927 * registered VM is invalid or inaccessible.
928 *
929 * @note Must be always called from this object's write lock
930 * (unless called from #init() that doesn't need any locking).
931 * @note Locks the mUSBController method for writing.
932 * @note Subclasses must not call this method.
933 */
934HRESULT Machine::i_registeredInit()
935{
936 AssertReturn(!i_isSessionMachine(), E_FAIL);
937 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
938 AssertReturn(mData->mUuid.isValid(), E_FAIL);
939 AssertReturn(!mData->mAccessible, E_FAIL);
940
941 HRESULT rc = initDataAndChildObjects();
942
943 if (SUCCEEDED(rc))
944 {
945 /* Temporarily reset the registered flag in order to let setters
946 * potentially called from loadSettings() succeed (isMutable() used in
947 * all setters will return FALSE for a Machine instance if mRegistered
948 * is TRUE). */
949 mData->mRegistered = FALSE;
950
951 PCVBOXCRYPTOIF pCryptoIf = NULL;
952 SecretKey *pKey = NULL;
953 const char *pszPassword = NULL;
954#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
955 /* Resolve password and cryptographic support interface if machine is encrypted. */
956 if (mData->mstrKeyId.isNotEmpty())
957 {
958 /* Get at the crpytographic interface. */
959 rc = mParent->i_retainCryptoIf(&pCryptoIf);
960 if (SUCCEEDED(rc))
961 {
962 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
963 if (RT_SUCCESS(vrc))
964 pszPassword = (const char *)pKey->getKeyBuffer();
965 else
966 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to retain key for key ID '%s' with %Rrc"),
967 mData->mstrKeyId.c_str(), vrc);
968 }
969 }
970#else
971 RT_NOREF(pKey);
972#endif
973
974 if (SUCCEEDED(rc))
975 {
976 try
977 {
978 // load and parse machine XML; this will throw on XML or logic errors
979 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
980 pCryptoIf, pszPassword);
981
982 if (mData->mUuid != mData->pMachineConfigFile->uuid)
983 throw setError(E_FAIL,
984 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
985 mData->pMachineConfigFile->uuid.raw(),
986 mData->m_strConfigFileFull.c_str(),
987 mData->mUuid.toString().c_str(),
988 mParent->i_settingsFilePath().c_str());
989
990#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
991 // If config is encrypted, no exception is thrown allowing us to get the uuid and the encryption fields.
992 // We fill in the encryptions fields, and the rest will be filled in if all data parsed
993 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
994 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
995
996 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
997 throw setError(VBOX_E_PASSWORD_INCORRECT,
998 tr("Config decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
999 mData->pMachineConfigFile->uuid.raw());
1000#endif
1001
1002 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
1003 NULL /* const Guid *puuidRegistry */);
1004 if (FAILED(rc)) throw rc;
1005 }
1006 catch (HRESULT err)
1007 {
1008 /* we assume that error info is set by the thrower */
1009 rc = err;
1010 }
1011 catch (...)
1012 {
1013 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
1014 }
1015
1016 /* Restore the registered flag (even on failure) */
1017 mData->mRegistered = TRUE;
1018 }
1019
1020#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1021 if (pCryptoIf)
1022 mParent->i_releaseCryptoIf(pCryptoIf);
1023 if (pKey)
1024 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
1025#endif
1026 }
1027
1028 if (SUCCEEDED(rc))
1029 {
1030 /* Set mAccessible to TRUE only if we successfully locked and loaded
1031 * the settings file */
1032 mData->mAccessible = TRUE;
1033
1034 /* commit all changes made during loading the settings file */
1035 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
1036 /// @todo r=klaus for some reason the settings loading logic backs up
1037 // the settings, and therefore a commit is needed. Should probably be changed.
1038 }
1039 else
1040 {
1041 /* If the machine is registered, then, instead of returning a
1042 * failure, we mark it as inaccessible and set the result to
1043 * success to give it a try later */
1044
1045 /* fetch the current error info */
1046 mData->mAccessError = com::ErrorInfo();
1047 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
1048
1049 /* rollback all changes */
1050 i_rollback(false /* aNotify */);
1051
1052 // uninit media from this machine's media registry, or else
1053 // reloading the settings will fail
1054 mParent->i_unregisterMachineMedia(i_getId());
1055
1056 /* uninitialize the common part to make sure all data is reset to
1057 * default (null) values */
1058 uninitDataAndChildObjects();
1059
1060 rc = S_OK;
1061 }
1062
1063 return rc;
1064}
1065
1066/**
1067 * Uninitializes the instance.
1068 * Called either from FinalRelease() or by the parent when it gets destroyed.
1069 *
1070 * @note The caller of this method must make sure that this object
1071 * a) doesn't have active callers on the current thread and b) is not locked
1072 * by the current thread; otherwise uninit() will hang either a) due to
1073 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
1074 * a dead-lock caused by this thread waiting for all callers on the other
1075 * threads are done but preventing them from doing so by holding a lock.
1076 */
1077void Machine::uninit()
1078{
1079 LogFlowThisFuncEnter();
1080
1081 Assert(!isWriteLockOnCurrentThread());
1082
1083 Assert(!uRegistryNeedsSaving);
1084 if (uRegistryNeedsSaving)
1085 {
1086 AutoCaller autoCaller(this);
1087 if (SUCCEEDED(autoCaller.rc()))
1088 {
1089 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1090 i_saveSettings(NULL, alock, Machine::SaveS_Force);
1091 }
1092 }
1093
1094 /* Enclose the state transition Ready->InUninit->NotReady */
1095 AutoUninitSpan autoUninitSpan(this);
1096 if (autoUninitSpan.uninitDone())
1097 return;
1098
1099 Assert(!i_isSnapshotMachine());
1100 Assert(!i_isSessionMachine());
1101 Assert(!!mData);
1102
1103 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
1104 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
1105
1106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1107
1108 if (!mData->mSession.mMachine.isNull())
1109 {
1110 /* Theoretically, this can only happen if the VirtualBox server has been
1111 * terminated while there were clients running that owned open direct
1112 * sessions. Since in this case we are definitely called by
1113 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
1114 * won't happen on the client watcher thread (because it has a
1115 * VirtualBox caller for the duration of the
1116 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
1117 * cannot happen until the VirtualBox caller is released). This is
1118 * important, because SessionMachine::uninit() cannot correctly operate
1119 * after we return from this method (it expects the Machine instance is
1120 * still valid). We'll call it ourselves below.
1121 */
1122 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
1123 (SessionMachine*)mData->mSession.mMachine));
1124
1125 if (Global::IsOnlineOrTransient(mData->mMachineState))
1126 {
1127 Log1WarningThisFunc(("Setting state to Aborted!\n"));
1128 /* set machine state using SessionMachine reimplementation */
1129 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
1130 }
1131
1132 /*
1133 * Uninitialize SessionMachine using public uninit() to indicate
1134 * an unexpected uninitialization.
1135 */
1136 mData->mSession.mMachine->uninit();
1137 /* SessionMachine::uninit() must set mSession.mMachine to null */
1138 Assert(mData->mSession.mMachine.isNull());
1139 }
1140
1141 // uninit media from this machine's media registry, if they're still there
1142 Guid uuidMachine(i_getId());
1143
1144 /* the lock is no more necessary (SessionMachine is uninitialized) */
1145 alock.release();
1146
1147 /* XXX This will fail with
1148 * "cannot be closed because it is still attached to 1 virtual machines"
1149 * because at this point we did not call uninitDataAndChildObjects() yet
1150 * and therefore also removeBackReference() for all these mediums was not called! */
1151
1152 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
1153 mParent->i_unregisterMachineMedia(uuidMachine);
1154
1155 // has machine been modified?
1156 if (mData->flModifications)
1157 {
1158 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
1159 i_rollback(false /* aNotify */);
1160 }
1161
1162 if (mData->mAccessible)
1163 uninitDataAndChildObjects();
1164
1165#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1166 if (mData->mpKeyStore != NULL)
1167 delete mData->mpKeyStore;
1168#endif
1169
1170 /* free the essential data structure last */
1171 mData.free();
1172
1173 LogFlowThisFuncLeave();
1174}
1175
1176// Wrapped IMachine properties
1177/////////////////////////////////////////////////////////////////////////////
1178HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
1179{
1180 /* mParent is constant during life time, no need to lock */
1181 ComObjPtr<VirtualBox> pVirtualBox(mParent);
1182 aParent = pVirtualBox;
1183
1184 return S_OK;
1185}
1186
1187
1188HRESULT Machine::getAccessible(BOOL *aAccessible)
1189{
1190 /* In some cases (medium registry related), it is necessary to be able to
1191 * go through the list of all machines. Happens when an inaccessible VM
1192 * has a sensible medium registry. */
1193 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
1194 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1195
1196 HRESULT rc = S_OK;
1197
1198 if (!mData->mAccessible)
1199 {
1200 /* try to initialize the VM once more if not accessible */
1201
1202 AutoReinitSpan autoReinitSpan(this);
1203 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
1204
1205#ifdef DEBUG
1206 LogFlowThisFunc(("Dumping media backreferences\n"));
1207 mParent->i_dumpAllBackRefs();
1208#endif
1209
1210 if (mData->pMachineConfigFile)
1211 {
1212 // reset the XML file to force loadSettings() (called from i_registeredInit())
1213 // to parse it again; the file might have changed
1214 delete mData->pMachineConfigFile;
1215 mData->pMachineConfigFile = NULL;
1216 }
1217
1218 rc = i_registeredInit();
1219
1220 if (SUCCEEDED(rc) && mData->mAccessible)
1221 {
1222 autoReinitSpan.setSucceeded();
1223
1224 /* make sure interesting parties will notice the accessibility
1225 * state change */
1226 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
1227 mParent->i_onMachineDataChanged(mData->mUuid);
1228 }
1229 }
1230
1231 if (SUCCEEDED(rc))
1232 *aAccessible = mData->mAccessible;
1233
1234 LogFlowThisFuncLeave();
1235
1236 return rc;
1237}
1238
1239HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
1240{
1241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1242
1243 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
1244 {
1245 /* return shortly */
1246 aAccessError = NULL;
1247 return S_OK;
1248 }
1249
1250 HRESULT rc = S_OK;
1251
1252 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1253 rc = errorInfo.createObject();
1254 if (SUCCEEDED(rc))
1255 {
1256 errorInfo->init(mData->mAccessError.getResultCode(),
1257 mData->mAccessError.getInterfaceID().ref(),
1258 Utf8Str(mData->mAccessError.getComponent()).c_str(),
1259 Utf8Str(mData->mAccessError.getText()));
1260 aAccessError = errorInfo;
1261 }
1262
1263 return rc;
1264}
1265
1266HRESULT Machine::getName(com::Utf8Str &aName)
1267{
1268 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1269
1270 aName = mUserData->s.strName;
1271
1272 return S_OK;
1273}
1274
1275HRESULT Machine::setName(const com::Utf8Str &aName)
1276{
1277 // prohibit setting a UUID only as the machine name, or else it can
1278 // never be found by findMachine()
1279 Guid test(aName);
1280
1281 if (test.isValid())
1282 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1283
1284 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1285
1286 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1287 if (FAILED(rc)) return rc;
1288
1289 i_setModified(IsModified_MachineData);
1290 mUserData.backup();
1291 mUserData->s.strName = aName;
1292
1293 return S_OK;
1294}
1295
1296HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1297{
1298 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1299
1300 aDescription = mUserData->s.strDescription;
1301
1302 return S_OK;
1303}
1304
1305HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1306{
1307 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1308
1309 // this can be done in principle in any state as it doesn't affect the VM
1310 // significantly, but play safe by not messing around while complex
1311 // activities are going on
1312 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1313 if (FAILED(rc)) return rc;
1314
1315 i_setModified(IsModified_MachineData);
1316 mUserData.backup();
1317 mUserData->s.strDescription = aDescription;
1318
1319 return S_OK;
1320}
1321
1322HRESULT Machine::getId(com::Guid &aId)
1323{
1324 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1325
1326 aId = mData->mUuid;
1327
1328 return S_OK;
1329}
1330
1331HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1332{
1333 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1334 aGroups.resize(mUserData->s.llGroups.size());
1335 size_t i = 0;
1336 for (StringsList::const_iterator
1337 it = mUserData->s.llGroups.begin();
1338 it != mUserData->s.llGroups.end();
1339 ++it, ++i)
1340 aGroups[i] = (*it);
1341
1342 return S_OK;
1343}
1344
1345HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1346{
1347 StringsList llGroups;
1348 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1349 if (FAILED(rc))
1350 return rc;
1351
1352 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1353
1354 rc = i_checkStateDependency(MutableOrSavedStateDep);
1355 if (FAILED(rc)) return rc;
1356
1357 i_setModified(IsModified_MachineData);
1358 mUserData.backup();
1359 mUserData->s.llGroups = llGroups;
1360
1361 return S_OK;
1362}
1363
1364HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1365{
1366 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1367
1368 aOSTypeId = mUserData->s.strOsType;
1369
1370 return S_OK;
1371}
1372
1373HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1374{
1375 /* look up the object by Id to check it is valid */
1376 ComObjPtr<GuestOSType> pGuestOSType;
1377 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1378
1379 /* when setting, always use the "etalon" value for consistency -- lookup
1380 * by ID is case-insensitive and the input value may have different case */
1381 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1382
1383 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1384
1385 HRESULT rc = i_checkStateDependency(MutableStateDep);
1386 if (FAILED(rc)) return rc;
1387
1388 i_setModified(IsModified_MachineData);
1389 mUserData.backup();
1390 mUserData->s.strOsType = osTypeId;
1391
1392 return S_OK;
1393}
1394
1395HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1396{
1397 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1398
1399 *aFirmwareType = mHWData->mFirmwareType;
1400
1401 return S_OK;
1402}
1403
1404HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1405{
1406 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1407
1408 HRESULT rc = i_checkStateDependency(MutableStateDep);
1409 if (FAILED(rc)) return rc;
1410
1411 i_setModified(IsModified_MachineData);
1412 mHWData.backup();
1413 mHWData->mFirmwareType = aFirmwareType;
1414 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1415 alock.release();
1416
1417 mNvramStore->i_updateNonVolatileStorageFile(strNVRAM);
1418
1419 return S_OK;
1420}
1421
1422HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1423{
1424 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1425
1426 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1427
1428 return S_OK;
1429}
1430
1431HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1432{
1433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1434
1435 HRESULT rc = i_checkStateDependency(MutableStateDep);
1436 if (FAILED(rc)) return rc;
1437
1438 i_setModified(IsModified_MachineData);
1439 mHWData.backup();
1440 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1441
1442 return S_OK;
1443}
1444
1445HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1446{
1447 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1448
1449 *aPointingHIDType = mHWData->mPointingHIDType;
1450
1451 return S_OK;
1452}
1453
1454HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1455{
1456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1457
1458 HRESULT rc = i_checkStateDependency(MutableStateDep);
1459 if (FAILED(rc)) return rc;
1460
1461 i_setModified(IsModified_MachineData);
1462 mHWData.backup();
1463 mHWData->mPointingHIDType = aPointingHIDType;
1464
1465 return S_OK;
1466}
1467
1468HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1469{
1470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1471
1472 *aChipsetType = mHWData->mChipsetType;
1473
1474 return S_OK;
1475}
1476
1477HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1478{
1479 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1480
1481 HRESULT rc = i_checkStateDependency(MutableStateDep);
1482 if (FAILED(rc)) return rc;
1483
1484 if (aChipsetType != mHWData->mChipsetType)
1485 {
1486 i_setModified(IsModified_MachineData);
1487 mHWData.backup();
1488 mHWData->mChipsetType = aChipsetType;
1489
1490 // Resize network adapter array, to be finalized on commit/rollback.
1491 // We must not throw away entries yet, otherwise settings are lost
1492 // without a way to roll back.
1493 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1494 size_t oldCount = mNetworkAdapters.size();
1495 if (newCount > oldCount)
1496 {
1497 mNetworkAdapters.resize(newCount);
1498 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1499 {
1500 unconst(mNetworkAdapters[slot]).createObject();
1501 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1502 }
1503 }
1504 }
1505
1506 return S_OK;
1507}
1508
1509HRESULT Machine::getIommuType(IommuType_T *aIommuType)
1510{
1511 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1512
1513 *aIommuType = mHWData->mIommuType;
1514
1515 return S_OK;
1516}
1517
1518HRESULT Machine::setIommuType(IommuType_T aIommuType)
1519{
1520 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1521
1522 HRESULT rc = i_checkStateDependency(MutableStateDep);
1523 if (FAILED(rc)) return rc;
1524
1525 if (aIommuType != mHWData->mIommuType)
1526 {
1527 if (aIommuType == IommuType_Intel)
1528 {
1529#ifndef VBOX_WITH_IOMMU_INTEL
1530 LogRelFunc(("Setting Intel IOMMU when Intel IOMMU support not available!\n"));
1531 return E_UNEXPECTED;
1532#endif
1533 }
1534
1535 i_setModified(IsModified_MachineData);
1536 mHWData.backup();
1537 mHWData->mIommuType = aIommuType;
1538 }
1539
1540 return S_OK;
1541}
1542
1543HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1544{
1545 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1546
1547 aParavirtDebug = mHWData->mParavirtDebug;
1548 return S_OK;
1549}
1550
1551HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1552{
1553 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1554
1555 HRESULT rc = i_checkStateDependency(MutableStateDep);
1556 if (FAILED(rc)) return rc;
1557
1558 /** @todo Parse/validate options? */
1559 if (aParavirtDebug != mHWData->mParavirtDebug)
1560 {
1561 i_setModified(IsModified_MachineData);
1562 mHWData.backup();
1563 mHWData->mParavirtDebug = aParavirtDebug;
1564 }
1565
1566 return S_OK;
1567}
1568
1569HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1570{
1571 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1572
1573 *aParavirtProvider = mHWData->mParavirtProvider;
1574
1575 return S_OK;
1576}
1577
1578HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1579{
1580 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1581
1582 HRESULT rc = i_checkStateDependency(MutableStateDep);
1583 if (FAILED(rc)) return rc;
1584
1585 if (aParavirtProvider != mHWData->mParavirtProvider)
1586 {
1587 i_setModified(IsModified_MachineData);
1588 mHWData.backup();
1589 mHWData->mParavirtProvider = aParavirtProvider;
1590 }
1591
1592 return S_OK;
1593}
1594
1595HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1596{
1597 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1598
1599 *aParavirtProvider = mHWData->mParavirtProvider;
1600 switch (mHWData->mParavirtProvider)
1601 {
1602 case ParavirtProvider_None:
1603 case ParavirtProvider_HyperV:
1604 case ParavirtProvider_KVM:
1605 case ParavirtProvider_Minimal:
1606 break;
1607
1608 /* Resolve dynamic provider types to the effective types. */
1609 default:
1610 {
1611 ComObjPtr<GuestOSType> pGuestOSType;
1612 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1613 pGuestOSType);
1614 if (FAILED(hrc2) || pGuestOSType.isNull())
1615 {
1616 *aParavirtProvider = ParavirtProvider_None;
1617 break;
1618 }
1619
1620 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1621 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1622
1623 switch (mHWData->mParavirtProvider)
1624 {
1625 case ParavirtProvider_Legacy:
1626 {
1627 if (fOsXGuest)
1628 *aParavirtProvider = ParavirtProvider_Minimal;
1629 else
1630 *aParavirtProvider = ParavirtProvider_None;
1631 break;
1632 }
1633
1634 case ParavirtProvider_Default:
1635 {
1636 if (fOsXGuest)
1637 *aParavirtProvider = ParavirtProvider_Minimal;
1638 else if ( mUserData->s.strOsType == "Windows11_64"
1639 || mUserData->s.strOsType == "Windows10"
1640 || mUserData->s.strOsType == "Windows10_64"
1641 || mUserData->s.strOsType == "Windows81"
1642 || mUserData->s.strOsType == "Windows81_64"
1643 || mUserData->s.strOsType == "Windows8"
1644 || mUserData->s.strOsType == "Windows8_64"
1645 || mUserData->s.strOsType == "Windows7"
1646 || mUserData->s.strOsType == "Windows7_64"
1647 || mUserData->s.strOsType == "WindowsVista"
1648 || mUserData->s.strOsType == "WindowsVista_64"
1649 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1650 || mUserData->s.strOsType.startsWith("Windows201"))
1651 && mUserData->s.strOsType.endsWith("_64"))
1652 || mUserData->s.strOsType == "Windows2012"
1653 || mUserData->s.strOsType == "Windows2012_64"
1654 || mUserData->s.strOsType == "Windows2008"
1655 || mUserData->s.strOsType == "Windows2008_64")
1656 {
1657 *aParavirtProvider = ParavirtProvider_HyperV;
1658 }
1659 else if (guestTypeFamilyId == "Linux" &&
1660 mUserData->s.strOsType != "Linux22" && // Linux22 and Linux24{_64} excluded as they're too old
1661 mUserData->s.strOsType != "Linux24" && // to have any KVM paravirtualization support.
1662 mUserData->s.strOsType != "Linux24_64")
1663 {
1664 *aParavirtProvider = ParavirtProvider_KVM;
1665 }
1666 else
1667 *aParavirtProvider = ParavirtProvider_None;
1668 break;
1669 }
1670
1671 default: AssertFailedBreak(); /* Shut up MSC. */
1672 }
1673 break;
1674 }
1675 }
1676
1677 Assert( *aParavirtProvider == ParavirtProvider_None
1678 || *aParavirtProvider == ParavirtProvider_Minimal
1679 || *aParavirtProvider == ParavirtProvider_HyperV
1680 || *aParavirtProvider == ParavirtProvider_KVM);
1681 return S_OK;
1682}
1683
1684HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1685{
1686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1687
1688 aHardwareVersion = mHWData->mHWVersion;
1689
1690 return S_OK;
1691}
1692
1693HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1694{
1695 /* check known version */
1696 Utf8Str hwVersion = aHardwareVersion;
1697 if ( hwVersion.compare("1") != 0
1698 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1699 return setError(E_INVALIDARG,
1700 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1701
1702 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1703
1704 HRESULT rc = i_checkStateDependency(MutableStateDep);
1705 if (FAILED(rc)) return rc;
1706
1707 i_setModified(IsModified_MachineData);
1708 mHWData.backup();
1709 mHWData->mHWVersion = aHardwareVersion;
1710
1711 return S_OK;
1712}
1713
1714HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1715{
1716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1717
1718 if (!mHWData->mHardwareUUID.isZero())
1719 aHardwareUUID = mHWData->mHardwareUUID;
1720 else
1721 aHardwareUUID = mData->mUuid;
1722
1723 return S_OK;
1724}
1725
1726HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1727{
1728 if (!aHardwareUUID.isValid())
1729 return E_INVALIDARG;
1730
1731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1732
1733 HRESULT rc = i_checkStateDependency(MutableStateDep);
1734 if (FAILED(rc)) return rc;
1735
1736 i_setModified(IsModified_MachineData);
1737 mHWData.backup();
1738 if (aHardwareUUID == mData->mUuid)
1739 mHWData->mHardwareUUID.clear();
1740 else
1741 mHWData->mHardwareUUID = aHardwareUUID;
1742
1743 return S_OK;
1744}
1745
1746HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1747{
1748 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1749
1750 *aMemorySize = mHWData->mMemorySize;
1751
1752 return S_OK;
1753}
1754
1755HRESULT Machine::setMemorySize(ULONG aMemorySize)
1756{
1757 /* check RAM limits */
1758 if ( aMemorySize < MM_RAM_MIN_IN_MB
1759 || aMemorySize > MM_RAM_MAX_IN_MB
1760 )
1761 return setError(E_INVALIDARG,
1762 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1763 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1764
1765 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1766
1767 HRESULT rc = i_checkStateDependency(MutableStateDep);
1768 if (FAILED(rc)) return rc;
1769
1770 i_setModified(IsModified_MachineData);
1771 mHWData.backup();
1772 mHWData->mMemorySize = aMemorySize;
1773
1774 return S_OK;
1775}
1776
1777HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1778{
1779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1780
1781 *aCPUCount = mHWData->mCPUCount;
1782
1783 return S_OK;
1784}
1785
1786HRESULT Machine::setCPUCount(ULONG aCPUCount)
1787{
1788 /* check CPU limits */
1789 if ( aCPUCount < SchemaDefs::MinCPUCount
1790 || aCPUCount > SchemaDefs::MaxCPUCount
1791 )
1792 return setError(E_INVALIDARG,
1793 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1794 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1795
1796 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1797
1798 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1799 if (mHWData->mCPUHotPlugEnabled)
1800 {
1801 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1802 {
1803 if (mHWData->mCPUAttached[idx])
1804 return setError(E_INVALIDARG,
1805 tr("There is still a CPU attached to socket %lu."
1806 "Detach the CPU before removing the socket"),
1807 aCPUCount, idx+1);
1808 }
1809 }
1810
1811 HRESULT rc = i_checkStateDependency(MutableStateDep);
1812 if (FAILED(rc)) return rc;
1813
1814 i_setModified(IsModified_MachineData);
1815 mHWData.backup();
1816 mHWData->mCPUCount = aCPUCount;
1817
1818 return S_OK;
1819}
1820
1821HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1822{
1823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1824
1825 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1826
1827 return S_OK;
1828}
1829
1830HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1831{
1832 HRESULT rc = S_OK;
1833
1834 /* check throttle limits */
1835 if ( aCPUExecutionCap < 1
1836 || aCPUExecutionCap > 100
1837 )
1838 return setError(E_INVALIDARG,
1839 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1840 aCPUExecutionCap, 1, 100);
1841
1842 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1843
1844 rc = i_checkStateDependency(MutableOrRunningStateDep);
1845 if (FAILED(rc)) return rc;
1846
1847 alock.release();
1848 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1849 alock.acquire();
1850 if (FAILED(rc)) return rc;
1851
1852 i_setModified(IsModified_MachineData);
1853 mHWData.backup();
1854 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1855
1856 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1857 if (Global::IsOnline(mData->mMachineState))
1858 i_saveSettings(NULL, alock);
1859
1860 return S_OK;
1861}
1862
1863HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1864{
1865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1866
1867 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1868
1869 return S_OK;
1870}
1871
1872HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1873{
1874 HRESULT rc = S_OK;
1875
1876 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1877
1878 rc = i_checkStateDependency(MutableStateDep);
1879 if (FAILED(rc)) return rc;
1880
1881 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1882 {
1883 if (aCPUHotPlugEnabled)
1884 {
1885 i_setModified(IsModified_MachineData);
1886 mHWData.backup();
1887
1888 /* Add the amount of CPUs currently attached */
1889 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1890 mHWData->mCPUAttached[i] = true;
1891 }
1892 else
1893 {
1894 /*
1895 * We can disable hotplug only if the amount of maximum CPUs is equal
1896 * to the amount of attached CPUs
1897 */
1898 unsigned cCpusAttached = 0;
1899 unsigned iHighestId = 0;
1900
1901 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1902 {
1903 if (mHWData->mCPUAttached[i])
1904 {
1905 cCpusAttached++;
1906 iHighestId = i;
1907 }
1908 }
1909
1910 if ( (cCpusAttached != mHWData->mCPUCount)
1911 || (iHighestId >= mHWData->mCPUCount))
1912 return setError(E_INVALIDARG,
1913 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1914
1915 i_setModified(IsModified_MachineData);
1916 mHWData.backup();
1917 }
1918 }
1919
1920 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1921
1922 return rc;
1923}
1924
1925HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1926{
1927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1928
1929 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1930
1931 return S_OK;
1932}
1933
1934HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1935{
1936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1937
1938 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1939 if (SUCCEEDED(hrc))
1940 {
1941 i_setModified(IsModified_MachineData);
1942 mHWData.backup();
1943 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1944 }
1945 return hrc;
1946}
1947
1948HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1949{
1950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1951 aCPUProfile = mHWData->mCpuProfile;
1952 return S_OK;
1953}
1954
1955HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1956{
1957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1958 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1959 if (SUCCEEDED(hrc))
1960 {
1961 i_setModified(IsModified_MachineData);
1962 mHWData.backup();
1963 /* Empty equals 'host'. */
1964 if (aCPUProfile.isNotEmpty())
1965 mHWData->mCpuProfile = aCPUProfile;
1966 else
1967 mHWData->mCpuProfile = "host";
1968 }
1969 return hrc;
1970}
1971
1972HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1973{
1974#ifdef VBOX_WITH_USB_CARDREADER
1975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1976
1977 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1978
1979 return S_OK;
1980#else
1981 NOREF(aEmulatedUSBCardReaderEnabled);
1982 return E_NOTIMPL;
1983#endif
1984}
1985
1986HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1987{
1988#ifdef VBOX_WITH_USB_CARDREADER
1989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1990
1991 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1992 if (FAILED(rc)) return rc;
1993
1994 i_setModified(IsModified_MachineData);
1995 mHWData.backup();
1996 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1997
1998 return S_OK;
1999#else
2000 NOREF(aEmulatedUSBCardReaderEnabled);
2001 return E_NOTIMPL;
2002#endif
2003}
2004
2005HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
2006{
2007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2008
2009 *aHPETEnabled = mHWData->mHPETEnabled;
2010
2011 return S_OK;
2012}
2013
2014HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
2015{
2016 HRESULT rc = S_OK;
2017
2018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2019
2020 rc = i_checkStateDependency(MutableStateDep);
2021 if (FAILED(rc)) return rc;
2022
2023 i_setModified(IsModified_MachineData);
2024 mHWData.backup();
2025
2026 mHWData->mHPETEnabled = aHPETEnabled;
2027
2028 return rc;
2029}
2030
2031/** @todo this method should not be public */
2032HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2033{
2034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2035
2036 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2037
2038 return S_OK;
2039}
2040
2041/**
2042 * Set the memory balloon size.
2043 *
2044 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2045 * we have to make sure that we never call IGuest from here.
2046 */
2047HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2048{
2049 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2050#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2051 /* check limits */
2052 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2053 return setError(E_INVALIDARG,
2054 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2055 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2056
2057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2058
2059 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
2060 if (FAILED(rc)) return rc;
2061
2062 i_setModified(IsModified_MachineData);
2063 mHWData.backup();
2064 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2065
2066 return S_OK;
2067#else
2068 NOREF(aMemoryBalloonSize);
2069 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2070#endif
2071}
2072
2073HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2074{
2075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2076
2077 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2078 return S_OK;
2079}
2080
2081HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2082{
2083#ifdef VBOX_WITH_PAGE_SHARING
2084 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2085
2086 HRESULT rc = i_checkStateDependency(MutableStateDep);
2087 if (FAILED(rc)) return rc;
2088
2089 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2090 i_setModified(IsModified_MachineData);
2091 mHWData.backup();
2092 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2093 return S_OK;
2094#else
2095 NOREF(aPageFusionEnabled);
2096 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2097#endif
2098}
2099
2100HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2101{
2102 /* mBIOSSettings is constant during life time, no need to lock */
2103 aBIOSSettings = mBIOSSettings;
2104
2105 return S_OK;
2106}
2107
2108HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
2109{
2110 /* mTrustedPlatformModule is constant during life time, no need to lock */
2111 aTrustedPlatformModule = mTrustedPlatformModule;
2112
2113 return S_OK;
2114}
2115
2116HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
2117{
2118 /* mNvramStore is constant during life time, no need to lock */
2119 aNvramStore = mNvramStore;
2120
2121 return S_OK;
2122}
2123
2124HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
2125{
2126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2127
2128 aRecordingSettings = mRecordingSettings;
2129
2130 return S_OK;
2131}
2132
2133HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
2134{
2135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2136
2137 aGraphicsAdapter = mGraphicsAdapter;
2138
2139 return S_OK;
2140}
2141
2142HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2143{
2144 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2145
2146 switch (aProperty)
2147 {
2148 case CPUPropertyType_PAE:
2149 *aValue = mHWData->mPAEEnabled;
2150 break;
2151
2152 case CPUPropertyType_LongMode:
2153 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2154 *aValue = TRUE;
2155 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2156 *aValue = FALSE;
2157#if HC_ARCH_BITS == 64
2158 else
2159 *aValue = TRUE;
2160#else
2161 else
2162 {
2163 *aValue = FALSE;
2164
2165 ComObjPtr<GuestOSType> pGuestOSType;
2166 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2167 pGuestOSType);
2168 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
2169 {
2170 if (pGuestOSType->i_is64Bit())
2171 {
2172 ComObjPtr<Host> pHost = mParent->i_host();
2173 alock.release();
2174
2175 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2176 if (FAILED(hrc2))
2177 *aValue = FALSE;
2178 }
2179 }
2180 }
2181#endif
2182 break;
2183
2184 case CPUPropertyType_TripleFaultReset:
2185 *aValue = mHWData->mTripleFaultReset;
2186 break;
2187
2188 case CPUPropertyType_APIC:
2189 *aValue = mHWData->mAPIC;
2190 break;
2191
2192 case CPUPropertyType_X2APIC:
2193 *aValue = mHWData->mX2APIC;
2194 break;
2195
2196 case CPUPropertyType_IBPBOnVMExit:
2197 *aValue = mHWData->mIBPBOnVMExit;
2198 break;
2199
2200 case CPUPropertyType_IBPBOnVMEntry:
2201 *aValue = mHWData->mIBPBOnVMEntry;
2202 break;
2203
2204 case CPUPropertyType_SpecCtrl:
2205 *aValue = mHWData->mSpecCtrl;
2206 break;
2207
2208 case CPUPropertyType_SpecCtrlByHost:
2209 *aValue = mHWData->mSpecCtrlByHost;
2210 break;
2211
2212 case CPUPropertyType_HWVirt:
2213 *aValue = mHWData->mNestedHWVirt;
2214 break;
2215
2216 case CPUPropertyType_L1DFlushOnEMTScheduling:
2217 *aValue = mHWData->mL1DFlushOnSched;
2218 break;
2219
2220 case CPUPropertyType_L1DFlushOnVMEntry:
2221 *aValue = mHWData->mL1DFlushOnVMEntry;
2222 break;
2223
2224 case CPUPropertyType_MDSClearOnEMTScheduling:
2225 *aValue = mHWData->mMDSClearOnSched;
2226 break;
2227
2228 case CPUPropertyType_MDSClearOnVMEntry:
2229 *aValue = mHWData->mMDSClearOnVMEntry;
2230 break;
2231
2232 default:
2233 return E_INVALIDARG;
2234 }
2235 return S_OK;
2236}
2237
2238HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2239{
2240 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2241
2242 HRESULT rc = i_checkStateDependency(MutableStateDep);
2243 if (FAILED(rc)) return rc;
2244
2245 switch (aProperty)
2246 {
2247 case CPUPropertyType_PAE:
2248 i_setModified(IsModified_MachineData);
2249 mHWData.backup();
2250 mHWData->mPAEEnabled = !!aValue;
2251 break;
2252
2253 case CPUPropertyType_LongMode:
2254 i_setModified(IsModified_MachineData);
2255 mHWData.backup();
2256 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2257 break;
2258
2259 case CPUPropertyType_TripleFaultReset:
2260 i_setModified(IsModified_MachineData);
2261 mHWData.backup();
2262 mHWData->mTripleFaultReset = !!aValue;
2263 break;
2264
2265 case CPUPropertyType_APIC:
2266 if (mHWData->mX2APIC)
2267 aValue = TRUE;
2268 i_setModified(IsModified_MachineData);
2269 mHWData.backup();
2270 mHWData->mAPIC = !!aValue;
2271 break;
2272
2273 case CPUPropertyType_X2APIC:
2274 i_setModified(IsModified_MachineData);
2275 mHWData.backup();
2276 mHWData->mX2APIC = !!aValue;
2277 if (aValue)
2278 mHWData->mAPIC = !!aValue;
2279 break;
2280
2281 case CPUPropertyType_IBPBOnVMExit:
2282 i_setModified(IsModified_MachineData);
2283 mHWData.backup();
2284 mHWData->mIBPBOnVMExit = !!aValue;
2285 break;
2286
2287 case CPUPropertyType_IBPBOnVMEntry:
2288 i_setModified(IsModified_MachineData);
2289 mHWData.backup();
2290 mHWData->mIBPBOnVMEntry = !!aValue;
2291 break;
2292
2293 case CPUPropertyType_SpecCtrl:
2294 i_setModified(IsModified_MachineData);
2295 mHWData.backup();
2296 mHWData->mSpecCtrl = !!aValue;
2297 break;
2298
2299 case CPUPropertyType_SpecCtrlByHost:
2300 i_setModified(IsModified_MachineData);
2301 mHWData.backup();
2302 mHWData->mSpecCtrlByHost = !!aValue;
2303 break;
2304
2305 case CPUPropertyType_HWVirt:
2306 i_setModified(IsModified_MachineData);
2307 mHWData.backup();
2308 mHWData->mNestedHWVirt = !!aValue;
2309 break;
2310
2311 case CPUPropertyType_L1DFlushOnEMTScheduling:
2312 i_setModified(IsModified_MachineData);
2313 mHWData.backup();
2314 mHWData->mL1DFlushOnSched = !!aValue;
2315 break;
2316
2317 case CPUPropertyType_L1DFlushOnVMEntry:
2318 i_setModified(IsModified_MachineData);
2319 mHWData.backup();
2320 mHWData->mL1DFlushOnVMEntry = !!aValue;
2321 break;
2322
2323 case CPUPropertyType_MDSClearOnEMTScheduling:
2324 i_setModified(IsModified_MachineData);
2325 mHWData.backup();
2326 mHWData->mMDSClearOnSched = !!aValue;
2327 break;
2328
2329 case CPUPropertyType_MDSClearOnVMEntry:
2330 i_setModified(IsModified_MachineData);
2331 mHWData.backup();
2332 mHWData->mMDSClearOnVMEntry = !!aValue;
2333 break;
2334
2335 default:
2336 return E_INVALIDARG;
2337 }
2338 return S_OK;
2339}
2340
2341HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2342 ULONG *aValEcx, ULONG *aValEdx)
2343{
2344 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2345 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2346 {
2347 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2348 it != mHWData->mCpuIdLeafList.end();
2349 ++it)
2350 {
2351 if (aOrdinal == 0)
2352 {
2353 const settings::CpuIdLeaf &rLeaf= *it;
2354 *aIdx = rLeaf.idx;
2355 *aSubIdx = rLeaf.idxSub;
2356 *aValEax = rLeaf.uEax;
2357 *aValEbx = rLeaf.uEbx;
2358 *aValEcx = rLeaf.uEcx;
2359 *aValEdx = rLeaf.uEdx;
2360 return S_OK;
2361 }
2362 aOrdinal--;
2363 }
2364 }
2365 return E_INVALIDARG;
2366}
2367
2368HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2369{
2370 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2371
2372 /*
2373 * Search the list.
2374 */
2375 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2376 {
2377 const settings::CpuIdLeaf &rLeaf= *it;
2378 if ( rLeaf.idx == aIdx
2379 && ( aSubIdx == UINT32_MAX
2380 || rLeaf.idxSub == aSubIdx) )
2381 {
2382 *aValEax = rLeaf.uEax;
2383 *aValEbx = rLeaf.uEbx;
2384 *aValEcx = rLeaf.uEcx;
2385 *aValEdx = rLeaf.uEdx;
2386 return S_OK;
2387 }
2388 }
2389
2390 return E_INVALIDARG;
2391}
2392
2393
2394HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2395{
2396 /*
2397 * Validate input before taking locks and checking state.
2398 */
2399 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2400 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2401 if ( aIdx >= UINT32_C(0x20)
2402 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2403 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2404 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2405
2406 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2407 HRESULT rc = i_checkStateDependency(MutableStateDep);
2408 if (FAILED(rc)) return rc;
2409
2410 /*
2411 * Impose a maximum number of leaves.
2412 */
2413 if (mHWData->mCpuIdLeafList.size() > 256)
2414 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2415
2416 /*
2417 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2418 */
2419 i_setModified(IsModified_MachineData);
2420 mHWData.backup();
2421
2422 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2423 {
2424 settings::CpuIdLeaf &rLeaf= *it;
2425 if ( rLeaf.idx == aIdx
2426 && ( aSubIdx == UINT32_MAX
2427 || rLeaf.idxSub == aSubIdx) )
2428 it = mHWData->mCpuIdLeafList.erase(it);
2429 else
2430 ++it;
2431 }
2432
2433 settings::CpuIdLeaf NewLeaf;
2434 NewLeaf.idx = aIdx;
2435 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2436 NewLeaf.uEax = aValEax;
2437 NewLeaf.uEbx = aValEbx;
2438 NewLeaf.uEcx = aValEcx;
2439 NewLeaf.uEdx = aValEdx;
2440 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2441 return S_OK;
2442}
2443
2444HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2445{
2446 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2447
2448 HRESULT rc = i_checkStateDependency(MutableStateDep);
2449 if (FAILED(rc)) return rc;
2450
2451 /*
2452 * Do the removal.
2453 */
2454 bool fModified = mHWData.isBackedUp();
2455 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2456 {
2457 settings::CpuIdLeaf &rLeaf= *it;
2458 if ( rLeaf.idx == aIdx
2459 && ( aSubIdx == UINT32_MAX
2460 || rLeaf.idxSub == aSubIdx) )
2461 {
2462 if (!fModified)
2463 {
2464 fModified = true;
2465 i_setModified(IsModified_MachineData);
2466 mHWData.backup();
2467 // Start from the beginning, since mHWData.backup() creates
2468 // a new list, causing iterator mixup. This makes sure that
2469 // the settings are not unnecessarily marked as modified,
2470 // at the price of extra list walking.
2471 it = mHWData->mCpuIdLeafList.begin();
2472 }
2473 else
2474 it = mHWData->mCpuIdLeafList.erase(it);
2475 }
2476 else
2477 ++it;
2478 }
2479
2480 return S_OK;
2481}
2482
2483HRESULT Machine::removeAllCPUIDLeaves()
2484{
2485 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2486
2487 HRESULT rc = i_checkStateDependency(MutableStateDep);
2488 if (FAILED(rc)) return rc;
2489
2490 if (mHWData->mCpuIdLeafList.size() > 0)
2491 {
2492 i_setModified(IsModified_MachineData);
2493 mHWData.backup();
2494
2495 mHWData->mCpuIdLeafList.clear();
2496 }
2497
2498 return S_OK;
2499}
2500HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2501{
2502 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2503
2504 switch(aProperty)
2505 {
2506 case HWVirtExPropertyType_Enabled:
2507 *aValue = mHWData->mHWVirtExEnabled;
2508 break;
2509
2510 case HWVirtExPropertyType_VPID:
2511 *aValue = mHWData->mHWVirtExVPIDEnabled;
2512 break;
2513
2514 case HWVirtExPropertyType_NestedPaging:
2515 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2516 break;
2517
2518 case HWVirtExPropertyType_UnrestrictedExecution:
2519 *aValue = mHWData->mHWVirtExUXEnabled;
2520 break;
2521
2522 case HWVirtExPropertyType_LargePages:
2523 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2524 break;
2525
2526 case HWVirtExPropertyType_Force:
2527 *aValue = mHWData->mHWVirtExForceEnabled;
2528 break;
2529
2530 case HWVirtExPropertyType_UseNativeApi:
2531 *aValue = mHWData->mHWVirtExUseNativeApi;
2532 break;
2533
2534 case HWVirtExPropertyType_VirtVmsaveVmload:
2535 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2536 break;
2537
2538 default:
2539 return E_INVALIDARG;
2540 }
2541 return S_OK;
2542}
2543
2544HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2545{
2546 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2547
2548 HRESULT rc = i_checkStateDependency(MutableStateDep);
2549 if (FAILED(rc)) return rc;
2550
2551 switch (aProperty)
2552 {
2553 case HWVirtExPropertyType_Enabled:
2554 i_setModified(IsModified_MachineData);
2555 mHWData.backup();
2556 mHWData->mHWVirtExEnabled = !!aValue;
2557 break;
2558
2559 case HWVirtExPropertyType_VPID:
2560 i_setModified(IsModified_MachineData);
2561 mHWData.backup();
2562 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2563 break;
2564
2565 case HWVirtExPropertyType_NestedPaging:
2566 i_setModified(IsModified_MachineData);
2567 mHWData.backup();
2568 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2569 break;
2570
2571 case HWVirtExPropertyType_UnrestrictedExecution:
2572 i_setModified(IsModified_MachineData);
2573 mHWData.backup();
2574 mHWData->mHWVirtExUXEnabled = !!aValue;
2575 break;
2576
2577 case HWVirtExPropertyType_LargePages:
2578 i_setModified(IsModified_MachineData);
2579 mHWData.backup();
2580 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2581 break;
2582
2583 case HWVirtExPropertyType_Force:
2584 i_setModified(IsModified_MachineData);
2585 mHWData.backup();
2586 mHWData->mHWVirtExForceEnabled = !!aValue;
2587 break;
2588
2589 case HWVirtExPropertyType_UseNativeApi:
2590 i_setModified(IsModified_MachineData);
2591 mHWData.backup();
2592 mHWData->mHWVirtExUseNativeApi = !!aValue;
2593 break;
2594
2595 case HWVirtExPropertyType_VirtVmsaveVmload:
2596 i_setModified(IsModified_MachineData);
2597 mHWData.backup();
2598 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2599 break;
2600
2601 default:
2602 return E_INVALIDARG;
2603 }
2604
2605 return S_OK;
2606}
2607
2608HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2609{
2610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2611
2612 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2613
2614 return S_OK;
2615}
2616
2617HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2618{
2619 /** @todo (r=dmik):
2620 * 1. Allow to change the name of the snapshot folder containing snapshots
2621 * 2. Rename the folder on disk instead of just changing the property
2622 * value (to be smart and not to leave garbage). Note that it cannot be
2623 * done here because the change may be rolled back. Thus, the right
2624 * place is #saveSettings().
2625 */
2626
2627 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2628
2629 HRESULT rc = i_checkStateDependency(MutableStateDep);
2630 if (FAILED(rc)) return rc;
2631
2632 if (!mData->mCurrentSnapshot.isNull())
2633 return setError(E_FAIL,
2634 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2635
2636 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2637
2638 if (strSnapshotFolder.isEmpty())
2639 strSnapshotFolder = "Snapshots";
2640 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2641 if (RT_FAILURE(vrc))
2642 return setErrorBoth(E_FAIL, vrc,
2643 tr("Invalid snapshot folder '%s' (%Rrc)"),
2644 strSnapshotFolder.c_str(), vrc);
2645
2646 i_setModified(IsModified_MachineData);
2647 mUserData.backup();
2648
2649 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2650
2651 return S_OK;
2652}
2653
2654HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2655{
2656 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2657
2658 aMediumAttachments.resize(mMediumAttachments->size());
2659 size_t i = 0;
2660 for (MediumAttachmentList::const_iterator
2661 it = mMediumAttachments->begin();
2662 it != mMediumAttachments->end();
2663 ++it, ++i)
2664 aMediumAttachments[i] = *it;
2665
2666 return S_OK;
2667}
2668
2669HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2670{
2671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2672
2673 Assert(!!mVRDEServer);
2674
2675 aVRDEServer = mVRDEServer;
2676
2677 return S_OK;
2678}
2679
2680HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2681{
2682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2683
2684 aAudioAdapter = mAudioAdapter;
2685
2686 return S_OK;
2687}
2688
2689HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2690{
2691#ifdef VBOX_WITH_VUSB
2692 clearError();
2693 MultiResult rc(S_OK);
2694
2695# ifdef VBOX_WITH_USB
2696 rc = mParent->i_host()->i_checkUSBProxyService();
2697 if (FAILED(rc)) return rc;
2698# endif
2699
2700 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2701
2702 aUSBControllers.resize(mUSBControllers->size());
2703 size_t i = 0;
2704 for (USBControllerList::const_iterator
2705 it = mUSBControllers->begin();
2706 it != mUSBControllers->end();
2707 ++it, ++i)
2708 aUSBControllers[i] = *it;
2709
2710 return S_OK;
2711#else
2712 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2713 * extended error info to indicate that USB is simply not available
2714 * (w/o treating it as a failure), for example, as in OSE */
2715 NOREF(aUSBControllers);
2716 ReturnComNotImplemented();
2717#endif /* VBOX_WITH_VUSB */
2718}
2719
2720HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2721{
2722#ifdef VBOX_WITH_VUSB
2723 clearError();
2724 MultiResult rc(S_OK);
2725
2726# ifdef VBOX_WITH_USB
2727 rc = mParent->i_host()->i_checkUSBProxyService();
2728 if (FAILED(rc)) return rc;
2729# endif
2730
2731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2732
2733 aUSBDeviceFilters = mUSBDeviceFilters;
2734 return rc;
2735#else
2736 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2737 * extended error info to indicate that USB is simply not available
2738 * (w/o treating it as a failure), for example, as in OSE */
2739 NOREF(aUSBDeviceFilters);
2740 ReturnComNotImplemented();
2741#endif /* VBOX_WITH_VUSB */
2742}
2743
2744HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2745{
2746 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2747
2748 aSettingsFilePath = mData->m_strConfigFileFull;
2749
2750 return S_OK;
2751}
2752
2753HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2754{
2755 RT_NOREF(aSettingsFilePath);
2756 ReturnComNotImplemented();
2757}
2758
2759HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2760{
2761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2762
2763 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2764 if (FAILED(rc)) return rc;
2765
2766 if (!mData->pMachineConfigFile->fileExists())
2767 // this is a new machine, and no config file exists yet:
2768 *aSettingsModified = TRUE;
2769 else
2770 *aSettingsModified = (mData->flModifications != 0);
2771
2772 return S_OK;
2773}
2774
2775HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2776{
2777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2778
2779 *aSessionState = mData->mSession.mState;
2780
2781 return S_OK;
2782}
2783
2784HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2785{
2786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2787
2788 aSessionName = mData->mSession.mName;
2789
2790 return S_OK;
2791}
2792
2793HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2794{
2795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2796
2797 *aSessionPID = mData->mSession.mPID;
2798
2799 return S_OK;
2800}
2801
2802HRESULT Machine::getState(MachineState_T *aState)
2803{
2804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2805
2806 *aState = mData->mMachineState;
2807 Assert(mData->mMachineState != MachineState_Null);
2808
2809 return S_OK;
2810}
2811
2812HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2813{
2814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2815
2816 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2817
2818 return S_OK;
2819}
2820
2821HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2822{
2823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2824
2825 aStateFilePath = mSSData->strStateFilePath;
2826
2827 return S_OK;
2828}
2829
2830HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2831{
2832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2833
2834 i_getLogFolder(aLogFolder);
2835
2836 return S_OK;
2837}
2838
2839HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2840{
2841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2842
2843 aCurrentSnapshot = mData->mCurrentSnapshot;
2844
2845 return S_OK;
2846}
2847
2848HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2849{
2850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2851
2852 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2853 ? 0
2854 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2855
2856 return S_OK;
2857}
2858
2859HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2860{
2861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2862
2863 /* Note: for machines with no snapshots, we always return FALSE
2864 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2865 * reasons :) */
2866
2867 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2868 ? FALSE
2869 : mData->mCurrentStateModified;
2870
2871 return S_OK;
2872}
2873
2874HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2875{
2876 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2877
2878 aSharedFolders.resize(mHWData->mSharedFolders.size());
2879 size_t i = 0;
2880 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2881 it = mHWData->mSharedFolders.begin();
2882 it != mHWData->mSharedFolders.end();
2883 ++it, ++i)
2884 aSharedFolders[i] = *it;
2885
2886 return S_OK;
2887}
2888
2889HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2890{
2891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2892
2893 *aClipboardMode = mHWData->mClipboardMode;
2894
2895 return S_OK;
2896}
2897
2898HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2899{
2900 HRESULT rc = S_OK;
2901
2902 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2903
2904 rc = i_checkStateDependency(MutableOrRunningStateDep);
2905 if (FAILED(rc)) return rc;
2906
2907 alock.release();
2908 rc = i_onClipboardModeChange(aClipboardMode);
2909 alock.acquire();
2910 if (FAILED(rc)) return rc;
2911
2912 i_setModified(IsModified_MachineData);
2913 mHWData.backup();
2914 mHWData->mClipboardMode = aClipboardMode;
2915
2916 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2917 if (Global::IsOnline(mData->mMachineState))
2918 i_saveSettings(NULL, alock);
2919
2920 return S_OK;
2921}
2922
2923HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2924{
2925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2926
2927 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2928
2929 return S_OK;
2930}
2931
2932HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2933{
2934 HRESULT rc = S_OK;
2935
2936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2937
2938 rc = i_checkStateDependency(MutableOrRunningStateDep);
2939 if (FAILED(rc)) return rc;
2940
2941 alock.release();
2942 rc = i_onClipboardFileTransferModeChange(aEnabled);
2943 alock.acquire();
2944 if (FAILED(rc)) return rc;
2945
2946 i_setModified(IsModified_MachineData);
2947 mHWData.backup();
2948 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2949
2950 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2951 if (Global::IsOnline(mData->mMachineState))
2952 i_saveSettings(NULL, alock);
2953
2954 return S_OK;
2955}
2956
2957HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2958{
2959 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2960
2961 *aDnDMode = mHWData->mDnDMode;
2962
2963 return S_OK;
2964}
2965
2966HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2967{
2968 HRESULT rc = S_OK;
2969
2970 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2971
2972 rc = i_checkStateDependency(MutableOrRunningStateDep);
2973 if (FAILED(rc)) return rc;
2974
2975 alock.release();
2976 rc = i_onDnDModeChange(aDnDMode);
2977
2978 alock.acquire();
2979 if (FAILED(rc)) return rc;
2980
2981 i_setModified(IsModified_MachineData);
2982 mHWData.backup();
2983 mHWData->mDnDMode = aDnDMode;
2984
2985 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2986 if (Global::IsOnline(mData->mMachineState))
2987 i_saveSettings(NULL, alock);
2988
2989 return S_OK;
2990}
2991
2992HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2993{
2994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2995
2996 aStorageControllers.resize(mStorageControllers->size());
2997 size_t i = 0;
2998 for (StorageControllerList::const_iterator
2999 it = mStorageControllers->begin();
3000 it != mStorageControllers->end();
3001 ++it, ++i)
3002 aStorageControllers[i] = *it;
3003
3004 return S_OK;
3005}
3006
3007HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
3008{
3009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3010
3011 *aEnabled = mUserData->s.fTeleporterEnabled;
3012
3013 return S_OK;
3014}
3015
3016HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
3017{
3018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3019
3020 /* Only allow it to be set to true when PoweredOff or Aborted.
3021 (Clearing it is always permitted.) */
3022 if ( aTeleporterEnabled
3023 && mData->mRegistered
3024 && ( !i_isSessionMachine()
3025 || ( mData->mMachineState != MachineState_PoweredOff
3026 && mData->mMachineState != MachineState_Teleported
3027 && mData->mMachineState != MachineState_Aborted
3028 )
3029 )
3030 )
3031 return setError(VBOX_E_INVALID_VM_STATE,
3032 tr("The machine is not powered off (state is %s)"),
3033 Global::stringifyMachineState(mData->mMachineState));
3034
3035 i_setModified(IsModified_MachineData);
3036 mUserData.backup();
3037 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
3038
3039 return S_OK;
3040}
3041
3042HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
3043{
3044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3045
3046 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
3047
3048 return S_OK;
3049}
3050
3051HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
3052{
3053 if (aTeleporterPort >= _64K)
3054 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
3055
3056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3057
3058 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3059 if (FAILED(rc)) return rc;
3060
3061 i_setModified(IsModified_MachineData);
3062 mUserData.backup();
3063 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3064
3065 return S_OK;
3066}
3067
3068HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3069{
3070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3071
3072 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3073
3074 return S_OK;
3075}
3076
3077HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3078{
3079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3080
3081 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3082 if (FAILED(rc)) return rc;
3083
3084 i_setModified(IsModified_MachineData);
3085 mUserData.backup();
3086 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3087
3088 return S_OK;
3089}
3090
3091HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3092{
3093 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3094 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3095
3096 return S_OK;
3097}
3098
3099HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3100{
3101 /*
3102 * Hash the password first.
3103 */
3104 com::Utf8Str aT = aTeleporterPassword;
3105
3106 if (!aT.isEmpty())
3107 {
3108 if (VBoxIsPasswordHashed(&aT))
3109 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3110 VBoxHashPassword(&aT);
3111 }
3112
3113 /*
3114 * Do the update.
3115 */
3116 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3117 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3118 if (SUCCEEDED(hrc))
3119 {
3120 i_setModified(IsModified_MachineData);
3121 mUserData.backup();
3122 mUserData->s.strTeleporterPassword = aT;
3123 }
3124
3125 return hrc;
3126}
3127
3128HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3129{
3130 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3131
3132 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3133
3134 return S_OK;
3135}
3136
3137HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3138{
3139 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3140
3141 /* Only allow it to be set to true when PoweredOff or Aborted.
3142 (Clearing it is always permitted.) */
3143 if ( aRTCUseUTC
3144 && mData->mRegistered
3145 && ( !i_isSessionMachine()
3146 || ( mData->mMachineState != MachineState_PoweredOff
3147 && mData->mMachineState != MachineState_Teleported
3148 && mData->mMachineState != MachineState_Aborted
3149 )
3150 )
3151 )
3152 return setError(VBOX_E_INVALID_VM_STATE,
3153 tr("The machine is not powered off (state is %s)"),
3154 Global::stringifyMachineState(mData->mMachineState));
3155
3156 i_setModified(IsModified_MachineData);
3157 mUserData.backup();
3158 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3159
3160 return S_OK;
3161}
3162
3163HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3164{
3165 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3166
3167 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3168
3169 return S_OK;
3170}
3171
3172HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3173{
3174 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3175
3176 HRESULT rc = i_checkStateDependency(MutableStateDep);
3177 if (FAILED(rc)) return rc;
3178
3179 i_setModified(IsModified_MachineData);
3180 mHWData.backup();
3181 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3182
3183 return S_OK;
3184}
3185
3186HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3187{
3188 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3189
3190 *aIOCacheSize = mHWData->mIOCacheSize;
3191
3192 return S_OK;
3193}
3194
3195HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3196{
3197 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3198
3199 HRESULT rc = i_checkStateDependency(MutableStateDep);
3200 if (FAILED(rc)) return rc;
3201
3202 i_setModified(IsModified_MachineData);
3203 mHWData.backup();
3204 mHWData->mIOCacheSize = aIOCacheSize;
3205
3206 return S_OK;
3207}
3208
3209HRESULT Machine::getStateKeyId(com::Utf8Str &aKeyId)
3210{
3211 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3212
3213#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3214 aKeyId = mSSData->strStateKeyId;
3215#else
3216 aKeyId = com::Utf8Str::Empty;
3217#endif
3218
3219 return S_OK;
3220}
3221
3222HRESULT Machine::getStateKeyStore(com::Utf8Str &aKeyStore)
3223{
3224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3225
3226#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3227 aKeyStore = mSSData->strStateKeyStore;
3228#else
3229 aKeyStore = com::Utf8Str::Empty;
3230#endif
3231
3232 return S_OK;
3233}
3234
3235HRESULT Machine::getLogKeyId(com::Utf8Str &aKeyId)
3236{
3237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3238
3239#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3240 aKeyId = mData->mstrLogKeyId;
3241#else
3242 aKeyId = com::Utf8Str::Empty;
3243#endif
3244
3245 return S_OK;
3246}
3247
3248HRESULT Machine::getLogKeyStore(com::Utf8Str &aKeyStore)
3249{
3250 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3251
3252#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3253 aKeyStore = mData->mstrLogKeyStore;
3254#else
3255 aKeyStore = com::Utf8Str::Empty;
3256#endif
3257
3258 return S_OK;
3259}
3260
3261
3262/**
3263 * @note Locks objects!
3264 */
3265HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3266 LockType_T aLockType)
3267{
3268 /* check the session state */
3269 SessionState_T state;
3270 HRESULT rc = aSession->COMGETTER(State)(&state);
3271 if (FAILED(rc)) return rc;
3272
3273 if (state != SessionState_Unlocked)
3274 return setError(VBOX_E_INVALID_OBJECT_STATE,
3275 tr("The given session is busy"));
3276
3277 // get the client's IInternalSessionControl interface
3278 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3279 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
3280 E_INVALIDARG);
3281
3282 // session name (only used in some code paths)
3283 Utf8Str strSessionName;
3284
3285 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3286
3287 if (!mData->mRegistered)
3288 return setError(E_UNEXPECTED,
3289 tr("The machine '%s' is not registered"),
3290 mUserData->s.strName.c_str());
3291
3292 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3293
3294 SessionState_T oldState = mData->mSession.mState;
3295 /* Hack: in case the session is closing and there is a progress object
3296 * which allows waiting for the session to be closed, take the opportunity
3297 * and do a limited wait (max. 1 second). This helps a lot when the system
3298 * is busy and thus session closing can take a little while. */
3299 if ( mData->mSession.mState == SessionState_Unlocking
3300 && mData->mSession.mProgress)
3301 {
3302 alock.release();
3303 mData->mSession.mProgress->WaitForCompletion(1000);
3304 alock.acquire();
3305 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3306 }
3307
3308 // try again now
3309 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3310 // (i.e. session machine exists)
3311 && (aLockType == LockType_Shared) // caller wants a shared link to the
3312 // existing session that holds the write lock:
3313 )
3314 {
3315 // OK, share the session... we are now dealing with three processes:
3316 // 1) VBoxSVC (where this code runs);
3317 // 2) process C: the caller's client process (who wants a shared session);
3318 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3319
3320 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3321 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3322 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3323 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3324 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3325
3326 /*
3327 * Release the lock before calling the client process. It's safe here
3328 * since the only thing to do after we get the lock again is to add
3329 * the remote control to the list (which doesn't directly influence
3330 * anything).
3331 */
3332 alock.release();
3333
3334 // get the console of the session holding the write lock (this is a remote call)
3335 ComPtr<IConsole> pConsoleW;
3336 if (mData->mSession.mLockType == LockType_VM)
3337 {
3338 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3339 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3340 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3341 if (FAILED(rc))
3342 // the failure may occur w/o any error info (from RPC), so provide one
3343 return setError(VBOX_E_VM_ERROR,
3344 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3345 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3346 }
3347
3348 // share the session machine and W's console with the caller's session
3349 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3350 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3351 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3352
3353 if (FAILED(rc))
3354 // the failure may occur w/o any error info (from RPC), so provide one
3355 return setError(VBOX_E_VM_ERROR,
3356 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3357 alock.acquire();
3358
3359 // need to revalidate the state after acquiring the lock again
3360 if (mData->mSession.mState != SessionState_Locked)
3361 {
3362 pSessionControl->Uninitialize();
3363 return setError(VBOX_E_INVALID_SESSION_STATE,
3364 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3365 mUserData->s.strName.c_str());
3366 }
3367
3368 // add the caller's session to the list
3369 mData->mSession.mRemoteControls.push_back(pSessionControl);
3370 }
3371 else if ( mData->mSession.mState == SessionState_Locked
3372 || mData->mSession.mState == SessionState_Unlocking
3373 )
3374 {
3375 // sharing not permitted, or machine still unlocking:
3376 return setError(VBOX_E_INVALID_OBJECT_STATE,
3377 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3378 mUserData->s.strName.c_str());
3379 }
3380 else
3381 {
3382 // machine is not locked: then write-lock the machine (create the session machine)
3383
3384 // must not be busy
3385 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3386
3387 // get the caller's session PID
3388 RTPROCESS pid = NIL_RTPROCESS;
3389 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3390 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3391 Assert(pid != NIL_RTPROCESS);
3392
3393 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3394
3395 if (fLaunchingVMProcess)
3396 {
3397 if (mData->mSession.mPID == NIL_RTPROCESS)
3398 {
3399 // two or more clients racing for a lock, the one which set the
3400 // session state to Spawning will win, the others will get an
3401 // error as we can't decide here if waiting a little would help
3402 // (only for shared locks this would avoid an error)
3403 return setError(VBOX_E_INVALID_OBJECT_STATE,
3404 tr("The machine '%s' already has a lock request pending"),
3405 mUserData->s.strName.c_str());
3406 }
3407
3408 // this machine is awaiting for a spawning session to be opened:
3409 // then the calling process must be the one that got started by
3410 // LaunchVMProcess()
3411
3412 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3413 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3414
3415#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3416 /* Hardened windows builds spawns three processes when a VM is
3417 launched, the 3rd one is the one that will end up here. */
3418 RTPROCESS pidParent;
3419 int vrc = RTProcQueryParent(pid, &pidParent);
3420 if (RT_SUCCESS(vrc))
3421 vrc = RTProcQueryParent(pidParent, &pidParent);
3422 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3423 || vrc == VERR_ACCESS_DENIED)
3424 {
3425 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3426 mData->mSession.mPID = pid;
3427 }
3428#endif
3429
3430 if (mData->mSession.mPID != pid)
3431 return setError(E_ACCESSDENIED,
3432 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3433 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3434 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3435 }
3436
3437 // create the mutable SessionMachine from the current machine
3438 ComObjPtr<SessionMachine> sessionMachine;
3439 sessionMachine.createObject();
3440 rc = sessionMachine->init(this);
3441 AssertComRC(rc);
3442
3443 /* NOTE: doing return from this function after this point but
3444 * before the end is forbidden since it may call SessionMachine::uninit()
3445 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3446 * lock while still holding the Machine lock in alock so that a deadlock
3447 * is possible due to the wrong lock order. */
3448
3449 if (SUCCEEDED(rc))
3450 {
3451 /*
3452 * Set the session state to Spawning to protect against subsequent
3453 * attempts to open a session and to unregister the machine after
3454 * we release the lock.
3455 */
3456 SessionState_T origState = mData->mSession.mState;
3457 mData->mSession.mState = SessionState_Spawning;
3458
3459#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3460 /* Get the client token ID to be passed to the client process */
3461 Utf8Str strTokenId;
3462 sessionMachine->i_getTokenId(strTokenId);
3463 Assert(!strTokenId.isEmpty());
3464#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3465 /* Get the client token to be passed to the client process */
3466 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3467 /* The token is now "owned" by pToken, fix refcount */
3468 if (!pToken.isNull())
3469 pToken->Release();
3470#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3471
3472 /*
3473 * Release the lock before calling the client process -- it will call
3474 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3475 * because the state is Spawning, so that LaunchVMProcess() and
3476 * LockMachine() calls will fail. This method, called before we
3477 * acquire the lock again, will fail because of the wrong PID.
3478 *
3479 * Note that mData->mSession.mRemoteControls accessed outside
3480 * the lock may not be modified when state is Spawning, so it's safe.
3481 */
3482 alock.release();
3483
3484 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3485#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3486 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3487#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3488 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3489 /* Now the token is owned by the client process. */
3490 pToken.setNull();
3491#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3492 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3493
3494 /* The failure may occur w/o any error info (from RPC), so provide one */
3495 if (FAILED(rc))
3496 setError(VBOX_E_VM_ERROR,
3497 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3498
3499 // get session name, either to remember or to compare against
3500 // the already known session name.
3501 {
3502 Bstr bstrSessionName;
3503 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3504 if (SUCCEEDED(rc2))
3505 strSessionName = bstrSessionName;
3506 }
3507
3508 if ( SUCCEEDED(rc)
3509 && fLaunchingVMProcess
3510 )
3511 {
3512 /* complete the remote session initialization */
3513
3514 /* get the console from the direct session */
3515 ComPtr<IConsole> console;
3516 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3517 ComAssertComRC(rc);
3518
3519 if (SUCCEEDED(rc) && !console)
3520 {
3521 ComAssert(!!console);
3522 rc = E_FAIL;
3523 }
3524
3525 /* assign machine & console to the remote session */
3526 if (SUCCEEDED(rc))
3527 {
3528 /*
3529 * after LaunchVMProcess(), the first and the only
3530 * entry in remoteControls is that remote session
3531 */
3532 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3533 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3534 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3535
3536 /* The failure may occur w/o any error info (from RPC), so provide one */
3537 if (FAILED(rc))
3538 setError(VBOX_E_VM_ERROR,
3539 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3540 }
3541
3542 if (FAILED(rc))
3543 pSessionControl->Uninitialize();
3544 }
3545
3546 /* acquire the lock again */
3547 alock.acquire();
3548
3549 /* Restore the session state */
3550 mData->mSession.mState = origState;
3551 }
3552
3553 // finalize spawning anyway (this is why we don't return on errors above)
3554 if (fLaunchingVMProcess)
3555 {
3556 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3557 /* Note that the progress object is finalized later */
3558 /** @todo Consider checking mData->mSession.mProgress for cancellation
3559 * around here. */
3560
3561 /* We don't reset mSession.mPID here because it is necessary for
3562 * SessionMachine::uninit() to reap the child process later. */
3563
3564 if (FAILED(rc))
3565 {
3566 /* Close the remote session, remove the remote control from the list
3567 * and reset session state to Closed (@note keep the code in sync
3568 * with the relevant part in checkForSpawnFailure()). */
3569
3570 Assert(mData->mSession.mRemoteControls.size() == 1);
3571 if (mData->mSession.mRemoteControls.size() == 1)
3572 {
3573 ErrorInfoKeeper eik;
3574 mData->mSession.mRemoteControls.front()->Uninitialize();
3575 }
3576
3577 mData->mSession.mRemoteControls.clear();
3578 mData->mSession.mState = SessionState_Unlocked;
3579 }
3580 }
3581 else
3582 {
3583 /* memorize PID of the directly opened session */
3584 if (SUCCEEDED(rc))
3585 mData->mSession.mPID = pid;
3586 }
3587
3588 if (SUCCEEDED(rc))
3589 {
3590 mData->mSession.mLockType = aLockType;
3591 /* memorize the direct session control and cache IUnknown for it */
3592 mData->mSession.mDirectControl = pSessionControl;
3593 mData->mSession.mState = SessionState_Locked;
3594 if (!fLaunchingVMProcess)
3595 mData->mSession.mName = strSessionName;
3596 /* associate the SessionMachine with this Machine */
3597 mData->mSession.mMachine = sessionMachine;
3598
3599 /* request an IUnknown pointer early from the remote party for later
3600 * identity checks (it will be internally cached within mDirectControl
3601 * at least on XPCOM) */
3602 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3603 NOREF(unk);
3604 }
3605
3606 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3607 * would break the lock order */
3608 alock.release();
3609
3610 /* uninitialize the created session machine on failure */
3611 if (FAILED(rc))
3612 sessionMachine->uninit();
3613 }
3614
3615 if (SUCCEEDED(rc))
3616 {
3617 /*
3618 * tell the client watcher thread to update the set of
3619 * machines that have open sessions
3620 */
3621 mParent->i_updateClientWatcher();
3622
3623 if (oldState != SessionState_Locked)
3624 /* fire an event */
3625 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3626 }
3627
3628 return rc;
3629}
3630
3631/**
3632 * @note Locks objects!
3633 */
3634HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3635 const com::Utf8Str &aName,
3636 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3637 ComPtr<IProgress> &aProgress)
3638{
3639 Utf8Str strFrontend(aName);
3640 /* "emergencystop" doesn't need the session, so skip the checks/interface
3641 * retrieval. This code doesn't quite fit in here, but introducing a
3642 * special API method would be even more effort, and would require explicit
3643 * support by every API client. It's better to hide the feature a bit. */
3644 if (strFrontend != "emergencystop")
3645 CheckComArgNotNull(aSession);
3646
3647 HRESULT rc = S_OK;
3648 if (strFrontend.isEmpty())
3649 {
3650 Bstr bstrFrontend;
3651 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3652 if (FAILED(rc))
3653 return rc;
3654 strFrontend = bstrFrontend;
3655 if (strFrontend.isEmpty())
3656 {
3657 ComPtr<ISystemProperties> systemProperties;
3658 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3659 if (FAILED(rc))
3660 return rc;
3661 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3662 if (FAILED(rc))
3663 return rc;
3664 strFrontend = bstrFrontend;
3665 }
3666 /* paranoia - emergencystop is not a valid default */
3667 if (strFrontend == "emergencystop")
3668 strFrontend = Utf8Str::Empty;
3669 }
3670 /* default frontend: Qt GUI */
3671 if (strFrontend.isEmpty())
3672 strFrontend = "GUI/Qt";
3673
3674 if (strFrontend != "emergencystop")
3675 {
3676 /* check the session state */
3677 SessionState_T state;
3678 rc = aSession->COMGETTER(State)(&state);
3679 if (FAILED(rc))
3680 return rc;
3681
3682 if (state != SessionState_Unlocked)
3683 return setError(VBOX_E_INVALID_OBJECT_STATE,
3684 tr("The given session is busy"));
3685
3686 /* get the IInternalSessionControl interface */
3687 ComPtr<IInternalSessionControl> control(aSession);
3688 ComAssertMsgRet(!control.isNull(),
3689 ("No IInternalSessionControl interface"),
3690 E_INVALIDARG);
3691
3692 /* get the teleporter enable state for the progress object init. */
3693 BOOL fTeleporterEnabled;
3694 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3695 if (FAILED(rc))
3696 return rc;
3697
3698 /* create a progress object */
3699 ComObjPtr<ProgressProxy> progress;
3700 progress.createObject();
3701 rc = progress->init(mParent,
3702 static_cast<IMachine*>(this),
3703 Bstr(tr("Starting VM")).raw(),
3704 TRUE /* aCancelable */,
3705 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3706 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3707 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3708 2 /* uFirstOperationWeight */,
3709 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3710
3711 if (SUCCEEDED(rc))
3712 {
3713 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3714 if (SUCCEEDED(rc))
3715 {
3716 aProgress = progress;
3717
3718 /* signal the client watcher thread */
3719 mParent->i_updateClientWatcher();
3720
3721 /* fire an event */
3722 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3723 }
3724 }
3725 }
3726 else
3727 {
3728 /* no progress object - either instant success or failure */
3729 aProgress = NULL;
3730
3731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3732
3733 if (mData->mSession.mState != SessionState_Locked)
3734 return setError(VBOX_E_INVALID_OBJECT_STATE,
3735 tr("The machine '%s' is not locked by a session"),
3736 mUserData->s.strName.c_str());
3737
3738 /* must have a VM process associated - do not kill normal API clients
3739 * with an open session */
3740 if (!Global::IsOnline(mData->mMachineState))
3741 return setError(VBOX_E_INVALID_OBJECT_STATE,
3742 tr("The machine '%s' does not have a VM process"),
3743 mUserData->s.strName.c_str());
3744
3745 /* forcibly terminate the VM process */
3746 if (mData->mSession.mPID != NIL_RTPROCESS)
3747 RTProcTerminate(mData->mSession.mPID);
3748
3749 /* signal the client watcher thread, as most likely the client has
3750 * been terminated */
3751 mParent->i_updateClientWatcher();
3752 }
3753
3754 return rc;
3755}
3756
3757HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3758{
3759 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3760 return setError(E_INVALIDARG,
3761 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3762 aPosition, SchemaDefs::MaxBootPosition);
3763
3764 if (aDevice == DeviceType_USB)
3765 return setError(E_NOTIMPL,
3766 tr("Booting from USB device is currently not supported"));
3767
3768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3769
3770 HRESULT rc = i_checkStateDependency(MutableStateDep);
3771 if (FAILED(rc)) return rc;
3772
3773 i_setModified(IsModified_MachineData);
3774 mHWData.backup();
3775 mHWData->mBootOrder[aPosition - 1] = aDevice;
3776
3777 return S_OK;
3778}
3779
3780HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3781{
3782 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3783 return setError(E_INVALIDARG,
3784 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3785 aPosition, SchemaDefs::MaxBootPosition);
3786
3787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3788
3789 *aDevice = mHWData->mBootOrder[aPosition - 1];
3790
3791 return S_OK;
3792}
3793
3794HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3795 LONG aControllerPort,
3796 LONG aDevice,
3797 DeviceType_T aType,
3798 const ComPtr<IMedium> &aMedium)
3799{
3800 IMedium *aM = aMedium;
3801 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3802 aName.c_str(), aControllerPort, aDevice, aType, aM));
3803
3804 // request the host lock first, since might be calling Host methods for getting host drives;
3805 // next, protect the media tree all the while we're in here, as well as our member variables
3806 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3807 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3808
3809 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3810 if (FAILED(rc)) return rc;
3811
3812 /// @todo NEWMEDIA implicit machine registration
3813 if (!mData->mRegistered)
3814 return setError(VBOX_E_INVALID_OBJECT_STATE,
3815 tr("Cannot attach storage devices to an unregistered machine"));
3816
3817 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3818
3819 /* Check for an existing controller. */
3820 ComObjPtr<StorageController> ctl;
3821 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3822 if (FAILED(rc)) return rc;
3823
3824 StorageControllerType_T ctrlType;
3825 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3826 if (FAILED(rc))
3827 return setError(E_FAIL,
3828 tr("Could not get type of controller '%s'"),
3829 aName.c_str());
3830
3831 bool fSilent = false;
3832 Utf8Str strReconfig;
3833
3834 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3835 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3836 if ( mData->mMachineState == MachineState_Paused
3837 && strReconfig == "1")
3838 fSilent = true;
3839
3840 /* Check that the controller can do hot-plugging if we attach the device while the VM is running. */
3841 bool fHotplug = false;
3842 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3843 fHotplug = true;
3844
3845 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3846 return setError(VBOX_E_INVALID_VM_STATE,
3847 tr("Controller '%s' does not support hot-plugging"),
3848 aName.c_str());
3849
3850 /* Attaching a USB device when a VM is powered off should default to being marked as hot-pluggable */
3851 if (!fHotplug && !Global::IsOnlineOrTransient(mData->mMachineState) && ctrlType == StorageControllerType_USB)
3852 fHotplug = true;
3853
3854 // check that the port and device are not out of range
3855 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3856 if (FAILED(rc)) return rc;
3857
3858 /* check if the device slot is already busy */
3859 MediumAttachment *pAttachTemp;
3860 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3861 aName,
3862 aControllerPort,
3863 aDevice)))
3864 {
3865 Medium *pMedium = pAttachTemp->i_getMedium();
3866 if (pMedium)
3867 {
3868 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3869 return setError(VBOX_E_OBJECT_IN_USE,
3870 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3871 pMedium->i_getLocationFull().c_str(),
3872 aControllerPort,
3873 aDevice,
3874 aName.c_str());
3875 }
3876 else
3877 return setError(VBOX_E_OBJECT_IN_USE,
3878 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3879 aControllerPort, aDevice, aName.c_str());
3880 }
3881
3882 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3883 if (aMedium && medium.isNull())
3884 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3885
3886 AutoCaller mediumCaller(medium);
3887 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3888
3889 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3890
3891 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3892 && !medium.isNull()
3893 && ( medium->i_getType() != MediumType_Readonly
3894 || medium->i_getDeviceType() != DeviceType_DVD)
3895 )
3896 return setError(VBOX_E_OBJECT_IN_USE,
3897 tr("Medium '%s' is already attached to this virtual machine"),
3898 medium->i_getLocationFull().c_str());
3899
3900 if (!medium.isNull())
3901 {
3902 MediumType_T mtype = medium->i_getType();
3903 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3904 // For DVDs it's not written to the config file, so needs no global config
3905 // version bump. For floppies it's a new attribute "type", which is ignored
3906 // by older VirtualBox version, so needs no global config version bump either.
3907 // For hard disks this type is not accepted.
3908 if (mtype == MediumType_MultiAttach)
3909 {
3910 // This type is new with VirtualBox 4.0 and therefore requires settings
3911 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3912 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3913 // two reasons: The medium type is a property of the media registry tree, which
3914 // can reside in the global config file (for pre-4.0 media); we would therefore
3915 // possibly need to bump the global config version. We don't want to do that though
3916 // because that might make downgrading to pre-4.0 impossible.
3917 // As a result, we can only use these two new types if the medium is NOT in the
3918 // global registry:
3919 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3920 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3921 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3922 )
3923 return setError(VBOX_E_INVALID_OBJECT_STATE,
3924 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3925 "to machines that were created with VirtualBox 4.0 or later"),
3926 medium->i_getLocationFull().c_str());
3927 }
3928 }
3929
3930 bool fIndirect = false;
3931 if (!medium.isNull())
3932 fIndirect = medium->i_isReadOnly();
3933 bool associate = true;
3934
3935 do
3936 {
3937 if ( aType == DeviceType_HardDisk
3938 && mMediumAttachments.isBackedUp())
3939 {
3940 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3941
3942 /* check if the medium was attached to the VM before we started
3943 * changing attachments in which case the attachment just needs to
3944 * be restored */
3945 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3946 {
3947 AssertReturn(!fIndirect, E_FAIL);
3948
3949 /* see if it's the same bus/channel/device */
3950 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3951 {
3952 /* the simplest case: restore the whole attachment
3953 * and return, nothing else to do */
3954 mMediumAttachments->push_back(pAttachTemp);
3955
3956 /* Reattach the medium to the VM. */
3957 if (fHotplug || fSilent)
3958 {
3959 mediumLock.release();
3960 treeLock.release();
3961 alock.release();
3962
3963 MediumLockList *pMediumLockList(new MediumLockList());
3964
3965 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3966 medium /* pToLockWrite */,
3967 false /* fMediumLockWriteAll */,
3968 NULL,
3969 *pMediumLockList);
3970 alock.acquire();
3971 if (FAILED(rc))
3972 delete pMediumLockList;
3973 else
3974 {
3975 mData->mSession.mLockedMedia.Unlock();
3976 alock.release();
3977 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3978 mData->mSession.mLockedMedia.Lock();
3979 alock.acquire();
3980 }
3981 alock.release();
3982
3983 if (SUCCEEDED(rc))
3984 {
3985 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3986 /* Remove lock list in case of error. */
3987 if (FAILED(rc))
3988 {
3989 mData->mSession.mLockedMedia.Unlock();
3990 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3991 mData->mSession.mLockedMedia.Lock();
3992 }
3993 }
3994 }
3995
3996 return S_OK;
3997 }
3998
3999 /* bus/channel/device differ; we need a new attachment object,
4000 * but don't try to associate it again */
4001 associate = false;
4002 break;
4003 }
4004 }
4005
4006 /* go further only if the attachment is to be indirect */
4007 if (!fIndirect)
4008 break;
4009
4010 /* perform the so called smart attachment logic for indirect
4011 * attachments. Note that smart attachment is only applicable to base
4012 * hard disks. */
4013
4014 if (medium->i_getParent().isNull())
4015 {
4016 /* first, investigate the backup copy of the current hard disk
4017 * attachments to make it possible to re-attach existing diffs to
4018 * another device slot w/o losing their contents */
4019 if (mMediumAttachments.isBackedUp())
4020 {
4021 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4022
4023 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4024 uint32_t foundLevel = 0;
4025
4026 for (MediumAttachmentList::const_iterator
4027 it = oldAtts.begin();
4028 it != oldAtts.end();
4029 ++it)
4030 {
4031 uint32_t level = 0;
4032 MediumAttachment *pAttach = *it;
4033 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4034 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4035 if (pMedium.isNull())
4036 continue;
4037
4038 if (pMedium->i_getBase(&level) == medium)
4039 {
4040 /* skip the hard disk if its currently attached (we
4041 * cannot attach the same hard disk twice) */
4042 if (i_findAttachment(*mMediumAttachments.data(),
4043 pMedium))
4044 continue;
4045
4046 /* matched device, channel and bus (i.e. attached to the
4047 * same place) will win and immediately stop the search;
4048 * otherwise the attachment that has the youngest
4049 * descendant of medium will be used
4050 */
4051 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4052 {
4053 /* the simplest case: restore the whole attachment
4054 * and return, nothing else to do */
4055 mMediumAttachments->push_back(*it);
4056
4057 /* Reattach the medium to the VM. */
4058 if (fHotplug || fSilent)
4059 {
4060 mediumLock.release();
4061 treeLock.release();
4062 alock.release();
4063
4064 MediumLockList *pMediumLockList(new MediumLockList());
4065
4066 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4067 medium /* pToLockWrite */,
4068 false /* fMediumLockWriteAll */,
4069 NULL,
4070 *pMediumLockList);
4071 alock.acquire();
4072 if (FAILED(rc))
4073 delete pMediumLockList;
4074 else
4075 {
4076 mData->mSession.mLockedMedia.Unlock();
4077 alock.release();
4078 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4079 mData->mSession.mLockedMedia.Lock();
4080 alock.acquire();
4081 }
4082 alock.release();
4083
4084 if (SUCCEEDED(rc))
4085 {
4086 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4087 /* Remove lock list in case of error. */
4088 if (FAILED(rc))
4089 {
4090 mData->mSession.mLockedMedia.Unlock();
4091 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4092 mData->mSession.mLockedMedia.Lock();
4093 }
4094 }
4095 }
4096
4097 return S_OK;
4098 }
4099 else if ( foundIt == oldAtts.end()
4100 || level > foundLevel /* prefer younger */
4101 )
4102 {
4103 foundIt = it;
4104 foundLevel = level;
4105 }
4106 }
4107 }
4108
4109 if (foundIt != oldAtts.end())
4110 {
4111 /* use the previously attached hard disk */
4112 medium = (*foundIt)->i_getMedium();
4113 mediumCaller.attach(medium);
4114 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4115 mediumLock.attach(medium);
4116 /* not implicit, doesn't require association with this VM */
4117 fIndirect = false;
4118 associate = false;
4119 /* go right to the MediumAttachment creation */
4120 break;
4121 }
4122 }
4123
4124 /* must give up the medium lock and medium tree lock as below we
4125 * go over snapshots, which needs a lock with higher lock order. */
4126 mediumLock.release();
4127 treeLock.release();
4128
4129 /* then, search through snapshots for the best diff in the given
4130 * hard disk's chain to base the new diff on */
4131
4132 ComObjPtr<Medium> base;
4133 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4134 while (snap)
4135 {
4136 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4137
4138 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4139
4140 MediumAttachment *pAttachFound = NULL;
4141 uint32_t foundLevel = 0;
4142
4143 for (MediumAttachmentList::const_iterator
4144 it = snapAtts.begin();
4145 it != snapAtts.end();
4146 ++it)
4147 {
4148 MediumAttachment *pAttach = *it;
4149 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4150 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4151 if (pMedium.isNull())
4152 continue;
4153
4154 uint32_t level = 0;
4155 if (pMedium->i_getBase(&level) == medium)
4156 {
4157 /* matched device, channel and bus (i.e. attached to the
4158 * same place) will win and immediately stop the search;
4159 * otherwise the attachment that has the youngest
4160 * descendant of medium will be used
4161 */
4162 if ( pAttach->i_getDevice() == aDevice
4163 && pAttach->i_getPort() == aControllerPort
4164 && pAttach->i_getControllerName() == aName
4165 )
4166 {
4167 pAttachFound = pAttach;
4168 break;
4169 }
4170 else if ( !pAttachFound
4171 || level > foundLevel /* prefer younger */
4172 )
4173 {
4174 pAttachFound = pAttach;
4175 foundLevel = level;
4176 }
4177 }
4178 }
4179
4180 if (pAttachFound)
4181 {
4182 base = pAttachFound->i_getMedium();
4183 break;
4184 }
4185
4186 snap = snap->i_getParent();
4187 }
4188
4189 /* re-lock medium tree and the medium, as we need it below */
4190 treeLock.acquire();
4191 mediumLock.acquire();
4192
4193 /* found a suitable diff, use it as a base */
4194 if (!base.isNull())
4195 {
4196 medium = base;
4197 mediumCaller.attach(medium);
4198 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4199 mediumLock.attach(medium);
4200 }
4201 }
4202
4203 Utf8Str strFullSnapshotFolder;
4204 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4205
4206 ComObjPtr<Medium> diff;
4207 diff.createObject();
4208 // store this diff in the same registry as the parent
4209 Guid uuidRegistryParent;
4210 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4211 {
4212 // parent image has no registry: this can happen if we're attaching a new immutable
4213 // image that has not yet been attached (medium then points to the base and we're
4214 // creating the diff image for the immutable, and the parent is not yet registered);
4215 // put the parent in the machine registry then
4216 mediumLock.release();
4217 treeLock.release();
4218 alock.release();
4219 i_addMediumToRegistry(medium);
4220 alock.acquire();
4221 treeLock.acquire();
4222 mediumLock.acquire();
4223 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4224 }
4225 rc = diff->init(mParent,
4226 medium->i_getPreferredDiffFormat(),
4227 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4228 uuidRegistryParent,
4229 DeviceType_HardDisk);
4230 if (FAILED(rc)) return rc;
4231
4232 /* Apply the normal locking logic to the entire chain. */
4233 MediumLockList *pMediumLockList(new MediumLockList());
4234 mediumLock.release();
4235 treeLock.release();
4236 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4237 diff /* pToLockWrite */,
4238 false /* fMediumLockWriteAll */,
4239 medium,
4240 *pMediumLockList);
4241 treeLock.acquire();
4242 mediumLock.acquire();
4243 if (SUCCEEDED(rc))
4244 {
4245 mediumLock.release();
4246 treeLock.release();
4247 rc = pMediumLockList->Lock();
4248 treeLock.acquire();
4249 mediumLock.acquire();
4250 if (FAILED(rc))
4251 setError(rc,
4252 tr("Could not lock medium when creating diff '%s'"),
4253 diff->i_getLocationFull().c_str());
4254 else
4255 {
4256 /* will release the lock before the potentially lengthy
4257 * operation, so protect with the special state */
4258 MachineState_T oldState = mData->mMachineState;
4259 i_setMachineState(MachineState_SettingUp);
4260
4261 mediumLock.release();
4262 treeLock.release();
4263 alock.release();
4264
4265 rc = medium->i_createDiffStorage(diff,
4266 medium->i_getPreferredDiffVariant(),
4267 pMediumLockList,
4268 NULL /* aProgress */,
4269 true /* aWait */,
4270 false /* aNotify */);
4271
4272 alock.acquire();
4273 treeLock.acquire();
4274 mediumLock.acquire();
4275
4276 i_setMachineState(oldState);
4277 }
4278 }
4279
4280 /* Unlock the media and free the associated memory. */
4281 delete pMediumLockList;
4282
4283 if (FAILED(rc)) return rc;
4284
4285 /* use the created diff for the actual attachment */
4286 medium = diff;
4287 mediumCaller.attach(medium);
4288 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4289 mediumLock.attach(medium);
4290 }
4291 while (0);
4292
4293 ComObjPtr<MediumAttachment> attachment;
4294 attachment.createObject();
4295 rc = attachment->init(this,
4296 medium,
4297 aName,
4298 aControllerPort,
4299 aDevice,
4300 aType,
4301 fIndirect,
4302 false /* fPassthrough */,
4303 false /* fTempEject */,
4304 false /* fNonRotational */,
4305 false /* fDiscard */,
4306 fHotplug /* fHotPluggable */,
4307 Utf8Str::Empty);
4308 if (FAILED(rc)) return rc;
4309
4310 if (associate && !medium.isNull())
4311 {
4312 // as the last step, associate the medium to the VM
4313 rc = medium->i_addBackReference(mData->mUuid);
4314 // here we can fail because of Deleting, or being in process of creating a Diff
4315 if (FAILED(rc)) return rc;
4316
4317 mediumLock.release();
4318 treeLock.release();
4319 alock.release();
4320 i_addMediumToRegistry(medium);
4321 alock.acquire();
4322 treeLock.acquire();
4323 mediumLock.acquire();
4324 }
4325
4326 /* success: finally remember the attachment */
4327 i_setModified(IsModified_Storage);
4328 mMediumAttachments.backup();
4329 mMediumAttachments->push_back(attachment);
4330
4331 mediumLock.release();
4332 treeLock.release();
4333 alock.release();
4334
4335 if (fHotplug || fSilent)
4336 {
4337 if (!medium.isNull())
4338 {
4339 MediumLockList *pMediumLockList(new MediumLockList());
4340
4341 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4342 medium /* pToLockWrite */,
4343 false /* fMediumLockWriteAll */,
4344 NULL,
4345 *pMediumLockList);
4346 alock.acquire();
4347 if (FAILED(rc))
4348 delete pMediumLockList;
4349 else
4350 {
4351 mData->mSession.mLockedMedia.Unlock();
4352 alock.release();
4353 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4354 mData->mSession.mLockedMedia.Lock();
4355 alock.acquire();
4356 }
4357 alock.release();
4358 }
4359
4360 if (SUCCEEDED(rc))
4361 {
4362 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4363 /* Remove lock list in case of error. */
4364 if (FAILED(rc))
4365 {
4366 mData->mSession.mLockedMedia.Unlock();
4367 mData->mSession.mLockedMedia.Remove(attachment);
4368 mData->mSession.mLockedMedia.Lock();
4369 }
4370 }
4371 }
4372
4373 /* Save modified registries, but skip this machine as it's the caller's
4374 * job to save its settings like all other settings changes. */
4375 mParent->i_unmarkRegistryModified(i_getId());
4376 mParent->i_saveModifiedRegistries();
4377
4378 if (SUCCEEDED(rc))
4379 {
4380 if (fIndirect && medium != aM)
4381 mParent->i_onMediumConfigChanged(medium);
4382 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4383 }
4384
4385 return rc;
4386}
4387
4388HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4389 LONG aDevice)
4390{
4391 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4392 aName.c_str(), aControllerPort, aDevice));
4393
4394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4395
4396 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4397 if (FAILED(rc)) return rc;
4398
4399 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4400
4401 /* Check for an existing controller. */
4402 ComObjPtr<StorageController> ctl;
4403 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4404 if (FAILED(rc)) return rc;
4405
4406 StorageControllerType_T ctrlType;
4407 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4408 if (FAILED(rc))
4409 return setError(E_FAIL,
4410 tr("Could not get type of controller '%s'"),
4411 aName.c_str());
4412
4413 bool fSilent = false;
4414 Utf8Str strReconfig;
4415
4416 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4417 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4418 if ( mData->mMachineState == MachineState_Paused
4419 && strReconfig == "1")
4420 fSilent = true;
4421
4422 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4423 bool fHotplug = false;
4424 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4425 fHotplug = true;
4426
4427 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4428 return setError(VBOX_E_INVALID_VM_STATE,
4429 tr("Controller '%s' does not support hot-plugging"),
4430 aName.c_str());
4431
4432 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4433 aName,
4434 aControllerPort,
4435 aDevice);
4436 if (!pAttach)
4437 return setError(VBOX_E_OBJECT_NOT_FOUND,
4438 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4439 aDevice, aControllerPort, aName.c_str());
4440
4441 if (fHotplug && !pAttach->i_getHotPluggable())
4442 return setError(VBOX_E_NOT_SUPPORTED,
4443 tr("The device slot %d on port %d of controller '%s' does not support hot-plugging"),
4444 aDevice, aControllerPort, aName.c_str());
4445
4446 /*
4447 * The VM has to detach the device before we delete any implicit diffs.
4448 * If this fails we can roll back without loosing data.
4449 */
4450 if (fHotplug || fSilent)
4451 {
4452 alock.release();
4453 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4454 alock.acquire();
4455 }
4456 if (FAILED(rc)) return rc;
4457
4458 /* If we are here everything went well and we can delete the implicit now. */
4459 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4460
4461 alock.release();
4462
4463 /* Save modified registries, but skip this machine as it's the caller's
4464 * job to save its settings like all other settings changes. */
4465 mParent->i_unmarkRegistryModified(i_getId());
4466 mParent->i_saveModifiedRegistries();
4467
4468 if (SUCCEEDED(rc))
4469 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4470
4471 return rc;
4472}
4473
4474HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4475 LONG aDevice, BOOL aPassthrough)
4476{
4477 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4478 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4479
4480 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4481
4482 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4483 if (FAILED(rc)) return rc;
4484
4485 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4486
4487 /* Check for an existing controller. */
4488 ComObjPtr<StorageController> ctl;
4489 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4490 if (FAILED(rc)) return rc;
4491
4492 StorageControllerType_T ctrlType;
4493 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4494 if (FAILED(rc))
4495 return setError(E_FAIL,
4496 tr("Could not get type of controller '%s'"),
4497 aName.c_str());
4498
4499 bool fSilent = false;
4500 Utf8Str strReconfig;
4501
4502 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4503 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4504 if ( mData->mMachineState == MachineState_Paused
4505 && strReconfig == "1")
4506 fSilent = true;
4507
4508 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4509 bool fHotplug = false;
4510 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4511 fHotplug = true;
4512
4513 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4514 return setError(VBOX_E_INVALID_VM_STATE,
4515 tr("Controller '%s' does not support hot-plugging which is required to change the passthrough setting while the VM is running"),
4516 aName.c_str());
4517
4518 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4519 aName,
4520 aControllerPort,
4521 aDevice);
4522 if (!pAttach)
4523 return setError(VBOX_E_OBJECT_NOT_FOUND,
4524 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4525 aDevice, aControllerPort, aName.c_str());
4526
4527
4528 i_setModified(IsModified_Storage);
4529 mMediumAttachments.backup();
4530
4531 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4532
4533 if (pAttach->i_getType() != DeviceType_DVD)
4534 return setError(E_INVALIDARG,
4535 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4536 aDevice, aControllerPort, aName.c_str());
4537
4538 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4539
4540 pAttach->i_updatePassthrough(!!aPassthrough);
4541
4542 attLock.release();
4543 alock.release();
4544 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4545 if (SUCCEEDED(rc) && fValueChanged)
4546 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4547
4548 return rc;
4549}
4550
4551HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4552 LONG aDevice, BOOL aTemporaryEject)
4553{
4554
4555 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4556 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4557
4558 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4559
4560 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4561 if (FAILED(rc)) return rc;
4562
4563 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4564 aName,
4565 aControllerPort,
4566 aDevice);
4567 if (!pAttach)
4568 return setError(VBOX_E_OBJECT_NOT_FOUND,
4569 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4570 aDevice, aControllerPort, aName.c_str());
4571
4572
4573 i_setModified(IsModified_Storage);
4574 mMediumAttachments.backup();
4575
4576 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4577
4578 if (pAttach->i_getType() != DeviceType_DVD)
4579 return setError(E_INVALIDARG,
4580 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4581 aDevice, aControllerPort, aName.c_str());
4582 pAttach->i_updateTempEject(!!aTemporaryEject);
4583
4584 return S_OK;
4585}
4586
4587HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4588 LONG aDevice, BOOL aNonRotational)
4589{
4590
4591 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4592 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4593
4594 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4595
4596 HRESULT rc = i_checkStateDependency(MutableStateDep);
4597 if (FAILED(rc)) return rc;
4598
4599 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4600
4601 if (Global::IsOnlineOrTransient(mData->mMachineState))
4602 return setError(VBOX_E_INVALID_VM_STATE,
4603 tr("Invalid machine state: %s"),
4604 Global::stringifyMachineState(mData->mMachineState));
4605
4606 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4607 aName,
4608 aControllerPort,
4609 aDevice);
4610 if (!pAttach)
4611 return setError(VBOX_E_OBJECT_NOT_FOUND,
4612 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4613 aDevice, aControllerPort, aName.c_str());
4614
4615
4616 i_setModified(IsModified_Storage);
4617 mMediumAttachments.backup();
4618
4619 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4620
4621 if (pAttach->i_getType() != DeviceType_HardDisk)
4622 return setError(E_INVALIDARG,
4623 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4624 aDevice, aControllerPort, aName.c_str());
4625 pAttach->i_updateNonRotational(!!aNonRotational);
4626
4627 return S_OK;
4628}
4629
4630HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4631 LONG aDevice, BOOL aDiscard)
4632{
4633
4634 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4635 aName.c_str(), aControllerPort, aDevice, aDiscard));
4636
4637 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4638
4639 HRESULT rc = i_checkStateDependency(MutableStateDep);
4640 if (FAILED(rc)) return rc;
4641
4642 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4643
4644 if (Global::IsOnlineOrTransient(mData->mMachineState))
4645 return setError(VBOX_E_INVALID_VM_STATE,
4646 tr("Invalid machine state: %s"),
4647 Global::stringifyMachineState(mData->mMachineState));
4648
4649 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4650 aName,
4651 aControllerPort,
4652 aDevice);
4653 if (!pAttach)
4654 return setError(VBOX_E_OBJECT_NOT_FOUND,
4655 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4656 aDevice, aControllerPort, aName.c_str());
4657
4658
4659 i_setModified(IsModified_Storage);
4660 mMediumAttachments.backup();
4661
4662 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4663
4664 if (pAttach->i_getType() != DeviceType_HardDisk)
4665 return setError(E_INVALIDARG,
4666 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4667 aDevice, aControllerPort, aName.c_str());
4668 pAttach->i_updateDiscard(!!aDiscard);
4669
4670 return S_OK;
4671}
4672
4673HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4674 LONG aDevice, BOOL aHotPluggable)
4675{
4676 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4677 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4678
4679 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4680
4681 HRESULT rc = i_checkStateDependency(MutableStateDep);
4682 if (FAILED(rc)) return rc;
4683
4684 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4685
4686 if (Global::IsOnlineOrTransient(mData->mMachineState))
4687 return setError(VBOX_E_INVALID_VM_STATE,
4688 tr("Invalid machine state: %s"),
4689 Global::stringifyMachineState(mData->mMachineState));
4690
4691 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4692 aName,
4693 aControllerPort,
4694 aDevice);
4695 if (!pAttach)
4696 return setError(VBOX_E_OBJECT_NOT_FOUND,
4697 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4698 aDevice, aControllerPort, aName.c_str());
4699
4700 /* Check for an existing controller. */
4701 ComObjPtr<StorageController> ctl;
4702 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4703 if (FAILED(rc)) return rc;
4704
4705 StorageControllerType_T ctrlType;
4706 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4707 if (FAILED(rc))
4708 return setError(E_FAIL,
4709 tr("Could not get type of controller '%s'"),
4710 aName.c_str());
4711
4712 if (!i_isControllerHotplugCapable(ctrlType))
4713 return setError(VBOX_E_NOT_SUPPORTED,
4714 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4715 aName.c_str());
4716
4717 /* silently ignore attempts to modify the hot-plug status of USB devices */
4718 if (ctrlType == StorageControllerType_USB)
4719 return S_OK;
4720
4721 i_setModified(IsModified_Storage);
4722 mMediumAttachments.backup();
4723
4724 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4725
4726 if (pAttach->i_getType() == DeviceType_Floppy)
4727 return setError(E_INVALIDARG,
4728 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%s' is a floppy drive"),
4729 aDevice, aControllerPort, aName.c_str());
4730 pAttach->i_updateHotPluggable(!!aHotPluggable);
4731
4732 return S_OK;
4733}
4734
4735HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4736 LONG aDevice)
4737{
4738 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4739 aName.c_str(), aControllerPort, aDevice));
4740
4741 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4742}
4743
4744HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4745 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4746{
4747 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4748 aName.c_str(), aControllerPort, aDevice));
4749
4750 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4751
4752 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4753 if (FAILED(rc)) return rc;
4754
4755 if (Global::IsOnlineOrTransient(mData->mMachineState))
4756 return setError(VBOX_E_INVALID_VM_STATE,
4757 tr("Invalid machine state: %s"),
4758 Global::stringifyMachineState(mData->mMachineState));
4759
4760 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4761 aName,
4762 aControllerPort,
4763 aDevice);
4764 if (!pAttach)
4765 return setError(VBOX_E_OBJECT_NOT_FOUND,
4766 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4767 aDevice, aControllerPort, aName.c_str());
4768
4769
4770 i_setModified(IsModified_Storage);
4771 mMediumAttachments.backup();
4772
4773 IBandwidthGroup *iB = aBandwidthGroup;
4774 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4775 if (aBandwidthGroup && group.isNull())
4776 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4777
4778 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4779
4780 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4781 if (strBandwidthGroupOld.isNotEmpty())
4782 {
4783 /* Get the bandwidth group object and release it - this must not fail. */
4784 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4785 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4786 Assert(SUCCEEDED(rc));
4787
4788 pBandwidthGroupOld->i_release();
4789 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4790 }
4791
4792 if (!group.isNull())
4793 {
4794 group->i_reference();
4795 pAttach->i_updateBandwidthGroup(group->i_getName());
4796 }
4797
4798 return S_OK;
4799}
4800
4801HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4802 LONG aControllerPort,
4803 LONG aDevice,
4804 DeviceType_T aType)
4805{
4806 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4807 aName.c_str(), aControllerPort, aDevice, aType));
4808
4809 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4810}
4811
4812
4813HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4814 LONG aControllerPort,
4815 LONG aDevice,
4816 BOOL aForce)
4817{
4818 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4819 aName.c_str(), aControllerPort, aForce));
4820
4821 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4822}
4823
4824HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4825 LONG aControllerPort,
4826 LONG aDevice,
4827 const ComPtr<IMedium> &aMedium,
4828 BOOL aForce)
4829{
4830 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4831 aName.c_str(), aControllerPort, aDevice, aForce));
4832
4833 // request the host lock first, since might be calling Host methods for getting host drives;
4834 // next, protect the media tree all the while we're in here, as well as our member variables
4835 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4836 this->lockHandle(),
4837 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4838
4839 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4840 if (FAILED(hrc)) return hrc;
4841
4842 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4843 aName,
4844 aControllerPort,
4845 aDevice);
4846 if (pAttach.isNull())
4847 return setError(VBOX_E_OBJECT_NOT_FOUND,
4848 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4849 aDevice, aControllerPort, aName.c_str());
4850
4851 /* Remember previously mounted medium. The medium before taking the
4852 * backup is not necessarily the same thing. */
4853 ComObjPtr<Medium> oldmedium;
4854 oldmedium = pAttach->i_getMedium();
4855
4856 IMedium *iM = aMedium;
4857 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4858 if (aMedium && pMedium.isNull())
4859 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4860
4861 AutoCaller mediumCaller(pMedium);
4862 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4863
4864 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4865 if (pMedium)
4866 {
4867 DeviceType_T mediumType = pAttach->i_getType();
4868 switch (mediumType)
4869 {
4870 case DeviceType_DVD:
4871 case DeviceType_Floppy:
4872 break;
4873
4874 default:
4875 return setError(VBOX_E_INVALID_OBJECT_STATE,
4876 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4877 aControllerPort,
4878 aDevice,
4879 aName.c_str());
4880 }
4881 }
4882
4883 i_setModified(IsModified_Storage);
4884 mMediumAttachments.backup();
4885
4886 {
4887 // The backup operation makes the pAttach reference point to the
4888 // old settings. Re-get the correct reference.
4889 pAttach = i_findAttachment(*mMediumAttachments.data(),
4890 aName,
4891 aControllerPort,
4892 aDevice);
4893 if (!oldmedium.isNull())
4894 oldmedium->i_removeBackReference(mData->mUuid);
4895 if (!pMedium.isNull())
4896 {
4897 pMedium->i_addBackReference(mData->mUuid);
4898
4899 mediumLock.release();
4900 multiLock.release();
4901 i_addMediumToRegistry(pMedium);
4902 multiLock.acquire();
4903 mediumLock.acquire();
4904 }
4905
4906 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4907 pAttach->i_updateMedium(pMedium);
4908 }
4909
4910 i_setModified(IsModified_Storage);
4911
4912 mediumLock.release();
4913 multiLock.release();
4914 HRESULT rc = i_onMediumChange(pAttach, aForce);
4915 multiLock.acquire();
4916 mediumLock.acquire();
4917
4918 /* On error roll back this change only. */
4919 if (FAILED(rc))
4920 {
4921 if (!pMedium.isNull())
4922 pMedium->i_removeBackReference(mData->mUuid);
4923 pAttach = i_findAttachment(*mMediumAttachments.data(),
4924 aName,
4925 aControllerPort,
4926 aDevice);
4927 /* If the attachment is gone in the meantime, bail out. */
4928 if (pAttach.isNull())
4929 return rc;
4930 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4931 if (!oldmedium.isNull())
4932 oldmedium->i_addBackReference(mData->mUuid);
4933 pAttach->i_updateMedium(oldmedium);
4934 }
4935
4936 mediumLock.release();
4937 multiLock.release();
4938
4939 /* Save modified registries, but skip this machine as it's the caller's
4940 * job to save its settings like all other settings changes. */
4941 mParent->i_unmarkRegistryModified(i_getId());
4942 mParent->i_saveModifiedRegistries();
4943
4944 return rc;
4945}
4946HRESULT Machine::getMedium(const com::Utf8Str &aName,
4947 LONG aControllerPort,
4948 LONG aDevice,
4949 ComPtr<IMedium> &aMedium)
4950{
4951 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4952 aName.c_str(), aControllerPort, aDevice));
4953
4954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4955
4956 aMedium = NULL;
4957
4958 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4959 aName,
4960 aControllerPort,
4961 aDevice);
4962 if (pAttach.isNull())
4963 return setError(VBOX_E_OBJECT_NOT_FOUND,
4964 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4965 aDevice, aControllerPort, aName.c_str());
4966
4967 aMedium = pAttach->i_getMedium();
4968
4969 return S_OK;
4970}
4971
4972HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4973{
4974 if (aSlot < RT_ELEMENTS(mSerialPorts))
4975 {
4976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4977 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4978 return S_OK;
4979 }
4980 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4981}
4982
4983HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4984{
4985 if (aSlot < RT_ELEMENTS(mParallelPorts))
4986 {
4987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4988 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4989 return S_OK;
4990 }
4991 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4992}
4993
4994
4995HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4996{
4997 /* Do not assert if slot is out of range, just return the advertised
4998 status. testdriver/vbox.py triggers this in logVmInfo. */
4999 if (aSlot >= mNetworkAdapters.size())
5000 return setError(E_INVALIDARG,
5001 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
5002 aSlot, mNetworkAdapters.size());
5003
5004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5005
5006 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
5007
5008 return S_OK;
5009}
5010
5011HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
5012{
5013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5014
5015 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
5016 size_t i = 0;
5017 for (settings::StringsMap::const_iterator
5018 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5019 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5020 ++it, ++i)
5021 aKeys[i] = it->first;
5022
5023 return S_OK;
5024}
5025
5026 /**
5027 * @note Locks this object for reading.
5028 */
5029HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
5030 com::Utf8Str &aValue)
5031{
5032 /* start with nothing found */
5033 aValue = "";
5034
5035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5036
5037 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5038 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5039 // found:
5040 aValue = it->second; // source is a Utf8Str
5041
5042 /* return the result to caller (may be empty) */
5043 return S_OK;
5044}
5045
5046 /**
5047 * @note Locks mParent for writing + this object for writing.
5048 */
5049HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5050{
5051 /* Because control characters in aKey have caused problems in the settings
5052 * they are rejected unless the key should be deleted. */
5053 if (!aValue.isEmpty())
5054 {
5055 for (size_t i = 0; i < aKey.length(); ++i)
5056 {
5057 char ch = aKey[i];
5058 if (RTLocCIsCntrl(ch))
5059 return E_INVALIDARG;
5060 }
5061 }
5062
5063 Utf8Str strOldValue; // empty
5064
5065 // locking note: we only hold the read lock briefly to look up the old value,
5066 // then release it and call the onExtraCanChange callbacks. There is a small
5067 // chance of a race insofar as the callback might be called twice if two callers
5068 // change the same key at the same time, but that's a much better solution
5069 // than the deadlock we had here before. The actual changing of the extradata
5070 // is then performed under the write lock and race-free.
5071
5072 // look up the old value first; if nothing has changed then we need not do anything
5073 {
5074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5075
5076 // For snapshots don't even think about allowing changes, extradata
5077 // is global for a machine, so there is nothing snapshot specific.
5078 if (i_isSnapshotMachine())
5079 return setError(VBOX_E_INVALID_VM_STATE,
5080 tr("Cannot set extradata for a snapshot"));
5081
5082 // check if the right IMachine instance is used
5083 if (mData->mRegistered && !i_isSessionMachine())
5084 return setError(VBOX_E_INVALID_VM_STATE,
5085 tr("Cannot set extradata for an immutable machine"));
5086
5087 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5088 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5089 strOldValue = it->second;
5090 }
5091
5092 bool fChanged;
5093 if ((fChanged = (strOldValue != aValue)))
5094 {
5095 // ask for permission from all listeners outside the locks;
5096 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5097 // lock to copy the list of callbacks to invoke
5098 Bstr bstrError;
5099 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
5100 {
5101 const char *sep = bstrError.isEmpty() ? "" : ": ";
5102 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
5103 return setError(E_ACCESSDENIED,
5104 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5105 aKey.c_str(),
5106 aValue.c_str(),
5107 sep,
5108 bstrError.raw());
5109 }
5110
5111 // data is changing and change not vetoed: then write it out under the lock
5112 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5113
5114 if (aValue.isEmpty())
5115 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5116 else
5117 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5118 // creates a new key if needed
5119
5120 bool fNeedsGlobalSaveSettings = false;
5121 // This saving of settings is tricky: there is no "old state" for the
5122 // extradata items at all (unlike all other settings), so the old/new
5123 // settings comparison would give a wrong result!
5124 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
5125
5126 if (fNeedsGlobalSaveSettings)
5127 {
5128 // save the global settings; for that we should hold only the VirtualBox lock
5129 alock.release();
5130 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5131 mParent->i_saveSettings();
5132 }
5133 }
5134
5135 // fire notification outside the lock
5136 if (fChanged)
5137 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
5138
5139 return S_OK;
5140}
5141
5142HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5143{
5144 aProgress = NULL;
5145 NOREF(aSettingsFilePath);
5146 ReturnComNotImplemented();
5147}
5148
5149HRESULT Machine::saveSettings()
5150{
5151 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5152
5153 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5154 if (FAILED(rc)) return rc;
5155
5156 /* the settings file path may never be null */
5157 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5158
5159 /* save all VM data excluding snapshots */
5160 bool fNeedsGlobalSaveSettings = false;
5161 rc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
5162 mlock.release();
5163
5164 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5165 {
5166 // save the global settings; for that we should hold only the VirtualBox lock
5167 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5168 rc = mParent->i_saveSettings();
5169 }
5170
5171 return rc;
5172}
5173
5174
5175HRESULT Machine::discardSettings()
5176{
5177 /*
5178 * We need to take the machine list lock here as well as the machine one
5179 * or we'll get into trouble should any media stuff require rolling back.
5180 *
5181 * Details:
5182 *
5183 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5184 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5185 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other lock: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x]
5186 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: class=0000000000d5eb10 4-LISTOFMACHINES created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
5187 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5188 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5189 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5190 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: class=0000000000d5ecd0 5-MACHINEOBJECT created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
5191 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5192 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5193 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5194 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5195 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5196 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #00: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=2 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(11705) Machine::i_rollback 00007ff6853f6ce4} [x/r]
5197 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #01: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x] (*)
5198 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5199 * 0:005> k
5200 * # Child-SP RetAddr Call Site
5201 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5202 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5203 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5204 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5205 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5206 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5207 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5208 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5209 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5210 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5211 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5212 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5213 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5214 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5215 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5216 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5217 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5218 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5219 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5220 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5221 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5222 *
5223 */
5224 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5225 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5226
5227 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5228 if (FAILED(rc)) return rc;
5229
5230 /*
5231 * during this rollback, the session will be notified if data has
5232 * been actually changed
5233 */
5234 i_rollback(true /* aNotify */);
5235
5236 return S_OK;
5237}
5238
5239/** @note Locks objects! */
5240HRESULT Machine::unregister(AutoCaller &autoCaller,
5241 CleanupMode_T aCleanupMode,
5242 std::vector<ComPtr<IMedium> > &aMedia)
5243{
5244 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5245
5246 Guid id(i_getId());
5247
5248 if (mData->mSession.mState != SessionState_Unlocked)
5249 return setError(VBOX_E_INVALID_OBJECT_STATE,
5250 tr("Cannot unregister the machine '%s' while it is locked"),
5251 mUserData->s.strName.c_str());
5252
5253 // wait for state dependents to drop to zero
5254 i_ensureNoStateDependencies(alock);
5255
5256 if (!mData->mAccessible)
5257 {
5258 // inaccessible machines can only be unregistered; uninitialize ourselves
5259 // here because currently there may be no unregistered that are inaccessible
5260 // (this state combination is not supported). Note releasing the caller and
5261 // leaving the lock before calling uninit()
5262 alock.release();
5263 autoCaller.release();
5264
5265 uninit();
5266
5267 mParent->i_unregisterMachine(this, CleanupMode_UnregisterOnly, id);
5268 // calls VirtualBox::i_saveSettings()
5269
5270 return S_OK;
5271 }
5272
5273 HRESULT rc = S_OK;
5274 mData->llFilesToDelete.clear();
5275
5276 if (!mSSData->strStateFilePath.isEmpty())
5277 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5278
5279 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
5280 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
5281 mData->llFilesToDelete.push_back(strNVRAMFile);
5282
5283 // This list collects the medium objects from all medium attachments
5284 // which we will detach from the machine and its snapshots, in a specific
5285 // order which allows for closing all media without getting "media in use"
5286 // errors, simply by going through the list from the front to the back:
5287 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5288 // and must be closed before the parent media from the snapshots, or closing the parents
5289 // will fail because they still have children);
5290 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5291 // the root ("first") snapshot of the machine.
5292 MediaList llMedia;
5293
5294 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5295 && mMediumAttachments->size()
5296 )
5297 {
5298 // we have media attachments: detach them all and add the Medium objects to our list
5299 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5300 }
5301
5302 if (mData->mFirstSnapshot)
5303 {
5304 // add the media from the medium attachments of the snapshots to
5305 // llMedia as well, after the "main" machine media;
5306 // Snapshot::uninitAll() calls Machine::detachAllMedia() for each
5307 // snapshot machine, depth first.
5308
5309 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5310 MachineState_T oldState = mData->mMachineState;
5311 mData->mMachineState = MachineState_DeletingSnapshot;
5312
5313 // make a copy of the first snapshot reference so the refcount does not
5314 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
5315 // (would hang due to the AutoCaller voodoo)
5316 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5317
5318 // GO!
5319 pFirstSnapshot->i_uninitAll(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5320
5321 mData->mMachineState = oldState;
5322 }
5323
5324 if (FAILED(rc))
5325 {
5326 i_rollbackMedia();
5327 return rc;
5328 }
5329
5330 // commit all the media changes made above
5331 i_commitMedia();
5332
5333 mData->mRegistered = false;
5334
5335 // machine lock no longer needed
5336 alock.release();
5337
5338 /* Make sure that the settings of the current VM are not saved, because
5339 * they are rather crippled at this point to meet the cleanup expectations
5340 * and there's no point destroying the VM config on disk just because. */
5341 mParent->i_unmarkRegistryModified(id);
5342
5343 // return media to caller
5344 aMedia.resize(llMedia.size());
5345 size_t i = 0;
5346 for (MediaList::const_iterator
5347 it = llMedia.begin();
5348 it != llMedia.end();
5349 ++it, ++i)
5350 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5351
5352 mParent->i_unregisterMachine(this, aCleanupMode, id);
5353 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5354
5355 return S_OK;
5356}
5357
5358/**
5359 * Task record for deleting a machine config.
5360 */
5361class Machine::DeleteConfigTask
5362 : public Machine::Task
5363{
5364public:
5365 DeleteConfigTask(Machine *m,
5366 Progress *p,
5367 const Utf8Str &t,
5368 const RTCList<ComPtr<IMedium> > &llMediums,
5369 const StringsList &llFilesToDelete)
5370 : Task(m, p, t),
5371 m_llMediums(llMediums),
5372 m_llFilesToDelete(llFilesToDelete)
5373 {}
5374
5375private:
5376 void handler()
5377 {
5378 try
5379 {
5380 m_pMachine->i_deleteConfigHandler(*this);
5381 }
5382 catch (...)
5383 {
5384 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5385 }
5386 }
5387
5388 RTCList<ComPtr<IMedium> > m_llMediums;
5389 StringsList m_llFilesToDelete;
5390
5391 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5392};
5393
5394/**
5395 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5396 * SessionMachine::taskHandler().
5397 *
5398 * @note Locks this object for writing.
5399 *
5400 * @param task
5401 * @return
5402 */
5403void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5404{
5405 LogFlowThisFuncEnter();
5406
5407 AutoCaller autoCaller(this);
5408 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5409 if (FAILED(autoCaller.rc()))
5410 {
5411 /* we might have been uninitialized because the session was accidentally
5412 * closed by the client, so don't assert */
5413 HRESULT rc = setError(E_FAIL,
5414 tr("The session has been accidentally closed"));
5415 task.m_pProgress->i_notifyComplete(rc);
5416 LogFlowThisFuncLeave();
5417 return;
5418 }
5419
5420 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5421
5422 HRESULT rc = S_OK;
5423
5424 try
5425 {
5426 ULONG uLogHistoryCount = 3;
5427 ComPtr<ISystemProperties> systemProperties;
5428 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5429 if (FAILED(rc)) throw rc;
5430
5431 if (!systemProperties.isNull())
5432 {
5433 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5434 if (FAILED(rc)) throw rc;
5435 }
5436
5437 MachineState_T oldState = mData->mMachineState;
5438 i_setMachineState(MachineState_SettingUp);
5439 alock.release();
5440 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5441 {
5442 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5443 {
5444 AutoCaller mac(pMedium);
5445 if (FAILED(mac.rc())) throw mac.rc();
5446 Utf8Str strLocation = pMedium->i_getLocationFull();
5447 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5448 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5449 if (FAILED(rc)) throw rc;
5450 }
5451 if (pMedium->i_isMediumFormatFile())
5452 {
5453 ComPtr<IProgress> pProgress2;
5454 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5455 if (FAILED(rc)) throw rc;
5456 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5457 if (FAILED(rc)) throw rc;
5458 }
5459
5460 /* Close the medium, deliberately without checking the return
5461 * code, and without leaving any trace in the error info, as
5462 * a failure here is a very minor issue, which shouldn't happen
5463 * as above we even managed to delete the medium. */
5464 {
5465 ErrorInfoKeeper eik;
5466 pMedium->Close();
5467 }
5468 }
5469 i_setMachineState(oldState);
5470 alock.acquire();
5471
5472 // delete the files pushed on the task list by Machine::Delete()
5473 // (this includes saved states of the machine and snapshots and
5474 // medium storage files from the IMedium list passed in, and the
5475 // machine XML file)
5476 for (StringsList::const_iterator
5477 it = task.m_llFilesToDelete.begin();
5478 it != task.m_llFilesToDelete.end();
5479 ++it)
5480 {
5481 const Utf8Str &strFile = *it;
5482 LogFunc(("Deleting file %s\n", strFile.c_str()));
5483 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5484 if (FAILED(rc)) throw rc;
5485
5486 int vrc = RTFileDelete(strFile.c_str());
5487 if (RT_FAILURE(vrc))
5488 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5489 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5490 }
5491
5492 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5493 if (FAILED(rc)) throw rc;
5494
5495 /* delete the settings only when the file actually exists */
5496 if (mData->pMachineConfigFile->fileExists())
5497 {
5498 /* Delete any backup or uncommitted XML files. Ignore failures.
5499 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5500 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5501 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5502 RTFileDelete(otherXml.c_str());
5503 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5504 RTFileDelete(otherXml.c_str());
5505
5506 /* delete the Logs folder, nothing important should be left
5507 * there (we don't check for errors because the user might have
5508 * some private files there that we don't want to delete) */
5509 Utf8Str logFolder;
5510 getLogFolder(logFolder);
5511 Assert(logFolder.length());
5512 if (RTDirExists(logFolder.c_str()))
5513 {
5514 /* Delete all VBox.log[.N] files from the Logs folder
5515 * (this must be in sync with the rotation logic in
5516 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5517 * files that may have been created by the GUI. */
5518 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5519 RTFileDelete(log.c_str());
5520 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5521 RTFileDelete(log.c_str());
5522 for (ULONG i = uLogHistoryCount; i > 0; i--)
5523 {
5524 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5525 RTFileDelete(log.c_str());
5526 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5527 RTFileDelete(log.c_str());
5528 }
5529 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5530 RTFileDelete(log.c_str());
5531#if defined(RT_OS_WINDOWS)
5532 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5533 RTFileDelete(log.c_str());
5534 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5535 RTFileDelete(log.c_str());
5536#endif
5537
5538 RTDirRemove(logFolder.c_str());
5539 }
5540
5541 /* delete the Snapshots folder, nothing important should be left
5542 * there (we don't check for errors because the user might have
5543 * some private files there that we don't want to delete) */
5544 Utf8Str strFullSnapshotFolder;
5545 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5546 Assert(!strFullSnapshotFolder.isEmpty());
5547 if (RTDirExists(strFullSnapshotFolder.c_str()))
5548 RTDirRemove(strFullSnapshotFolder.c_str());
5549
5550 // delete the directory that contains the settings file, but only
5551 // if it matches the VM name
5552 Utf8Str settingsDir;
5553 if (i_isInOwnDir(&settingsDir))
5554 RTDirRemove(settingsDir.c_str());
5555 }
5556
5557 alock.release();
5558
5559 mParent->i_saveModifiedRegistries();
5560 }
5561 catch (HRESULT aRC) { rc = aRC; }
5562
5563 task.m_pProgress->i_notifyComplete(rc);
5564
5565 LogFlowThisFuncLeave();
5566}
5567
5568HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5569{
5570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5571
5572 HRESULT rc = i_checkStateDependency(MutableStateDep);
5573 if (FAILED(rc)) return rc;
5574
5575 if (mData->mRegistered)
5576 return setError(VBOX_E_INVALID_VM_STATE,
5577 tr("Cannot delete settings of a registered machine"));
5578
5579 // collect files to delete
5580 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5581 // machine config file
5582 if (mData->pMachineConfigFile->fileExists())
5583 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5584 // backup of machine config file
5585 Utf8Str strTmp(mData->m_strConfigFileFull);
5586 strTmp.append("-prev");
5587 if (RTFileExists(strTmp.c_str()))
5588 llFilesToDelete.push_back(strTmp);
5589
5590 RTCList<ComPtr<IMedium> > llMediums;
5591 for (size_t i = 0; i < aMedia.size(); ++i)
5592 {
5593 IMedium *pIMedium(aMedia[i]);
5594 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5595 if (pMedium.isNull())
5596 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
5597 SafeArray<BSTR> ids;
5598 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5599 if (FAILED(rc)) return rc;
5600 /* At this point the medium should not have any back references
5601 * anymore. If it has it is attached to another VM and *must* not
5602 * deleted. */
5603 if (ids.size() < 1)
5604 llMediums.append(pMedium);
5605 }
5606
5607 ComObjPtr<Progress> pProgress;
5608 pProgress.createObject();
5609 rc = pProgress->init(i_getVirtualBox(),
5610 static_cast<IMachine*>(this) /* aInitiator */,
5611 tr("Deleting files"),
5612 true /* fCancellable */,
5613 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5614 tr("Collecting file inventory"));
5615 if (FAILED(rc))
5616 return rc;
5617
5618 /* create and start the task on a separate thread (note that it will not
5619 * start working until we release alock) */
5620 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5621 rc = pTask->createThread();
5622 pTask = NULL;
5623 if (FAILED(rc))
5624 return rc;
5625
5626 pProgress.queryInterfaceTo(aProgress.asOutParam());
5627
5628 LogFlowFuncLeave();
5629
5630 return S_OK;
5631}
5632
5633HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5634{
5635 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5636
5637 ComObjPtr<Snapshot> pSnapshot;
5638 HRESULT rc;
5639
5640 if (aNameOrId.isEmpty())
5641 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5642 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5643 else
5644 {
5645 Guid uuid(aNameOrId);
5646 if (uuid.isValid())
5647 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5648 else
5649 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5650 }
5651 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5652
5653 return rc;
5654}
5655
5656HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5657 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5658{
5659 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5660
5661 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5662 if (FAILED(rc)) return rc;
5663
5664 ComObjPtr<SharedFolder> sharedFolder;
5665 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5666 if (SUCCEEDED(rc))
5667 return setError(VBOX_E_OBJECT_IN_USE,
5668 tr("Shared folder named '%s' already exists"),
5669 aName.c_str());
5670
5671 sharedFolder.createObject();
5672 rc = sharedFolder->init(i_getMachine(),
5673 aName,
5674 aHostPath,
5675 !!aWritable,
5676 !!aAutomount,
5677 aAutoMountPoint,
5678 true /* fFailOnError */);
5679 if (FAILED(rc)) return rc;
5680
5681 i_setModified(IsModified_SharedFolders);
5682 mHWData.backup();
5683 mHWData->mSharedFolders.push_back(sharedFolder);
5684
5685 /* inform the direct session if any */
5686 alock.release();
5687 i_onSharedFolderChange();
5688
5689 return S_OK;
5690}
5691
5692HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5693{
5694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5695
5696 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5697 if (FAILED(rc)) return rc;
5698
5699 ComObjPtr<SharedFolder> sharedFolder;
5700 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5701 if (FAILED(rc)) return rc;
5702
5703 i_setModified(IsModified_SharedFolders);
5704 mHWData.backup();
5705 mHWData->mSharedFolders.remove(sharedFolder);
5706
5707 /* inform the direct session if any */
5708 alock.release();
5709 i_onSharedFolderChange();
5710
5711 return S_OK;
5712}
5713
5714HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5715{
5716 /* start with No */
5717 *aCanShow = FALSE;
5718
5719 ComPtr<IInternalSessionControl> directControl;
5720 {
5721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5722
5723 if (mData->mSession.mState != SessionState_Locked)
5724 return setError(VBOX_E_INVALID_VM_STATE,
5725 tr("Machine is not locked for session (session state: %s)"),
5726 Global::stringifySessionState(mData->mSession.mState));
5727
5728 if (mData->mSession.mLockType == LockType_VM)
5729 directControl = mData->mSession.mDirectControl;
5730 }
5731
5732 /* ignore calls made after #OnSessionEnd() is called */
5733 if (!directControl)
5734 return S_OK;
5735
5736 LONG64 dummy;
5737 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5738}
5739
5740HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5741{
5742 ComPtr<IInternalSessionControl> directControl;
5743 {
5744 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5745
5746 if (mData->mSession.mState != SessionState_Locked)
5747 return setError(E_FAIL,
5748 tr("Machine is not locked for session (session state: %s)"),
5749 Global::stringifySessionState(mData->mSession.mState));
5750
5751 if (mData->mSession.mLockType == LockType_VM)
5752 directControl = mData->mSession.mDirectControl;
5753 }
5754
5755 /* ignore calls made after #OnSessionEnd() is called */
5756 if (!directControl)
5757 return S_OK;
5758
5759 BOOL dummy;
5760 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5761}
5762
5763#ifdef VBOX_WITH_GUEST_PROPS
5764/**
5765 * Look up a guest property in VBoxSVC's internal structures.
5766 */
5767HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5768 com::Utf8Str &aValue,
5769 LONG64 *aTimestamp,
5770 com::Utf8Str &aFlags) const
5771{
5772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5773
5774 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5775 if (it != mHWData->mGuestProperties.end())
5776 {
5777 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5778 aValue = it->second.strValue;
5779 *aTimestamp = it->second.mTimestamp;
5780 GuestPropWriteFlags(it->second.mFlags, szFlags);
5781 aFlags = Utf8Str(szFlags);
5782 }
5783
5784 return S_OK;
5785}
5786
5787/**
5788 * Query the VM that a guest property belongs to for the property.
5789 * @returns E_ACCESSDENIED if the VM process is not available or not
5790 * currently handling queries and the lookup should then be done in
5791 * VBoxSVC.
5792 */
5793HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5794 com::Utf8Str &aValue,
5795 LONG64 *aTimestamp,
5796 com::Utf8Str &aFlags) const
5797{
5798 HRESULT rc = S_OK;
5799 Bstr bstrValue;
5800 Bstr bstrFlags;
5801
5802 ComPtr<IInternalSessionControl> directControl;
5803 {
5804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5805 if (mData->mSession.mLockType == LockType_VM)
5806 directControl = mData->mSession.mDirectControl;
5807 }
5808
5809 /* ignore calls made after #OnSessionEnd() is called */
5810 if (!directControl)
5811 rc = E_ACCESSDENIED;
5812 else
5813 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5814 0 /* accessMode */,
5815 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5816
5817 aValue = bstrValue;
5818 aFlags = bstrFlags;
5819
5820 return rc;
5821}
5822#endif // VBOX_WITH_GUEST_PROPS
5823
5824HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5825 com::Utf8Str &aValue,
5826 LONG64 *aTimestamp,
5827 com::Utf8Str &aFlags)
5828{
5829#ifndef VBOX_WITH_GUEST_PROPS
5830 ReturnComNotImplemented();
5831#else // VBOX_WITH_GUEST_PROPS
5832
5833 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5834
5835 if (rc == E_ACCESSDENIED)
5836 /* The VM is not running or the service is not (yet) accessible */
5837 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5838 return rc;
5839#endif // VBOX_WITH_GUEST_PROPS
5840}
5841
5842HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5843{
5844 LONG64 dummyTimestamp;
5845 com::Utf8Str dummyFlags;
5846 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5847 return rc;
5848
5849}
5850HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5851{
5852 com::Utf8Str dummyFlags;
5853 com::Utf8Str dummyValue;
5854 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5855 return rc;
5856}
5857
5858#ifdef VBOX_WITH_GUEST_PROPS
5859/**
5860 * Set a guest property in VBoxSVC's internal structures.
5861 */
5862HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5863 const com::Utf8Str &aFlags, bool fDelete)
5864{
5865 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5866 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5867 if (FAILED(rc)) return rc;
5868
5869 try
5870 {
5871 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5872 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5873 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5874
5875 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
5876 return setError(E_INVALIDARG, tr("Properties with TRANSIENT or TRANSRESET flag cannot be set or modified if VM is not running"));
5877
5878 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5879 if (it == mHWData->mGuestProperties.end())
5880 {
5881 if (!fDelete)
5882 {
5883 i_setModified(IsModified_MachineData);
5884 mHWData.backupEx();
5885
5886 RTTIMESPEC time;
5887 HWData::GuestProperty prop;
5888 prop.strValue = aValue;
5889 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5890 prop.mFlags = fFlags;
5891 mHWData->mGuestProperties[aName] = prop;
5892 }
5893 }
5894 else
5895 {
5896 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5897 {
5898 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5899 }
5900 else
5901 {
5902 i_setModified(IsModified_MachineData);
5903 mHWData.backupEx();
5904
5905 /* The backupEx() operation invalidates our iterator,
5906 * so get a new one. */
5907 it = mHWData->mGuestProperties.find(aName);
5908 Assert(it != mHWData->mGuestProperties.end());
5909
5910 if (!fDelete)
5911 {
5912 RTTIMESPEC time;
5913 it->second.strValue = aValue;
5914 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5915 it->second.mFlags = fFlags;
5916 }
5917 else
5918 mHWData->mGuestProperties.erase(it);
5919 }
5920 }
5921
5922 if (SUCCEEDED(rc))
5923 {
5924 alock.release();
5925
5926 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fDelete);
5927 }
5928 }
5929 catch (std::bad_alloc &)
5930 {
5931 rc = E_OUTOFMEMORY;
5932 }
5933
5934 return rc;
5935}
5936
5937/**
5938 * Set a property on the VM that that property belongs to.
5939 * @returns E_ACCESSDENIED if the VM process is not available or not
5940 * currently handling queries and the setting should then be done in
5941 * VBoxSVC.
5942 */
5943HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5944 const com::Utf8Str &aFlags, bool fDelete)
5945{
5946 HRESULT rc;
5947
5948 try
5949 {
5950 ComPtr<IInternalSessionControl> directControl;
5951 {
5952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5953 if (mData->mSession.mLockType == LockType_VM)
5954 directControl = mData->mSession.mDirectControl;
5955 }
5956
5957 Bstr dummy1; /* will not be changed (setter) */
5958 Bstr dummy2; /* will not be changed (setter) */
5959 LONG64 dummy64;
5960 if (!directControl)
5961 rc = E_ACCESSDENIED;
5962 else
5963 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5964 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5965 fDelete ? 2 : 1 /* accessMode */,
5966 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5967 }
5968 catch (std::bad_alloc &)
5969 {
5970 rc = E_OUTOFMEMORY;
5971 }
5972
5973 return rc;
5974}
5975#endif // VBOX_WITH_GUEST_PROPS
5976
5977HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5978 const com::Utf8Str &aFlags)
5979{
5980#ifndef VBOX_WITH_GUEST_PROPS
5981 ReturnComNotImplemented();
5982#else // VBOX_WITH_GUEST_PROPS
5983 AssertReturn(RT_SUCCESS(GuestPropValidateName(aProperty.c_str(), (uint32_t)aProperty.length() + 1 /* '\0' */)), E_INVALIDARG);
5984 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValue.c_str(), (uint32_t)aValue.length() + 1 /* '\0' */)), E_INVALIDARG);
5985
5986 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5987 if (rc == E_ACCESSDENIED)
5988 /* The VM is not running or the service is not (yet) accessible */
5989 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5990 return rc;
5991#endif // VBOX_WITH_GUEST_PROPS
5992}
5993
5994HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5995{
5996 return setGuestProperty(aProperty, aValue, "");
5997}
5998
5999HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
6000{
6001#ifndef VBOX_WITH_GUEST_PROPS
6002 ReturnComNotImplemented();
6003#else // VBOX_WITH_GUEST_PROPS
6004 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
6005 if (rc == E_ACCESSDENIED)
6006 /* The VM is not running or the service is not (yet) accessible */
6007 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
6008 return rc;
6009#endif // VBOX_WITH_GUEST_PROPS
6010}
6011
6012#ifdef VBOX_WITH_GUEST_PROPS
6013/**
6014 * Enumerate the guest properties in VBoxSVC's internal structures.
6015 */
6016HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
6017 std::vector<com::Utf8Str> &aNames,
6018 std::vector<com::Utf8Str> &aValues,
6019 std::vector<LONG64> &aTimestamps,
6020 std::vector<com::Utf8Str> &aFlags)
6021{
6022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6023 Utf8Str strPatterns(aPatterns);
6024
6025 /*
6026 * Look for matching patterns and build up a list.
6027 */
6028 HWData::GuestPropertyMap propMap;
6029 for (HWData::GuestPropertyMap::const_iterator
6030 it = mHWData->mGuestProperties.begin();
6031 it != mHWData->mGuestProperties.end();
6032 ++it)
6033 {
6034 if ( strPatterns.isEmpty()
6035 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6036 RTSTR_MAX,
6037 it->first.c_str(),
6038 RTSTR_MAX,
6039 NULL)
6040 )
6041 propMap.insert(*it);
6042 }
6043
6044 alock.release();
6045
6046 /*
6047 * And build up the arrays for returning the property information.
6048 */
6049 size_t cEntries = propMap.size();
6050
6051 aNames.resize(cEntries);
6052 aValues.resize(cEntries);
6053 aTimestamps.resize(cEntries);
6054 aFlags.resize(cEntries);
6055
6056 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6057 size_t i = 0;
6058 for (HWData::GuestPropertyMap::const_iterator
6059 it = propMap.begin();
6060 it != propMap.end();
6061 ++it, ++i)
6062 {
6063 aNames[i] = it->first;
6064 aValues[i] = it->second.strValue;
6065 aTimestamps[i] = it->second.mTimestamp;
6066 GuestPropWriteFlags(it->second.mFlags, szFlags);
6067 aFlags[i] = Utf8Str(szFlags);
6068
6069 AssertReturn(RT_SUCCESS(GuestPropValidateName(aNames[i].c_str(), (uint32_t)aNames[i].length() + 1 /* '\0' */)), E_INVALIDARG);
6070 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValues[i].c_str(), (uint32_t)aValues[i].length() + 1 /* '\0' */)), E_INVALIDARG);
6071 }
6072
6073 return S_OK;
6074}
6075
6076/**
6077 * Enumerate the properties managed by a VM.
6078 * @returns E_ACCESSDENIED if the VM process is not available or not
6079 * currently handling queries and the setting should then be done in
6080 * VBoxSVC.
6081 */
6082HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6083 std::vector<com::Utf8Str> &aNames,
6084 std::vector<com::Utf8Str> &aValues,
6085 std::vector<LONG64> &aTimestamps,
6086 std::vector<com::Utf8Str> &aFlags)
6087{
6088 HRESULT rc;
6089 ComPtr<IInternalSessionControl> directControl;
6090 {
6091 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6092 if (mData->mSession.mLockType == LockType_VM)
6093 directControl = mData->mSession.mDirectControl;
6094 }
6095
6096 com::SafeArray<BSTR> bNames;
6097 com::SafeArray<BSTR> bValues;
6098 com::SafeArray<LONG64> bTimestamps;
6099 com::SafeArray<BSTR> bFlags;
6100
6101 if (!directControl)
6102 rc = E_ACCESSDENIED;
6103 else
6104 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6105 ComSafeArrayAsOutParam(bNames),
6106 ComSafeArrayAsOutParam(bValues),
6107 ComSafeArrayAsOutParam(bTimestamps),
6108 ComSafeArrayAsOutParam(bFlags));
6109 size_t i;
6110 aNames.resize(bNames.size());
6111 for (i = 0; i < bNames.size(); ++i)
6112 aNames[i] = Utf8Str(bNames[i]);
6113 aValues.resize(bValues.size());
6114 for (i = 0; i < bValues.size(); ++i)
6115 aValues[i] = Utf8Str(bValues[i]);
6116 aTimestamps.resize(bTimestamps.size());
6117 for (i = 0; i < bTimestamps.size(); ++i)
6118 aTimestamps[i] = bTimestamps[i];
6119 aFlags.resize(bFlags.size());
6120 for (i = 0; i < bFlags.size(); ++i)
6121 aFlags[i] = Utf8Str(bFlags[i]);
6122
6123 return rc;
6124}
6125#endif // VBOX_WITH_GUEST_PROPS
6126HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6127 std::vector<com::Utf8Str> &aNames,
6128 std::vector<com::Utf8Str> &aValues,
6129 std::vector<LONG64> &aTimestamps,
6130 std::vector<com::Utf8Str> &aFlags)
6131{
6132#ifndef VBOX_WITH_GUEST_PROPS
6133 ReturnComNotImplemented();
6134#else // VBOX_WITH_GUEST_PROPS
6135
6136 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6137
6138 if (rc == E_ACCESSDENIED)
6139 /* The VM is not running or the service is not (yet) accessible */
6140 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6141 return rc;
6142#endif // VBOX_WITH_GUEST_PROPS
6143}
6144
6145HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6146 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6147{
6148 MediumAttachmentList atts;
6149
6150 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6151 if (FAILED(rc)) return rc;
6152
6153 aMediumAttachments.resize(atts.size());
6154 size_t i = 0;
6155 for (MediumAttachmentList::const_iterator
6156 it = atts.begin();
6157 it != atts.end();
6158 ++it, ++i)
6159 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6160
6161 return S_OK;
6162}
6163
6164HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6165 LONG aControllerPort,
6166 LONG aDevice,
6167 ComPtr<IMediumAttachment> &aAttachment)
6168{
6169 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6170 aName.c_str(), aControllerPort, aDevice));
6171
6172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6173
6174 aAttachment = NULL;
6175
6176 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6177 aName,
6178 aControllerPort,
6179 aDevice);
6180 if (pAttach.isNull())
6181 return setError(VBOX_E_OBJECT_NOT_FOUND,
6182 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6183 aDevice, aControllerPort, aName.c_str());
6184
6185 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6186
6187 return S_OK;
6188}
6189
6190
6191HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6192 StorageBus_T aConnectionType,
6193 ComPtr<IStorageController> &aController)
6194{
6195 if ( (aConnectionType <= StorageBus_Null)
6196 || (aConnectionType > StorageBus_VirtioSCSI))
6197 return setError(E_INVALIDARG,
6198 tr("Invalid connection type: %d"),
6199 aConnectionType);
6200
6201 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6202
6203 HRESULT rc = i_checkStateDependency(MutableStateDep);
6204 if (FAILED(rc)) return rc;
6205
6206 /* try to find one with the name first. */
6207 ComObjPtr<StorageController> ctrl;
6208
6209 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6210 if (SUCCEEDED(rc))
6211 return setError(VBOX_E_OBJECT_IN_USE,
6212 tr("Storage controller named '%s' already exists"),
6213 aName.c_str());
6214
6215 ctrl.createObject();
6216
6217 /* get a new instance number for the storage controller */
6218 ULONG ulInstance = 0;
6219 bool fBootable = true;
6220 for (StorageControllerList::const_iterator
6221 it = mStorageControllers->begin();
6222 it != mStorageControllers->end();
6223 ++it)
6224 {
6225 if ((*it)->i_getStorageBus() == aConnectionType)
6226 {
6227 ULONG ulCurInst = (*it)->i_getInstance();
6228
6229 if (ulCurInst >= ulInstance)
6230 ulInstance = ulCurInst + 1;
6231
6232 /* Only one controller of each type can be marked as bootable. */
6233 if ((*it)->i_getBootable())
6234 fBootable = false;
6235 }
6236 }
6237
6238 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6239 if (FAILED(rc)) return rc;
6240
6241 i_setModified(IsModified_Storage);
6242 mStorageControllers.backup();
6243 mStorageControllers->push_back(ctrl);
6244
6245 ctrl.queryInterfaceTo(aController.asOutParam());
6246
6247 /* inform the direct session if any */
6248 alock.release();
6249 i_onStorageControllerChange(i_getId(), aName);
6250
6251 return S_OK;
6252}
6253
6254HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6255 ComPtr<IStorageController> &aStorageController)
6256{
6257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6258
6259 ComObjPtr<StorageController> ctrl;
6260
6261 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6262 if (SUCCEEDED(rc))
6263 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6264
6265 return rc;
6266}
6267
6268HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6269 ULONG aInstance,
6270 ComPtr<IStorageController> &aStorageController)
6271{
6272 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6273
6274 for (StorageControllerList::const_iterator
6275 it = mStorageControllers->begin();
6276 it != mStorageControllers->end();
6277 ++it)
6278 {
6279 if ( (*it)->i_getStorageBus() == aConnectionType
6280 && (*it)->i_getInstance() == aInstance)
6281 {
6282 (*it).queryInterfaceTo(aStorageController.asOutParam());
6283 return S_OK;
6284 }
6285 }
6286
6287 return setError(VBOX_E_OBJECT_NOT_FOUND,
6288 tr("Could not find a storage controller with instance number '%lu'"),
6289 aInstance);
6290}
6291
6292HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6293{
6294 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6295
6296 HRESULT rc = i_checkStateDependency(MutableStateDep);
6297 if (FAILED(rc)) return rc;
6298
6299 ComObjPtr<StorageController> ctrl;
6300
6301 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6302 if (SUCCEEDED(rc))
6303 {
6304 /* Ensure that only one controller of each type is marked as bootable. */
6305 if (aBootable == TRUE)
6306 {
6307 for (StorageControllerList::const_iterator
6308 it = mStorageControllers->begin();
6309 it != mStorageControllers->end();
6310 ++it)
6311 {
6312 ComObjPtr<StorageController> aCtrl = (*it);
6313
6314 if ( (aCtrl->i_getName() != aName)
6315 && aCtrl->i_getBootable() == TRUE
6316 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6317 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6318 {
6319 aCtrl->i_setBootable(FALSE);
6320 break;
6321 }
6322 }
6323 }
6324
6325 if (SUCCEEDED(rc))
6326 {
6327 ctrl->i_setBootable(aBootable);
6328 i_setModified(IsModified_Storage);
6329 }
6330 }
6331
6332 if (SUCCEEDED(rc))
6333 {
6334 /* inform the direct session if any */
6335 alock.release();
6336 i_onStorageControllerChange(i_getId(), aName);
6337 }
6338
6339 return rc;
6340}
6341
6342HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6343{
6344 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6345
6346 HRESULT rc = i_checkStateDependency(MutableStateDep);
6347 if (FAILED(rc)) return rc;
6348
6349 ComObjPtr<StorageController> ctrl;
6350 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6351 if (FAILED(rc)) return rc;
6352
6353 MediumAttachmentList llDetachedAttachments;
6354 {
6355 /* find all attached devices to the appropriate storage controller and detach them all */
6356 // make a temporary list because detachDevice invalidates iterators into
6357 // mMediumAttachments
6358 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6359
6360 for (MediumAttachmentList::const_iterator
6361 it = llAttachments2.begin();
6362 it != llAttachments2.end();
6363 ++it)
6364 {
6365 MediumAttachment *pAttachTemp = *it;
6366
6367 AutoCaller localAutoCaller(pAttachTemp);
6368 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6369
6370 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6371
6372 if (pAttachTemp->i_getControllerName() == aName)
6373 {
6374 llDetachedAttachments.push_back(pAttachTemp);
6375 rc = i_detachDevice(pAttachTemp, alock, NULL);
6376 if (FAILED(rc)) return rc;
6377 }
6378 }
6379 }
6380
6381 /* send event about detached devices before removing parent controller */
6382 for (MediumAttachmentList::const_iterator
6383 it = llDetachedAttachments.begin();
6384 it != llDetachedAttachments.end();
6385 ++it)
6386 {
6387 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6388 }
6389
6390 /* We can remove it now. */
6391 i_setModified(IsModified_Storage);
6392 mStorageControllers.backup();
6393
6394 ctrl->i_unshare();
6395
6396 mStorageControllers->remove(ctrl);
6397
6398 /* inform the direct session if any */
6399 alock.release();
6400 i_onStorageControllerChange(i_getId(), aName);
6401
6402 return S_OK;
6403}
6404
6405HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6406 ComPtr<IUSBController> &aController)
6407{
6408 if ( (aType <= USBControllerType_Null)
6409 || (aType >= USBControllerType_Last))
6410 return setError(E_INVALIDARG,
6411 tr("Invalid USB controller type: %d"),
6412 aType);
6413
6414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6415
6416 HRESULT rc = i_checkStateDependency(MutableStateDep);
6417 if (FAILED(rc)) return rc;
6418
6419 /* try to find one with the same type first. */
6420 ComObjPtr<USBController> ctrl;
6421
6422 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6423 if (SUCCEEDED(rc))
6424 return setError(VBOX_E_OBJECT_IN_USE,
6425 tr("USB controller named '%s' already exists"),
6426 aName.c_str());
6427
6428 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6429 ULONG maxInstances;
6430 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6431 if (FAILED(rc))
6432 return rc;
6433
6434 ULONG cInstances = i_getUSBControllerCountByType(aType);
6435 if (cInstances >= maxInstances)
6436 return setError(E_INVALIDARG,
6437 tr("Too many USB controllers of this type"));
6438
6439 ctrl.createObject();
6440
6441 rc = ctrl->init(this, aName, aType);
6442 if (FAILED(rc)) return rc;
6443
6444 i_setModified(IsModified_USB);
6445 mUSBControllers.backup();
6446 mUSBControllers->push_back(ctrl);
6447
6448 ctrl.queryInterfaceTo(aController.asOutParam());
6449
6450 /* inform the direct session if any */
6451 alock.release();
6452 i_onUSBControllerChange();
6453
6454 return S_OK;
6455}
6456
6457HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6458{
6459 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6460
6461 ComObjPtr<USBController> ctrl;
6462
6463 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6464 if (SUCCEEDED(rc))
6465 ctrl.queryInterfaceTo(aController.asOutParam());
6466
6467 return rc;
6468}
6469
6470HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6471 ULONG *aControllers)
6472{
6473 if ( (aType <= USBControllerType_Null)
6474 || (aType >= USBControllerType_Last))
6475 return setError(E_INVALIDARG,
6476 tr("Invalid USB controller type: %d"),
6477 aType);
6478
6479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6480
6481 ComObjPtr<USBController> ctrl;
6482
6483 *aControllers = i_getUSBControllerCountByType(aType);
6484
6485 return S_OK;
6486}
6487
6488HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6489{
6490
6491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6492
6493 HRESULT rc = i_checkStateDependency(MutableStateDep);
6494 if (FAILED(rc)) return rc;
6495
6496 ComObjPtr<USBController> ctrl;
6497 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6498 if (FAILED(rc)) return rc;
6499
6500 i_setModified(IsModified_USB);
6501 mUSBControllers.backup();
6502
6503 ctrl->i_unshare();
6504
6505 mUSBControllers->remove(ctrl);
6506
6507 /* inform the direct session if any */
6508 alock.release();
6509 i_onUSBControllerChange();
6510
6511 return S_OK;
6512}
6513
6514HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6515 ULONG *aOriginX,
6516 ULONG *aOriginY,
6517 ULONG *aWidth,
6518 ULONG *aHeight,
6519 BOOL *aEnabled)
6520{
6521 uint32_t u32OriginX= 0;
6522 uint32_t u32OriginY= 0;
6523 uint32_t u32Width = 0;
6524 uint32_t u32Height = 0;
6525 uint16_t u16Flags = 0;
6526
6527 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6528 int vrc = readSavedGuestScreenInfo(SavedStateStream, mSSData->strStateFilePath, aScreenId,
6529 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6530 if (RT_FAILURE(vrc))
6531 {
6532#ifdef RT_OS_WINDOWS
6533 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6534 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6535 * So just assign fEnable to TRUE again.
6536 * The right fix would be to change GUI API wrappers to make sure that parameters
6537 * are changed only if API succeeds.
6538 */
6539 *aEnabled = TRUE;
6540#endif
6541 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6542 tr("Saved guest size is not available (%Rrc)"),
6543 vrc);
6544 }
6545
6546 *aOriginX = u32OriginX;
6547 *aOriginY = u32OriginY;
6548 *aWidth = u32Width;
6549 *aHeight = u32Height;
6550 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6551
6552 return S_OK;
6553}
6554
6555HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6556 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6557{
6558 if (aScreenId != 0)
6559 return E_NOTIMPL;
6560
6561 if ( aBitmapFormat != BitmapFormat_BGR0
6562 && aBitmapFormat != BitmapFormat_BGRA
6563 && aBitmapFormat != BitmapFormat_RGBA
6564 && aBitmapFormat != BitmapFormat_PNG)
6565 return setError(E_NOTIMPL,
6566 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6567
6568 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6569
6570 uint8_t *pu8Data = NULL;
6571 uint32_t cbData = 0;
6572 uint32_t u32Width = 0;
6573 uint32_t u32Height = 0;
6574
6575 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6576 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 0 /* u32Type */,
6577 &pu8Data, &cbData, &u32Width, &u32Height);
6578 if (RT_FAILURE(vrc))
6579 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6580 tr("Saved thumbnail data is not available (%Rrc)"),
6581 vrc);
6582
6583 HRESULT hr = S_OK;
6584
6585 *aWidth = u32Width;
6586 *aHeight = u32Height;
6587
6588 if (cbData > 0)
6589 {
6590 /* Convert pixels to the format expected by the API caller. */
6591 if (aBitmapFormat == BitmapFormat_BGR0)
6592 {
6593 /* [0] B, [1] G, [2] R, [3] 0. */
6594 aData.resize(cbData);
6595 memcpy(&aData.front(), pu8Data, cbData);
6596 }
6597 else if (aBitmapFormat == BitmapFormat_BGRA)
6598 {
6599 /* [0] B, [1] G, [2] R, [3] A. */
6600 aData.resize(cbData);
6601 for (uint32_t i = 0; i < cbData; i += 4)
6602 {
6603 aData[i] = pu8Data[i];
6604 aData[i + 1] = pu8Data[i + 1];
6605 aData[i + 2] = pu8Data[i + 2];
6606 aData[i + 3] = 0xff;
6607 }
6608 }
6609 else if (aBitmapFormat == BitmapFormat_RGBA)
6610 {
6611 /* [0] R, [1] G, [2] B, [3] A. */
6612 aData.resize(cbData);
6613 for (uint32_t i = 0; i < cbData; i += 4)
6614 {
6615 aData[i] = pu8Data[i + 2];
6616 aData[i + 1] = pu8Data[i + 1];
6617 aData[i + 2] = pu8Data[i];
6618 aData[i + 3] = 0xff;
6619 }
6620 }
6621 else if (aBitmapFormat == BitmapFormat_PNG)
6622 {
6623 uint8_t *pu8PNG = NULL;
6624 uint32_t cbPNG = 0;
6625 uint32_t cxPNG = 0;
6626 uint32_t cyPNG = 0;
6627
6628 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6629
6630 if (RT_SUCCESS(vrc))
6631 {
6632 aData.resize(cbPNG);
6633 if (cbPNG)
6634 memcpy(&aData.front(), pu8PNG, cbPNG);
6635 }
6636 else
6637 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6638 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6639 vrc);
6640
6641 RTMemFree(pu8PNG);
6642 }
6643 }
6644
6645 freeSavedDisplayScreenshot(pu8Data);
6646
6647 return hr;
6648}
6649
6650HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6651 ULONG *aWidth,
6652 ULONG *aHeight,
6653 std::vector<BitmapFormat_T> &aBitmapFormats)
6654{
6655 if (aScreenId != 0)
6656 return E_NOTIMPL;
6657
6658 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6659
6660 uint8_t *pu8Data = NULL;
6661 uint32_t cbData = 0;
6662 uint32_t u32Width = 0;
6663 uint32_t u32Height = 0;
6664
6665 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6666 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6667 &pu8Data, &cbData, &u32Width, &u32Height);
6668
6669 if (RT_FAILURE(vrc))
6670 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6671 tr("Saved screenshot data is not available (%Rrc)"),
6672 vrc);
6673
6674 *aWidth = u32Width;
6675 *aHeight = u32Height;
6676 aBitmapFormats.resize(1);
6677 aBitmapFormats[0] = BitmapFormat_PNG;
6678
6679 freeSavedDisplayScreenshot(pu8Data);
6680
6681 return S_OK;
6682}
6683
6684HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6685 BitmapFormat_T aBitmapFormat,
6686 ULONG *aWidth,
6687 ULONG *aHeight,
6688 std::vector<BYTE> &aData)
6689{
6690 if (aScreenId != 0)
6691 return E_NOTIMPL;
6692
6693 if (aBitmapFormat != BitmapFormat_PNG)
6694 return E_NOTIMPL;
6695
6696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6697
6698 uint8_t *pu8Data = NULL;
6699 uint32_t cbData = 0;
6700 uint32_t u32Width = 0;
6701 uint32_t u32Height = 0;
6702
6703 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6704 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6705 &pu8Data, &cbData, &u32Width, &u32Height);
6706
6707 if (RT_FAILURE(vrc))
6708 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6709 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6710 vrc);
6711
6712 *aWidth = u32Width;
6713 *aHeight = u32Height;
6714
6715 aData.resize(cbData);
6716 if (cbData)
6717 memcpy(&aData.front(), pu8Data, cbData);
6718
6719 freeSavedDisplayScreenshot(pu8Data);
6720
6721 return S_OK;
6722}
6723
6724HRESULT Machine::hotPlugCPU(ULONG aCpu)
6725{
6726 HRESULT rc = S_OK;
6727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6728
6729 if (!mHWData->mCPUHotPlugEnabled)
6730 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6731
6732 if (aCpu >= mHWData->mCPUCount)
6733 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6734
6735 if (mHWData->mCPUAttached[aCpu])
6736 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6737
6738 rc = i_checkStateDependency(MutableOrRunningStateDep);
6739 if (FAILED(rc)) return rc;
6740
6741 alock.release();
6742 rc = i_onCPUChange(aCpu, false);
6743 alock.acquire();
6744 if (FAILED(rc)) return rc;
6745
6746 i_setModified(IsModified_MachineData);
6747 mHWData.backup();
6748 mHWData->mCPUAttached[aCpu] = true;
6749
6750 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6751 if (Global::IsOnline(mData->mMachineState))
6752 i_saveSettings(NULL, alock);
6753
6754 return S_OK;
6755}
6756
6757HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6758{
6759 HRESULT rc = S_OK;
6760
6761 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6762
6763 if (!mHWData->mCPUHotPlugEnabled)
6764 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6765
6766 if (aCpu >= SchemaDefs::MaxCPUCount)
6767 return setError(E_INVALIDARG,
6768 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6769 SchemaDefs::MaxCPUCount);
6770
6771 if (!mHWData->mCPUAttached[aCpu])
6772 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6773
6774 /* CPU 0 can't be detached */
6775 if (aCpu == 0)
6776 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6777
6778 rc = i_checkStateDependency(MutableOrRunningStateDep);
6779 if (FAILED(rc)) return rc;
6780
6781 alock.release();
6782 rc = i_onCPUChange(aCpu, true);
6783 alock.acquire();
6784 if (FAILED(rc)) return rc;
6785
6786 i_setModified(IsModified_MachineData);
6787 mHWData.backup();
6788 mHWData->mCPUAttached[aCpu] = false;
6789
6790 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6791 if (Global::IsOnline(mData->mMachineState))
6792 i_saveSettings(NULL, alock);
6793
6794 return S_OK;
6795}
6796
6797HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6798{
6799 *aAttached = false;
6800
6801 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6802
6803 /* If hotplug is enabled the CPU is always enabled. */
6804 if (!mHWData->mCPUHotPlugEnabled)
6805 {
6806 if (aCpu < mHWData->mCPUCount)
6807 *aAttached = true;
6808 }
6809 else
6810 {
6811 if (aCpu < SchemaDefs::MaxCPUCount)
6812 *aAttached = mHWData->mCPUAttached[aCpu];
6813 }
6814
6815 return S_OK;
6816}
6817
6818HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6819{
6820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6821
6822 Utf8Str log = i_getLogFilename(aIdx);
6823 if (!RTFileExists(log.c_str()))
6824 log.setNull();
6825 aFilename = log;
6826
6827 return S_OK;
6828}
6829
6830HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6831{
6832 if (aSize < 0)
6833 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6834
6835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6836
6837 HRESULT rc = S_OK;
6838 Utf8Str log = i_getLogFilename(aIdx);
6839
6840 /* do not unnecessarily hold the lock while doing something which does
6841 * not need the lock and potentially takes a long time. */
6842 alock.release();
6843
6844 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6845 * keeps the SOAP reply size under 1M for the webservice (we're using
6846 * base64 encoded strings for binary data for years now, avoiding the
6847 * expansion of each byte array element to approx. 25 bytes of XML. */
6848 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6849 aData.resize(cbData);
6850
6851 RTFILE LogFile;
6852 int vrc = RTFileOpen(&LogFile, log.c_str(),
6853 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6854 if (RT_SUCCESS(vrc))
6855 {
6856 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6857 if (RT_SUCCESS(vrc))
6858 aData.resize(cbData);
6859 else
6860 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6861 tr("Could not read log file '%s' (%Rrc)"),
6862 log.c_str(), vrc);
6863 RTFileClose(LogFile);
6864 }
6865 else
6866 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6867 tr("Could not open log file '%s' (%Rrc)"),
6868 log.c_str(), vrc);
6869
6870 if (FAILED(rc))
6871 aData.resize(0);
6872
6873 return rc;
6874}
6875
6876
6877/**
6878 * Currently this method doesn't attach device to the running VM,
6879 * just makes sure it's plugged on next VM start.
6880 */
6881HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6882{
6883 // lock scope
6884 {
6885 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6886
6887 HRESULT rc = i_checkStateDependency(MutableStateDep);
6888 if (FAILED(rc)) return rc;
6889
6890 ChipsetType_T aChipset = ChipsetType_PIIX3;
6891 COMGETTER(ChipsetType)(&aChipset);
6892
6893 if (aChipset != ChipsetType_ICH9)
6894 {
6895 return setError(E_INVALIDARG,
6896 tr("Host PCI attachment only supported with ICH9 chipset"));
6897 }
6898
6899 // check if device with this host PCI address already attached
6900 for (HWData::PCIDeviceAssignmentList::const_iterator
6901 it = mHWData->mPCIDeviceAssignments.begin();
6902 it != mHWData->mPCIDeviceAssignments.end();
6903 ++it)
6904 {
6905 LONG iHostAddress = -1;
6906 ComPtr<PCIDeviceAttachment> pAttach;
6907 pAttach = *it;
6908 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6909 if (iHostAddress == aHostAddress)
6910 return setError(E_INVALIDARG,
6911 tr("Device with host PCI address already attached to this VM"));
6912 }
6913
6914 ComObjPtr<PCIDeviceAttachment> pda;
6915 char name[32];
6916
6917 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6918 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6919 pda.createObject();
6920 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6921 i_setModified(IsModified_MachineData);
6922 mHWData.backup();
6923 mHWData->mPCIDeviceAssignments.push_back(pda);
6924 }
6925
6926 return S_OK;
6927}
6928
6929/**
6930 * Currently this method doesn't detach device from the running VM,
6931 * just makes sure it's not plugged on next VM start.
6932 */
6933HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6934{
6935 ComObjPtr<PCIDeviceAttachment> pAttach;
6936 bool fRemoved = false;
6937 HRESULT rc;
6938
6939 // lock scope
6940 {
6941 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6942
6943 rc = i_checkStateDependency(MutableStateDep);
6944 if (FAILED(rc)) return rc;
6945
6946 for (HWData::PCIDeviceAssignmentList::const_iterator
6947 it = mHWData->mPCIDeviceAssignments.begin();
6948 it != mHWData->mPCIDeviceAssignments.end();
6949 ++it)
6950 {
6951 LONG iHostAddress = -1;
6952 pAttach = *it;
6953 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6954 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6955 {
6956 i_setModified(IsModified_MachineData);
6957 mHWData.backup();
6958 mHWData->mPCIDeviceAssignments.remove(pAttach);
6959 fRemoved = true;
6960 break;
6961 }
6962 }
6963 }
6964
6965
6966 /* Fire event outside of the lock */
6967 if (fRemoved)
6968 {
6969 Assert(!pAttach.isNull());
6970 ComPtr<IEventSource> es;
6971 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6972 Assert(SUCCEEDED(rc));
6973 Bstr mid;
6974 rc = this->COMGETTER(Id)(mid.asOutParam());
6975 Assert(SUCCEEDED(rc));
6976 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6977 }
6978
6979 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6980 tr("No host PCI device %08x attached"),
6981 aHostAddress
6982 );
6983}
6984
6985HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6986{
6987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6988
6989 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6990 size_t i = 0;
6991 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6992 it = mHWData->mPCIDeviceAssignments.begin();
6993 it != mHWData->mPCIDeviceAssignments.end();
6994 ++it, ++i)
6995 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6996
6997 return S_OK;
6998}
6999
7000HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
7001{
7002 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
7003
7004 return S_OK;
7005}
7006
7007HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
7008{
7009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7010
7011 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
7012
7013 return S_OK;
7014}
7015
7016HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
7017{
7018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7019 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7020 if (SUCCEEDED(hrc))
7021 {
7022 hrc = mHWData.backupEx();
7023 if (SUCCEEDED(hrc))
7024 {
7025 i_setModified(IsModified_MachineData);
7026 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
7027 }
7028 }
7029 return hrc;
7030}
7031
7032HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
7033{
7034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7035 aTracingConfig = mHWData->mDebugging.strTracingConfig;
7036 return S_OK;
7037}
7038
7039HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
7040{
7041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7042 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7043 if (SUCCEEDED(hrc))
7044 {
7045 hrc = mHWData.backupEx();
7046 if (SUCCEEDED(hrc))
7047 {
7048 mHWData->mDebugging.strTracingConfig = aTracingConfig;
7049 if (SUCCEEDED(hrc))
7050 i_setModified(IsModified_MachineData);
7051 }
7052 }
7053 return hrc;
7054}
7055
7056HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
7057{
7058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7059
7060 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
7061
7062 return S_OK;
7063}
7064
7065HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
7066{
7067 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7068 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7069 if (SUCCEEDED(hrc))
7070 {
7071 hrc = mHWData.backupEx();
7072 if (SUCCEEDED(hrc))
7073 {
7074 i_setModified(IsModified_MachineData);
7075 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
7076 }
7077 }
7078 return hrc;
7079}
7080
7081HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7082{
7083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7084
7085 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7086
7087 return S_OK;
7088}
7089
7090HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7091{
7092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7093
7094 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7095 if ( SUCCEEDED(hrc)
7096 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7097 {
7098 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7099 int vrc;
7100
7101 if (aAutostartEnabled)
7102 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7103 else
7104 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7105
7106 if (RT_SUCCESS(vrc))
7107 {
7108 hrc = mHWData.backupEx();
7109 if (SUCCEEDED(hrc))
7110 {
7111 i_setModified(IsModified_MachineData);
7112 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7113 }
7114 }
7115 else if (vrc == VERR_NOT_SUPPORTED)
7116 hrc = setError(VBOX_E_NOT_SUPPORTED,
7117 tr("The VM autostart feature is not supported on this platform"));
7118 else if (vrc == VERR_PATH_NOT_FOUND)
7119 hrc = setError(E_FAIL,
7120 tr("The path to the autostart database is not set"));
7121 else
7122 hrc = setError(E_UNEXPECTED,
7123 aAutostartEnabled ?
7124 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
7125 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
7126 mUserData->s.strName.c_str(), vrc);
7127 }
7128 return hrc;
7129}
7130
7131HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7132{
7133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7134
7135 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7136
7137 return S_OK;
7138}
7139
7140HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7141{
7142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7143 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7144 if (SUCCEEDED(hrc))
7145 {
7146 hrc = mHWData.backupEx();
7147 if (SUCCEEDED(hrc))
7148 {
7149 i_setModified(IsModified_MachineData);
7150 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7151 }
7152 }
7153 return hrc;
7154}
7155
7156HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7157{
7158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7159
7160 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7161
7162 return S_OK;
7163}
7164
7165HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7166{
7167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7168 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7169 if ( SUCCEEDED(hrc)
7170 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7171 {
7172 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7173 int vrc;
7174
7175 if (aAutostopType != AutostopType_Disabled)
7176 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7177 else
7178 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7179
7180 if (RT_SUCCESS(vrc))
7181 {
7182 hrc = mHWData.backupEx();
7183 if (SUCCEEDED(hrc))
7184 {
7185 i_setModified(IsModified_MachineData);
7186 mHWData->mAutostart.enmAutostopType = aAutostopType;
7187 }
7188 }
7189 else if (vrc == VERR_NOT_SUPPORTED)
7190 hrc = setError(VBOX_E_NOT_SUPPORTED,
7191 tr("The VM autostop feature is not supported on this platform"));
7192 else if (vrc == VERR_PATH_NOT_FOUND)
7193 hrc = setError(E_FAIL,
7194 tr("The path to the autostart database is not set"));
7195 else
7196 hrc = setError(E_UNEXPECTED,
7197 aAutostopType != AutostopType_Disabled ?
7198 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
7199 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
7200 mUserData->s.strName.c_str(), vrc);
7201 }
7202 return hrc;
7203}
7204
7205HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7206{
7207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7208
7209 aDefaultFrontend = mHWData->mDefaultFrontend;
7210
7211 return S_OK;
7212}
7213
7214HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7215{
7216 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7217 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7218 if (SUCCEEDED(hrc))
7219 {
7220 hrc = mHWData.backupEx();
7221 if (SUCCEEDED(hrc))
7222 {
7223 i_setModified(IsModified_MachineData);
7224 mHWData->mDefaultFrontend = aDefaultFrontend;
7225 }
7226 }
7227 return hrc;
7228}
7229
7230HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7231{
7232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7233 size_t cbIcon = mUserData->s.ovIcon.size();
7234 aIcon.resize(cbIcon);
7235 if (cbIcon)
7236 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7237 return S_OK;
7238}
7239
7240HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7241{
7242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7243 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7244 if (SUCCEEDED(hrc))
7245 {
7246 i_setModified(IsModified_MachineData);
7247 mUserData.backup();
7248 size_t cbIcon = aIcon.size();
7249 mUserData->s.ovIcon.resize(cbIcon);
7250 if (cbIcon)
7251 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7252 }
7253 return hrc;
7254}
7255
7256HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7257{
7258#ifdef VBOX_WITH_USB
7259 *aUSBProxyAvailable = true;
7260#else
7261 *aUSBProxyAvailable = false;
7262#endif
7263 return S_OK;
7264}
7265
7266HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
7267{
7268 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7269
7270 *aVMProcessPriority = mUserData->s.enmVMPriority;
7271
7272 return S_OK;
7273}
7274
7275HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
7276{
7277 RT_NOREF(aVMProcessPriority);
7278 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7279 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7280 if (SUCCEEDED(hrc))
7281 {
7282 hrc = mUserData.backupEx();
7283 if (SUCCEEDED(hrc))
7284 {
7285 i_setModified(IsModified_MachineData);
7286 mUserData->s.enmVMPriority = aVMProcessPriority;
7287 }
7288 }
7289 alock.release();
7290 if (SUCCEEDED(hrc))
7291 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
7292 return hrc;
7293}
7294
7295HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7296 ComPtr<IProgress> &aProgress)
7297{
7298 ComObjPtr<Progress> pP;
7299 Progress *ppP = pP;
7300 IProgress *iP = static_cast<IProgress *>(ppP);
7301 IProgress **pProgress = &iP;
7302
7303 IMachine *pTarget = aTarget;
7304
7305 /* Convert the options. */
7306 RTCList<CloneOptions_T> optList;
7307 if (aOptions.size())
7308 for (size_t i = 0; i < aOptions.size(); ++i)
7309 optList.append(aOptions[i]);
7310
7311 if (optList.contains(CloneOptions_Link))
7312 {
7313 if (!i_isSnapshotMachine())
7314 return setError(E_INVALIDARG,
7315 tr("Linked clone can only be created from a snapshot"));
7316 if (aMode != CloneMode_MachineState)
7317 return setError(E_INVALIDARG,
7318 tr("Linked clone can only be created for a single machine state"));
7319 }
7320 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7321
7322 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7323
7324 HRESULT rc = pWorker->start(pProgress);
7325
7326 pP = static_cast<Progress *>(*pProgress);
7327 pP.queryInterfaceTo(aProgress.asOutParam());
7328
7329 return rc;
7330
7331}
7332
7333HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7334 const com::Utf8Str &aType,
7335 ComPtr<IProgress> &aProgress)
7336{
7337 LogFlowThisFuncEnter();
7338
7339 ComObjPtr<Progress> ptrProgress;
7340 HRESULT hrc = ptrProgress.createObject();
7341 if (SUCCEEDED(hrc))
7342 {
7343 com::Utf8Str strDefaultPath;
7344 if (aTargetPath.isEmpty())
7345 i_calculateFullPath(".", strDefaultPath);
7346
7347 /* Initialize our worker task */
7348 MachineMoveVM *pTask = NULL;
7349 try
7350 {
7351 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7352 }
7353 catch (std::bad_alloc &)
7354 {
7355 return E_OUTOFMEMORY;
7356 }
7357
7358 hrc = pTask->init();//no exceptions are thrown
7359
7360 if (SUCCEEDED(hrc))
7361 {
7362 hrc = pTask->createThread();
7363 pTask = NULL; /* Consumed by createThread(). */
7364 if (SUCCEEDED(hrc))
7365 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7366 else
7367 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7368 }
7369 else
7370 delete pTask;
7371 }
7372
7373 LogFlowThisFuncLeave();
7374 return hrc;
7375
7376}
7377
7378HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7379{
7380 NOREF(aProgress);
7381 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7382
7383 // This check should always fail.
7384 HRESULT rc = i_checkStateDependency(MutableStateDep);
7385 if (FAILED(rc)) return rc;
7386
7387 AssertFailedReturn(E_NOTIMPL);
7388}
7389
7390HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7391{
7392 NOREF(aSavedStateFile);
7393 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7394
7395 // This check should always fail.
7396 HRESULT rc = i_checkStateDependency(MutableStateDep);
7397 if (FAILED(rc)) return rc;
7398
7399 AssertFailedReturn(E_NOTIMPL);
7400}
7401
7402HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7403{
7404 NOREF(aFRemoveFile);
7405 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7406
7407 // This check should always fail.
7408 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7409 if (FAILED(rc)) return rc;
7410
7411 AssertFailedReturn(E_NOTIMPL);
7412}
7413
7414// public methods for internal purposes
7415/////////////////////////////////////////////////////////////////////////////
7416
7417/**
7418 * Adds the given IsModified_* flag to the dirty flags of the machine.
7419 * This must be called either during i_loadSettings or under the machine write lock.
7420 * @param fl Flag
7421 * @param fAllowStateModification If state modifications are allowed.
7422 */
7423void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7424{
7425 mData->flModifications |= fl;
7426 if (fAllowStateModification && i_isStateModificationAllowed())
7427 mData->mCurrentStateModified = true;
7428}
7429
7430/**
7431 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7432 * care of the write locking.
7433 *
7434 * @param fModification The flag to add.
7435 * @param fAllowStateModification If state modifications are allowed.
7436 */
7437void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7438{
7439 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7440 i_setModified(fModification, fAllowStateModification);
7441}
7442
7443/**
7444 * Saves the registry entry of this machine to the given configuration node.
7445 *
7446 * @param data Machine registry data.
7447 *
7448 * @note locks this object for reading.
7449 */
7450HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7451{
7452 AutoLimitedCaller autoCaller(this);
7453 AssertComRCReturnRC(autoCaller.rc());
7454
7455 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7456
7457 data.uuid = mData->mUuid;
7458 data.strSettingsFile = mData->m_strConfigFile;
7459
7460 return S_OK;
7461}
7462
7463/**
7464 * Calculates the absolute path of the given path taking the directory of the
7465 * machine settings file as the current directory.
7466 *
7467 * @param strPath Path to calculate the absolute path for.
7468 * @param aResult Where to put the result (used only on success, can be the
7469 * same Utf8Str instance as passed in @a aPath).
7470 * @return IPRT result.
7471 *
7472 * @note Locks this object for reading.
7473 */
7474int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7475{
7476 AutoCaller autoCaller(this);
7477 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7478
7479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7480
7481 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7482
7483 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7484
7485 strSettingsDir.stripFilename();
7486 char szFolder[RTPATH_MAX];
7487 size_t cbFolder = sizeof(szFolder);
7488 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7489 if (RT_SUCCESS(vrc))
7490 aResult = szFolder;
7491
7492 return vrc;
7493}
7494
7495/**
7496 * Copies strSource to strTarget, making it relative to the machine folder
7497 * if it is a subdirectory thereof, or simply copying it otherwise.
7498 *
7499 * @param strSource Path to evaluate and copy.
7500 * @param strTarget Buffer to receive target path.
7501 *
7502 * @note Locks this object for reading.
7503 */
7504void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7505 Utf8Str &strTarget)
7506{
7507 AutoCaller autoCaller(this);
7508 AssertComRCReturn(autoCaller.rc(), (void)0);
7509
7510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7511
7512 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7513 // use strTarget as a temporary buffer to hold the machine settings dir
7514 strTarget = mData->m_strConfigFileFull;
7515 strTarget.stripFilename();
7516 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7517 {
7518 // is relative: then append what's left
7519 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7520 // for empty paths (only possible for subdirs) use "." to avoid
7521 // triggering default settings for not present config attributes.
7522 if (strTarget.isEmpty())
7523 strTarget = ".";
7524 }
7525 else
7526 // is not relative: then overwrite
7527 strTarget = strSource;
7528}
7529
7530/**
7531 * Returns the full path to the machine's log folder in the
7532 * \a aLogFolder argument.
7533 */
7534void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7535{
7536 AutoCaller autoCaller(this);
7537 AssertComRCReturnVoid(autoCaller.rc());
7538
7539 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7540
7541 char szTmp[RTPATH_MAX];
7542 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7543 if (RT_SUCCESS(vrc))
7544 {
7545 if (szTmp[0] && !mUserData.isNull())
7546 {
7547 char szTmp2[RTPATH_MAX];
7548 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7549 if (RT_SUCCESS(vrc))
7550 aLogFolder.printf("%s%c%s",
7551 szTmp2,
7552 RTPATH_DELIMITER,
7553 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7554 }
7555 else
7556 vrc = VERR_PATH_IS_RELATIVE;
7557 }
7558
7559 if (RT_FAILURE(vrc))
7560 {
7561 // fallback if VBOX_USER_LOGHOME is not set or invalid
7562 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7563 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7564 aLogFolder.append(RTPATH_DELIMITER);
7565 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7566 }
7567}
7568
7569/**
7570 * Returns the full path to the machine's log file for an given index.
7571 */
7572Utf8Str Machine::i_getLogFilename(ULONG idx)
7573{
7574 Utf8Str logFolder;
7575 getLogFolder(logFolder);
7576 Assert(logFolder.length());
7577
7578 Utf8Str log;
7579 if (idx == 0)
7580 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7581#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7582 else if (idx == 1)
7583 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7584 else
7585 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7586#else
7587 else
7588 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7589#endif
7590 return log;
7591}
7592
7593/**
7594 * Returns the full path to the machine's hardened log file.
7595 */
7596Utf8Str Machine::i_getHardeningLogFilename(void)
7597{
7598 Utf8Str strFilename;
7599 getLogFolder(strFilename);
7600 Assert(strFilename.length());
7601 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7602 return strFilename;
7603}
7604
7605/**
7606 * Returns the default NVRAM filename based on the location of the VM config.
7607 * Note that this is a relative path.
7608 */
7609Utf8Str Machine::i_getDefaultNVRAMFilename()
7610{
7611 AutoCaller autoCaller(this);
7612 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7613
7614 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7615
7616 if (i_isSnapshotMachine())
7617 return Utf8Str::Empty;
7618
7619 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7620 strNVRAMFilePath.stripPath();
7621 strNVRAMFilePath.stripSuffix();
7622 strNVRAMFilePath += ".nvram";
7623
7624 return strNVRAMFilePath;
7625}
7626
7627/**
7628 * Returns the NVRAM filename for a new snapshot. This intentionally works
7629 * similarly to the saved state file naming. Note that this is usually
7630 * a relative path, unless the snapshot folder is absolute.
7631 */
7632Utf8Str Machine::i_getSnapshotNVRAMFilename()
7633{
7634 AutoCaller autoCaller(this);
7635 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7636
7637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7638
7639 RTTIMESPEC ts;
7640 RTTimeNow(&ts);
7641 RTTIME time;
7642 RTTimeExplode(&time, &ts);
7643
7644 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7645 strNVRAMFilePath += RTPATH_DELIMITER;
7646 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7647 time.i32Year, time.u8Month, time.u8MonthDay,
7648 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7649
7650 return strNVRAMFilePath;
7651}
7652
7653/**
7654 * Returns the version of the settings file.
7655 */
7656SettingsVersion_T Machine::i_getSettingsVersion(void)
7657{
7658 AutoCaller autoCaller(this);
7659 AssertComRCReturn(autoCaller.rc(), SettingsVersion_Null);
7660
7661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7662
7663 return mData->pMachineConfigFile->getSettingsVersion();
7664}
7665
7666/**
7667 * Composes a unique saved state filename based on the current system time. The filename is
7668 * granular to the second so this will work so long as no more than one snapshot is taken on
7669 * a machine per second.
7670 *
7671 * Before version 4.1, we used this formula for saved state files:
7672 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7673 * which no longer works because saved state files can now be shared between the saved state of the
7674 * "saved" machine and an online snapshot, and the following would cause problems:
7675 * 1) save machine
7676 * 2) create online snapshot from that machine state --> reusing saved state file
7677 * 3) save machine again --> filename would be reused, breaking the online snapshot
7678 *
7679 * So instead we now use a timestamp.
7680 *
7681 * @param strStateFilePath
7682 */
7683
7684void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7685{
7686 AutoCaller autoCaller(this);
7687 AssertComRCReturnVoid(autoCaller.rc());
7688
7689 {
7690 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7691 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7692 }
7693
7694 RTTIMESPEC ts;
7695 RTTimeNow(&ts);
7696 RTTIME time;
7697 RTTimeExplode(&time, &ts);
7698
7699 strStateFilePath += RTPATH_DELIMITER;
7700 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7701 time.i32Year, time.u8Month, time.u8MonthDay,
7702 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7703}
7704
7705/**
7706 * Returns whether at least one USB controller is present for the VM.
7707 */
7708bool Machine::i_isUSBControllerPresent()
7709{
7710 AutoCaller autoCaller(this);
7711 AssertComRCReturn(autoCaller.rc(), false);
7712
7713 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7714
7715 return (mUSBControllers->size() > 0);
7716}
7717
7718
7719/**
7720 * @note Locks this object for writing, calls the client process
7721 * (inside the lock).
7722 */
7723HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7724 const Utf8Str &strFrontend,
7725 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7726 ProgressProxy *aProgress)
7727{
7728 LogFlowThisFuncEnter();
7729
7730 AssertReturn(aControl, E_FAIL);
7731 AssertReturn(aProgress, E_FAIL);
7732 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7733
7734 AutoCaller autoCaller(this);
7735 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7736
7737 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7738
7739 if (!mData->mRegistered)
7740 return setError(E_UNEXPECTED,
7741 tr("The machine '%s' is not registered"),
7742 mUserData->s.strName.c_str());
7743
7744 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7745
7746 /* The process started when launching a VM with separate UI/VM processes is always
7747 * the UI process, i.e. needs special handling as it won't claim the session. */
7748 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7749
7750 if (fSeparate)
7751 {
7752 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7753 return setError(VBOX_E_INVALID_OBJECT_STATE,
7754 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7755 mUserData->s.strName.c_str());
7756 }
7757 else
7758 {
7759 if ( mData->mSession.mState == SessionState_Locked
7760 || mData->mSession.mState == SessionState_Spawning
7761 || mData->mSession.mState == SessionState_Unlocking)
7762 return setError(VBOX_E_INVALID_OBJECT_STATE,
7763 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7764 mUserData->s.strName.c_str());
7765
7766 /* may not be busy */
7767 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7768 }
7769
7770 /* Hardening logging */
7771#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7772 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7773 {
7774 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7775 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7776 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7777 {
7778 Utf8Str strStartupLogDir = strHardeningLogFile;
7779 strStartupLogDir.stripFilename();
7780 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7781 file without stripping the file. */
7782 }
7783 strSupHardeningLogArg.append(strHardeningLogFile);
7784
7785 /* Remove legacy log filename to avoid confusion. */
7786 Utf8Str strOldStartupLogFile;
7787 getLogFolder(strOldStartupLogFile);
7788 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7789 RTFileDelete(strOldStartupLogFile.c_str());
7790 }
7791#else
7792 Utf8Str strSupHardeningLogArg;
7793#endif
7794
7795 Utf8Str strAppOverride;
7796#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7797 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7798#endif
7799
7800 bool fUseVBoxSDS = false;
7801 Utf8Str strCanonicalName;
7802 if (false)
7803 { }
7804#ifdef VBOX_WITH_QTGUI
7805 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7806 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7807 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7808 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7809 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7810 {
7811 strCanonicalName = "GUI/Qt";
7812 fUseVBoxSDS = true;
7813 }
7814#endif
7815#ifdef VBOX_WITH_VBOXSDL
7816 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7817 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7818 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7819 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7820 {
7821 strCanonicalName = "GUI/SDL";
7822 fUseVBoxSDS = true;
7823 }
7824#endif
7825#ifdef VBOX_WITH_HEADLESS
7826 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7827 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7828 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7829 {
7830 strCanonicalName = "headless";
7831 }
7832#endif
7833 else
7834 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7835
7836 Utf8Str idStr = mData->mUuid.toString();
7837 Utf8Str const &strMachineName = mUserData->s.strName;
7838 RTPROCESS pid = NIL_RTPROCESS;
7839
7840#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7841 RT_NOREF(fUseVBoxSDS);
7842#else
7843 DWORD idCallerSession = ~(DWORD)0;
7844 if (fUseVBoxSDS)
7845 {
7846 /*
7847 * The VBoxSDS should be used for process launching the VM with
7848 * GUI only if the caller and the VBoxSDS are in different Windows
7849 * sessions and the caller in the interactive one.
7850 */
7851 fUseVBoxSDS = false;
7852
7853 /* Get windows session of the current process. The process token used
7854 due to several reasons:
7855 1. The token is absent for the current thread except someone set it
7856 for us.
7857 2. Needs to get the id of the session where the process is started.
7858 We only need to do this once, though. */
7859 static DWORD s_idCurrentSession = ~(DWORD)0;
7860 DWORD idCurrentSession = s_idCurrentSession;
7861 if (idCurrentSession == ~(DWORD)0)
7862 {
7863 HANDLE hCurrentProcessToken = NULL;
7864 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7865 {
7866 DWORD cbIgn = 0;
7867 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7868 s_idCurrentSession = idCurrentSession;
7869 else
7870 {
7871 idCurrentSession = ~(DWORD)0;
7872 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7873 }
7874 CloseHandle(hCurrentProcessToken);
7875 }
7876 else
7877 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7878 }
7879
7880 /* get the caller's session */
7881 HRESULT hrc = CoImpersonateClient();
7882 if (SUCCEEDED(hrc))
7883 {
7884 HANDLE hCallerThreadToken;
7885 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7886 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7887 &hCallerThreadToken))
7888 {
7889 SetLastError(NO_ERROR);
7890 DWORD cbIgn = 0;
7891 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7892 {
7893 /* Only need to use SDS if the session ID differs: */
7894 if (idCurrentSession != idCallerSession)
7895 {
7896 fUseVBoxSDS = false;
7897
7898 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7899 DWORD cbTokenGroups = 0;
7900 PTOKEN_GROUPS pTokenGroups = NULL;
7901 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7902 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7903 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7904 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7905 {
7906 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7907 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7908 PSID pInteractiveSid = NULL;
7909 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7910 {
7911 /* Iterate over the groups looking for the interactive SID: */
7912 fUseVBoxSDS = false;
7913 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7914 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7915 {
7916 fUseVBoxSDS = true;
7917 break;
7918 }
7919 FreeSid(pInteractiveSid);
7920 }
7921 }
7922 else
7923 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7924 RTMemTmpFree(pTokenGroups);
7925 }
7926 }
7927 else
7928 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7929 CloseHandle(hCallerThreadToken);
7930 }
7931 else
7932 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7933 CoRevertToSelf();
7934 }
7935 else
7936 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7937 }
7938 if (fUseVBoxSDS)
7939 {
7940 /* connect to VBoxSDS */
7941 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7942 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7943 if (FAILED(rc))
7944 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7945 strMachineName.c_str());
7946
7947 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7948 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7949 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7950 service to access the files. */
7951 rc = CoSetProxyBlanket(pVBoxSDS,
7952 RPC_C_AUTHN_DEFAULT,
7953 RPC_C_AUTHZ_DEFAULT,
7954 COLE_DEFAULT_PRINCIPAL,
7955 RPC_C_AUTHN_LEVEL_DEFAULT,
7956 RPC_C_IMP_LEVEL_IMPERSONATE,
7957 NULL,
7958 EOAC_DEFAULT);
7959 if (FAILED(rc))
7960 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7961
7962 size_t const cEnvVars = aEnvironmentChanges.size();
7963 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7964 for (size_t i = 0; i < cEnvVars; i++)
7965 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7966
7967 ULONG uPid = 0;
7968 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7969 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7970 idCallerSession, &uPid);
7971 if (FAILED(rc))
7972 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7973 pid = (RTPROCESS)uPid;
7974 }
7975 else
7976#endif /* VBOX_WITH_SDS && RT_OS_WINDOWS */
7977 {
7978 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7979 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7980 if (RT_FAILURE(vrc))
7981 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7982 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7983 }
7984
7985 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7986 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7987
7988 if (!fSeparate)
7989 {
7990 /*
7991 * Note that we don't release the lock here before calling the client,
7992 * because it doesn't need to call us back if called with a NULL argument.
7993 * Releasing the lock here is dangerous because we didn't prepare the
7994 * launch data yet, but the client we've just started may happen to be
7995 * too fast and call LockMachine() that will fail (because of PID, etc.),
7996 * so that the Machine will never get out of the Spawning session state.
7997 */
7998
7999 /* inform the session that it will be a remote one */
8000 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8001#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8002 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8003#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8004 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8005#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8006 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8007
8008 if (FAILED(rc))
8009 {
8010 /* restore the session state */
8011 mData->mSession.mState = SessionState_Unlocked;
8012 alock.release();
8013 mParent->i_addProcessToReap(pid);
8014 /* The failure may occur w/o any error info (from RPC), so provide one */
8015 return setError(VBOX_E_VM_ERROR,
8016 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8017 }
8018
8019 /* attach launch data to the machine */
8020 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8021 mData->mSession.mRemoteControls.push_back(aControl);
8022 mData->mSession.mProgress = aProgress;
8023 mData->mSession.mPID = pid;
8024 mData->mSession.mState = SessionState_Spawning;
8025 Assert(strCanonicalName.isNotEmpty());
8026 mData->mSession.mName = strCanonicalName;
8027 }
8028 else
8029 {
8030 /* For separate UI process we declare the launch as completed instantly, as the
8031 * actual headless VM start may or may not come. No point in remembering anything
8032 * yet, as what matters for us is when the headless VM gets started. */
8033 aProgress->i_notifyComplete(S_OK);
8034 }
8035
8036 alock.release();
8037 mParent->i_addProcessToReap(pid);
8038
8039 LogFlowThisFuncLeave();
8040 return S_OK;
8041}
8042
8043/**
8044 * Returns @c true if the given session machine instance has an open direct
8045 * session (and optionally also for direct sessions which are closing) and
8046 * returns the session control machine instance if so.
8047 *
8048 * Note that when the method returns @c false, the arguments remain unchanged.
8049 *
8050 * @param aMachine Session machine object.
8051 * @param aControl Direct session control object (optional).
8052 * @param aRequireVM If true then only allow VM sessions.
8053 * @param aAllowClosing If true then additionally a session which is currently
8054 * being closed will also be allowed.
8055 *
8056 * @note locks this object for reading.
8057 */
8058bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8059 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8060 bool aRequireVM /*= false*/,
8061 bool aAllowClosing /*= false*/)
8062{
8063 AutoLimitedCaller autoCaller(this);
8064 AssertComRCReturn(autoCaller.rc(), false);
8065
8066 /* just return false for inaccessible machines */
8067 if (getObjectState().getState() != ObjectState::Ready)
8068 return false;
8069
8070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8071
8072 if ( ( mData->mSession.mState == SessionState_Locked
8073 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
8074 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8075 )
8076 {
8077 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8078
8079 aMachine = mData->mSession.mMachine;
8080
8081 if (aControl != NULL)
8082 *aControl = mData->mSession.mDirectControl;
8083
8084 return true;
8085 }
8086
8087 return false;
8088}
8089
8090/**
8091 * Returns @c true if the given machine has an spawning direct session.
8092 *
8093 * @note locks this object for reading.
8094 */
8095bool Machine::i_isSessionSpawning()
8096{
8097 AutoLimitedCaller autoCaller(this);
8098 AssertComRCReturn(autoCaller.rc(), false);
8099
8100 /* just return false for inaccessible machines */
8101 if (getObjectState().getState() != ObjectState::Ready)
8102 return false;
8103
8104 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8105
8106 if (mData->mSession.mState == SessionState_Spawning)
8107 return true;
8108
8109 return false;
8110}
8111
8112/**
8113 * Called from the client watcher thread to check for unexpected client process
8114 * death during Session_Spawning state (e.g. before it successfully opened a
8115 * direct session).
8116 *
8117 * On Win32 and on OS/2, this method is called only when we've got the
8118 * direct client's process termination notification, so it always returns @c
8119 * true.
8120 *
8121 * On other platforms, this method returns @c true if the client process is
8122 * terminated and @c false if it's still alive.
8123 *
8124 * @note Locks this object for writing.
8125 */
8126bool Machine::i_checkForSpawnFailure()
8127{
8128 AutoCaller autoCaller(this);
8129 if (!autoCaller.isOk())
8130 {
8131 /* nothing to do */
8132 LogFlowThisFunc(("Already uninitialized!\n"));
8133 return true;
8134 }
8135
8136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8137
8138 if (mData->mSession.mState != SessionState_Spawning)
8139 {
8140 /* nothing to do */
8141 LogFlowThisFunc(("Not spawning any more!\n"));
8142 return true;
8143 }
8144
8145 HRESULT rc = S_OK;
8146
8147 /* PID not yet initialized, skip check. */
8148 if (mData->mSession.mPID == NIL_RTPROCESS)
8149 return false;
8150
8151 RTPROCSTATUS status;
8152 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8153
8154 if (vrc != VERR_PROCESS_RUNNING)
8155 {
8156 Utf8Str strExtraInfo;
8157
8158#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8159 /* If the startup logfile exists and is of non-zero length, tell the
8160 user to look there for more details to encourage them to attach it
8161 when reporting startup issues. */
8162 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8163 uint64_t cbStartupLogFile = 0;
8164 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
8165 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8166 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
8167#endif
8168
8169 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8170 rc = setError(E_FAIL,
8171 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8172 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8173 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8174 rc = setError(E_FAIL,
8175 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8176 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8177 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8178 rc = setError(E_FAIL,
8179 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8180 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8181 else
8182 rc = setErrorBoth(E_FAIL, vrc,
8183 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8184 i_getName().c_str(), vrc, strExtraInfo.c_str());
8185 }
8186
8187 if (FAILED(rc))
8188 {
8189 /* Close the remote session, remove the remote control from the list
8190 * and reset session state to Closed (@note keep the code in sync with
8191 * the relevant part in LockMachine()). */
8192
8193 Assert(mData->mSession.mRemoteControls.size() == 1);
8194 if (mData->mSession.mRemoteControls.size() == 1)
8195 {
8196 ErrorInfoKeeper eik;
8197 mData->mSession.mRemoteControls.front()->Uninitialize();
8198 }
8199
8200 mData->mSession.mRemoteControls.clear();
8201 mData->mSession.mState = SessionState_Unlocked;
8202
8203 /* finalize the progress after setting the state */
8204 if (!mData->mSession.mProgress.isNull())
8205 {
8206 mData->mSession.mProgress->notifyComplete(rc);
8207 mData->mSession.mProgress.setNull();
8208 }
8209
8210 mData->mSession.mPID = NIL_RTPROCESS;
8211
8212 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
8213 return true;
8214 }
8215
8216 return false;
8217}
8218
8219/**
8220 * Checks whether the machine can be registered. If so, commits and saves
8221 * all settings.
8222 *
8223 * @note Must be called from mParent's write lock. Locks this object and
8224 * children for writing.
8225 */
8226HRESULT Machine::i_prepareRegister()
8227{
8228 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8229
8230 AutoLimitedCaller autoCaller(this);
8231 AssertComRCReturnRC(autoCaller.rc());
8232
8233 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8234
8235 /* wait for state dependents to drop to zero */
8236 i_ensureNoStateDependencies(alock);
8237
8238 if (!mData->mAccessible)
8239 return setError(VBOX_E_INVALID_OBJECT_STATE,
8240 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8241 mUserData->s.strName.c_str(),
8242 mData->mUuid.toString().c_str());
8243
8244 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8245
8246 if (mData->mRegistered)
8247 return setError(VBOX_E_INVALID_OBJECT_STATE,
8248 tr("The machine '%s' with UUID {%s} is already registered"),
8249 mUserData->s.strName.c_str(),
8250 mData->mUuid.toString().c_str());
8251
8252 HRESULT rc = S_OK;
8253
8254 // Ensure the settings are saved. If we are going to be registered and
8255 // no config file exists yet, create it by calling i_saveSettings() too.
8256 if ( (mData->flModifications)
8257 || (!mData->pMachineConfigFile->fileExists())
8258 )
8259 {
8260 rc = i_saveSettings(NULL, alock);
8261 // no need to check whether VirtualBox.xml needs saving too since
8262 // we can't have a machine XML file rename pending
8263 if (FAILED(rc)) return rc;
8264 }
8265
8266 /* more config checking goes here */
8267
8268 if (SUCCEEDED(rc))
8269 {
8270 /* we may have had implicit modifications we want to fix on success */
8271 i_commit();
8272
8273 mData->mRegistered = true;
8274 }
8275 else
8276 {
8277 /* we may have had implicit modifications we want to cancel on failure*/
8278 i_rollback(false /* aNotify */);
8279 }
8280
8281 return rc;
8282}
8283
8284/**
8285 * Increases the number of objects dependent on the machine state or on the
8286 * registered state. Guarantees that these two states will not change at least
8287 * until #i_releaseStateDependency() is called.
8288 *
8289 * Depending on the @a aDepType value, additional state checks may be made.
8290 * These checks will set extended error info on failure. See
8291 * #i_checkStateDependency() for more info.
8292 *
8293 * If this method returns a failure, the dependency is not added and the caller
8294 * is not allowed to rely on any particular machine state or registration state
8295 * value and may return the failed result code to the upper level.
8296 *
8297 * @param aDepType Dependency type to add.
8298 * @param aState Current machine state (NULL if not interested).
8299 * @param aRegistered Current registered state (NULL if not interested).
8300 *
8301 * @note Locks this object for writing.
8302 */
8303HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8304 MachineState_T *aState /* = NULL */,
8305 BOOL *aRegistered /* = NULL */)
8306{
8307 AutoCaller autoCaller(this);
8308 AssertComRCReturnRC(autoCaller.rc());
8309
8310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8311
8312 HRESULT rc = i_checkStateDependency(aDepType);
8313 if (FAILED(rc)) return rc;
8314
8315 {
8316 if (mData->mMachineStateChangePending != 0)
8317 {
8318 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8319 * drop to zero so don't add more. It may make sense to wait a bit
8320 * and retry before reporting an error (since the pending state
8321 * transition should be really quick) but let's just assert for
8322 * now to see if it ever happens on practice. */
8323
8324 AssertFailed();
8325
8326 return setError(E_ACCESSDENIED,
8327 tr("Machine state change is in progress. Please retry the operation later."));
8328 }
8329
8330 ++mData->mMachineStateDeps;
8331 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8332 }
8333
8334 if (aState)
8335 *aState = mData->mMachineState;
8336 if (aRegistered)
8337 *aRegistered = mData->mRegistered;
8338
8339 return S_OK;
8340}
8341
8342/**
8343 * Decreases the number of objects dependent on the machine state.
8344 * Must always complete the #i_addStateDependency() call after the state
8345 * dependency is no more necessary.
8346 */
8347void Machine::i_releaseStateDependency()
8348{
8349 AutoCaller autoCaller(this);
8350 AssertComRCReturnVoid(autoCaller.rc());
8351
8352 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8353
8354 /* releaseStateDependency() w/o addStateDependency()? */
8355 AssertReturnVoid(mData->mMachineStateDeps != 0);
8356 -- mData->mMachineStateDeps;
8357
8358 if (mData->mMachineStateDeps == 0)
8359 {
8360 /* inform i_ensureNoStateDependencies() that there are no more deps */
8361 if (mData->mMachineStateChangePending != 0)
8362 {
8363 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8364 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8365 }
8366 }
8367}
8368
8369Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8370{
8371 /* start with nothing found */
8372 Utf8Str strResult("");
8373
8374 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8375
8376 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8377 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8378 // found:
8379 strResult = it->second; // source is a Utf8Str
8380
8381 return strResult;
8382}
8383
8384// protected methods
8385/////////////////////////////////////////////////////////////////////////////
8386
8387/**
8388 * Performs machine state checks based on the @a aDepType value. If a check
8389 * fails, this method will set extended error info, otherwise it will return
8390 * S_OK. It is supposed, that on failure, the caller will immediately return
8391 * the return value of this method to the upper level.
8392 *
8393 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8394 *
8395 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8396 * current state of this machine object allows to change settings of the
8397 * machine (i.e. the machine is not registered, or registered but not running
8398 * and not saved). It is useful to call this method from Machine setters
8399 * before performing any change.
8400 *
8401 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8402 * as for MutableStateDep except that if the machine is saved, S_OK is also
8403 * returned. This is useful in setters which allow changing machine
8404 * properties when it is in the saved state.
8405 *
8406 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8407 * if the current state of this machine object allows to change runtime
8408 * changeable settings of the machine (i.e. the machine is not registered, or
8409 * registered but either running or not running and not saved). It is useful
8410 * to call this method from Machine setters before performing any changes to
8411 * runtime changeable settings.
8412 *
8413 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8414 * the same as for MutableOrRunningStateDep except that if the machine is
8415 * saved, S_OK is also returned. This is useful in setters which allow
8416 * changing runtime and saved state changeable machine properties.
8417 *
8418 * @param aDepType Dependency type to check.
8419 *
8420 * @note Non Machine based classes should use #i_addStateDependency() and
8421 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8422 * template.
8423 *
8424 * @note This method must be called from under this object's read or write
8425 * lock.
8426 */
8427HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8428{
8429 switch (aDepType)
8430 {
8431 case AnyStateDep:
8432 {
8433 break;
8434 }
8435 case MutableStateDep:
8436 {
8437 if ( mData->mRegistered
8438 && ( !i_isSessionMachine()
8439 || ( mData->mMachineState != MachineState_Aborted
8440 && mData->mMachineState != MachineState_Teleported
8441 && mData->mMachineState != MachineState_PoweredOff
8442 )
8443 )
8444 )
8445 return setError(VBOX_E_INVALID_VM_STATE,
8446 tr("The machine is not mutable (state is %s)"),
8447 Global::stringifyMachineState(mData->mMachineState));
8448 break;
8449 }
8450 case MutableOrSavedStateDep:
8451 {
8452 if ( mData->mRegistered
8453 && ( !i_isSessionMachine()
8454 || ( mData->mMachineState != MachineState_Aborted
8455 && mData->mMachineState != MachineState_Teleported
8456 && mData->mMachineState != MachineState_Saved
8457 && mData->mMachineState != MachineState_AbortedSaved
8458 && mData->mMachineState != MachineState_PoweredOff
8459 )
8460 )
8461 )
8462 return setError(VBOX_E_INVALID_VM_STATE,
8463 tr("The machine is not mutable or saved (state is %s)"),
8464 Global::stringifyMachineState(mData->mMachineState));
8465 break;
8466 }
8467 case MutableOrRunningStateDep:
8468 {
8469 if ( mData->mRegistered
8470 && ( !i_isSessionMachine()
8471 || ( mData->mMachineState != MachineState_Aborted
8472 && mData->mMachineState != MachineState_Teleported
8473 && mData->mMachineState != MachineState_PoweredOff
8474 && !Global::IsOnline(mData->mMachineState)
8475 )
8476 )
8477 )
8478 return setError(VBOX_E_INVALID_VM_STATE,
8479 tr("The machine is not mutable or running (state is %s)"),
8480 Global::stringifyMachineState(mData->mMachineState));
8481 break;
8482 }
8483 case MutableOrSavedOrRunningStateDep:
8484 {
8485 if ( mData->mRegistered
8486 && ( !i_isSessionMachine()
8487 || ( mData->mMachineState != MachineState_Aborted
8488 && mData->mMachineState != MachineState_Teleported
8489 && mData->mMachineState != MachineState_Saved
8490 && mData->mMachineState != MachineState_AbortedSaved
8491 && mData->mMachineState != MachineState_PoweredOff
8492 && !Global::IsOnline(mData->mMachineState)
8493 )
8494 )
8495 )
8496 return setError(VBOX_E_INVALID_VM_STATE,
8497 tr("The machine is not mutable, saved or running (state is %s)"),
8498 Global::stringifyMachineState(mData->mMachineState));
8499 break;
8500 }
8501 }
8502
8503 return S_OK;
8504}
8505
8506/**
8507 * Helper to initialize all associated child objects and allocate data
8508 * structures.
8509 *
8510 * This method must be called as a part of the object's initialization procedure
8511 * (usually done in the #init() method).
8512 *
8513 * @note Must be called only from #init() or from #i_registeredInit().
8514 */
8515HRESULT Machine::initDataAndChildObjects()
8516{
8517 AutoCaller autoCaller(this);
8518 AssertComRCReturnRC(autoCaller.rc());
8519 AssertReturn( getObjectState().getState() == ObjectState::InInit
8520 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8521
8522 AssertReturn(!mData->mAccessible, E_FAIL);
8523
8524 /* allocate data structures */
8525 mSSData.allocate();
8526 mUserData.allocate();
8527 mHWData.allocate();
8528 mMediumAttachments.allocate();
8529 mStorageControllers.allocate();
8530 mUSBControllers.allocate();
8531
8532 /* initialize mOSTypeId */
8533 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8534
8535/** @todo r=bird: init() methods never fails, right? Why don't we make them
8536 * return void then! */
8537
8538 /* create associated BIOS settings object */
8539 unconst(mBIOSSettings).createObject();
8540 mBIOSSettings->init(this);
8541
8542 /* create associated trusted platform module object */
8543 unconst(mTrustedPlatformModule).createObject();
8544 mTrustedPlatformModule->init(this);
8545
8546 /* create associated NVRAM store object */
8547 unconst(mNvramStore).createObject();
8548 mNvramStore->init(this);
8549
8550 /* create associated record settings object */
8551 unconst(mRecordingSettings).createObject();
8552 mRecordingSettings->init(this);
8553
8554 /* create the graphics adapter object (always present) */
8555 unconst(mGraphicsAdapter).createObject();
8556 mGraphicsAdapter->init(this);
8557
8558 /* create an associated VRDE object (default is disabled) */
8559 unconst(mVRDEServer).createObject();
8560 mVRDEServer->init(this);
8561
8562 /* create associated serial port objects */
8563 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8564 {
8565 unconst(mSerialPorts[slot]).createObject();
8566 mSerialPorts[slot]->init(this, slot);
8567 }
8568
8569 /* create associated parallel port objects */
8570 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8571 {
8572 unconst(mParallelPorts[slot]).createObject();
8573 mParallelPorts[slot]->init(this, slot);
8574 }
8575
8576 /* create the audio adapter object (always present, default is disabled) */
8577 unconst(mAudioAdapter).createObject();
8578 mAudioAdapter->init(this);
8579
8580 /* create the USB device filters object (always present) */
8581 unconst(mUSBDeviceFilters).createObject();
8582 mUSBDeviceFilters->init(this);
8583
8584 /* create associated network adapter objects */
8585 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8586 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8587 {
8588 unconst(mNetworkAdapters[slot]).createObject();
8589 mNetworkAdapters[slot]->init(this, slot);
8590 }
8591
8592 /* create the bandwidth control */
8593 unconst(mBandwidthControl).createObject();
8594 mBandwidthControl->init(this);
8595
8596 return S_OK;
8597}
8598
8599/**
8600 * Helper to uninitialize all associated child objects and to free all data
8601 * structures.
8602 *
8603 * This method must be called as a part of the object's uninitialization
8604 * procedure (usually done in the #uninit() method).
8605 *
8606 * @note Must be called only from #uninit() or from #i_registeredInit().
8607 */
8608void Machine::uninitDataAndChildObjects()
8609{
8610 AutoCaller autoCaller(this);
8611 AssertComRCReturnVoid(autoCaller.rc());
8612 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8613 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8614 || getObjectState().getState() == ObjectState::InUninit
8615 || getObjectState().getState() == ObjectState::Limited);
8616
8617 /* tell all our other child objects we've been uninitialized */
8618 if (mBandwidthControl)
8619 {
8620 mBandwidthControl->uninit();
8621 unconst(mBandwidthControl).setNull();
8622 }
8623
8624 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8625 {
8626 if (mNetworkAdapters[slot])
8627 {
8628 mNetworkAdapters[slot]->uninit();
8629 unconst(mNetworkAdapters[slot]).setNull();
8630 }
8631 }
8632
8633 if (mUSBDeviceFilters)
8634 {
8635 mUSBDeviceFilters->uninit();
8636 unconst(mUSBDeviceFilters).setNull();
8637 }
8638
8639 if (mAudioAdapter)
8640 {
8641 mAudioAdapter->uninit();
8642 unconst(mAudioAdapter).setNull();
8643 }
8644
8645 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8646 {
8647 if (mParallelPorts[slot])
8648 {
8649 mParallelPorts[slot]->uninit();
8650 unconst(mParallelPorts[slot]).setNull();
8651 }
8652 }
8653
8654 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8655 {
8656 if (mSerialPorts[slot])
8657 {
8658 mSerialPorts[slot]->uninit();
8659 unconst(mSerialPorts[slot]).setNull();
8660 }
8661 }
8662
8663 if (mVRDEServer)
8664 {
8665 mVRDEServer->uninit();
8666 unconst(mVRDEServer).setNull();
8667 }
8668
8669 if (mGraphicsAdapter)
8670 {
8671 mGraphicsAdapter->uninit();
8672 unconst(mGraphicsAdapter).setNull();
8673 }
8674
8675 if (mBIOSSettings)
8676 {
8677 mBIOSSettings->uninit();
8678 unconst(mBIOSSettings).setNull();
8679 }
8680
8681 if (mTrustedPlatformModule)
8682 {
8683 mTrustedPlatformModule->uninit();
8684 unconst(mTrustedPlatformModule).setNull();
8685 }
8686
8687 if (mNvramStore)
8688 {
8689 mNvramStore->uninit();
8690 unconst(mNvramStore).setNull();
8691 }
8692
8693 if (mRecordingSettings)
8694 {
8695 mRecordingSettings->uninit();
8696 unconst(mRecordingSettings).setNull();
8697 }
8698
8699 /* Deassociate media (only when a real Machine or a SnapshotMachine
8700 * instance is uninitialized; SessionMachine instances refer to real
8701 * Machine media). This is necessary for a clean re-initialization of
8702 * the VM after successfully re-checking the accessibility state. Note
8703 * that in case of normal Machine or SnapshotMachine uninitialization (as
8704 * a result of unregistering or deleting the snapshot), outdated media
8705 * attachments will already be uninitialized and deleted, so this
8706 * code will not affect them. */
8707 if ( !mMediumAttachments.isNull()
8708 && !i_isSessionMachine()
8709 )
8710 {
8711 for (MediumAttachmentList::const_iterator
8712 it = mMediumAttachments->begin();
8713 it != mMediumAttachments->end();
8714 ++it)
8715 {
8716 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8717 if (pMedium.isNull())
8718 continue;
8719 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8720 AssertComRC(rc);
8721 }
8722 }
8723
8724 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8725 {
8726 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children)
8727 if (mData->mFirstSnapshot)
8728 {
8729 // Snapshots tree is protected by machine write lock.
8730 // Otherwise we assert in Snapshot::uninit()
8731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8732 mData->mFirstSnapshot->uninit();
8733 mData->mFirstSnapshot.setNull();
8734 }
8735
8736 mData->mCurrentSnapshot.setNull();
8737 }
8738
8739 /* free data structures (the essential mData structure is not freed here
8740 * since it may be still in use) */
8741 mMediumAttachments.free();
8742 mStorageControllers.free();
8743 mUSBControllers.free();
8744 mHWData.free();
8745 mUserData.free();
8746 mSSData.free();
8747}
8748
8749/**
8750 * Returns a pointer to the Machine object for this machine that acts like a
8751 * parent for complex machine data objects such as shared folders, etc.
8752 *
8753 * For primary Machine objects and for SnapshotMachine objects, returns this
8754 * object's pointer itself. For SessionMachine objects, returns the peer
8755 * (primary) machine pointer.
8756 */
8757Machine *Machine::i_getMachine()
8758{
8759 if (i_isSessionMachine())
8760 return (Machine*)mPeer;
8761 return this;
8762}
8763
8764/**
8765 * Makes sure that there are no machine state dependents. If necessary, waits
8766 * for the number of dependents to drop to zero.
8767 *
8768 * Make sure this method is called from under this object's write lock to
8769 * guarantee that no new dependents may be added when this method returns
8770 * control to the caller.
8771 *
8772 * @note Receives a lock to this object for writing. The lock will be released
8773 * while waiting (if necessary).
8774 *
8775 * @warning To be used only in methods that change the machine state!
8776 */
8777void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8778{
8779 AssertReturnVoid(isWriteLockOnCurrentThread());
8780
8781 /* Wait for all state dependents if necessary */
8782 if (mData->mMachineStateDeps != 0)
8783 {
8784 /* lazy semaphore creation */
8785 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8786 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8787
8788 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8789 mData->mMachineStateDeps));
8790
8791 ++mData->mMachineStateChangePending;
8792
8793 /* reset the semaphore before waiting, the last dependent will signal
8794 * it */
8795 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8796
8797 alock.release();
8798
8799 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8800
8801 alock.acquire();
8802
8803 -- mData->mMachineStateChangePending;
8804 }
8805}
8806
8807/**
8808 * Changes the machine state and informs callbacks.
8809 *
8810 * This method is not intended to fail so it either returns S_OK or asserts (and
8811 * returns a failure).
8812 *
8813 * @note Locks this object for writing.
8814 */
8815HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8816{
8817 LogFlowThisFuncEnter();
8818 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8819 Assert(aMachineState != MachineState_Null);
8820
8821 AutoCaller autoCaller(this);
8822 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8823
8824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8825
8826 /* wait for state dependents to drop to zero */
8827 i_ensureNoStateDependencies(alock);
8828
8829 MachineState_T const enmOldState = mData->mMachineState;
8830 if (enmOldState != aMachineState)
8831 {
8832 mData->mMachineState = aMachineState;
8833 RTTimeNow(&mData->mLastStateChange);
8834
8835#ifdef VBOX_WITH_DTRACE_R3_MAIN
8836 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8837#endif
8838 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8839 }
8840
8841 LogFlowThisFuncLeave();
8842 return S_OK;
8843}
8844
8845/**
8846 * Searches for a shared folder with the given logical name
8847 * in the collection of shared folders.
8848 *
8849 * @param aName logical name of the shared folder
8850 * @param aSharedFolder where to return the found object
8851 * @param aSetError whether to set the error info if the folder is
8852 * not found
8853 * @return
8854 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8855 *
8856 * @note
8857 * must be called from under the object's lock!
8858 */
8859HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8860 ComObjPtr<SharedFolder> &aSharedFolder,
8861 bool aSetError /* = false */)
8862{
8863 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8864 for (HWData::SharedFolderList::const_iterator
8865 it = mHWData->mSharedFolders.begin();
8866 it != mHWData->mSharedFolders.end();
8867 ++it)
8868 {
8869 SharedFolder *pSF = *it;
8870 AutoCaller autoCaller(pSF);
8871 if (pSF->i_getName() == aName)
8872 {
8873 aSharedFolder = pSF;
8874 rc = S_OK;
8875 break;
8876 }
8877 }
8878
8879 if (aSetError && FAILED(rc))
8880 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8881
8882 return rc;
8883}
8884
8885/**
8886 * Initializes all machine instance data from the given settings structures
8887 * from XML. The exception is the machine UUID which needs special handling
8888 * depending on the caller's use case, so the caller needs to set that herself.
8889 *
8890 * This gets called in several contexts during machine initialization:
8891 *
8892 * -- When machine XML exists on disk already and needs to be loaded into memory,
8893 * for example, from #i_registeredInit() to load all registered machines on
8894 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8895 * attached to the machine should be part of some media registry already.
8896 *
8897 * -- During OVF import, when a machine config has been constructed from an
8898 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8899 * ensure that the media listed as attachments in the config (which have
8900 * been imported from the OVF) receive the correct registry ID.
8901 *
8902 * -- During VM cloning.
8903 *
8904 * @param config Machine settings from XML.
8905 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8906 * for each attached medium in the config.
8907 * @return
8908 */
8909HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8910 const Guid *puuidRegistry)
8911{
8912 // copy name, description, OS type, teleporter, UTC etc.
8913 mUserData->s = config.machineUserData;
8914
8915 // look up the object by Id to check it is valid
8916 ComObjPtr<GuestOSType> pGuestOSType;
8917 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8918 if (!pGuestOSType.isNull())
8919 mUserData->s.strOsType = pGuestOSType->i_id();
8920
8921 // stateFile (optional)
8922 if (config.strStateFile.isEmpty())
8923 mSSData->strStateFilePath.setNull();
8924 else
8925 {
8926 Utf8Str stateFilePathFull(config.strStateFile);
8927 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8928 if (RT_FAILURE(vrc))
8929 return setErrorBoth(E_FAIL, vrc,
8930 tr("Invalid saved state file path '%s' (%Rrc)"),
8931 config.strStateFile.c_str(),
8932 vrc);
8933 mSSData->strStateFilePath = stateFilePathFull;
8934 }
8935
8936 // snapshot folder needs special processing so set it again
8937 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8938 if (FAILED(rc)) return rc;
8939
8940 /* Copy the extra data items (config may or may not be the same as
8941 * mData->pMachineConfigFile) if necessary. When loading the XML files
8942 * from disk they are the same, but not for OVF import. */
8943 if (mData->pMachineConfigFile != &config)
8944 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8945
8946 /* currentStateModified (optional, default is true) */
8947 mData->mCurrentStateModified = config.fCurrentStateModified;
8948
8949 mData->mLastStateChange = config.timeLastStateChange;
8950
8951 /*
8952 * note: all mUserData members must be assigned prior this point because
8953 * we need to commit changes in order to let mUserData be shared by all
8954 * snapshot machine instances.
8955 */
8956 mUserData.commitCopy();
8957
8958 // machine registry, if present (must be loaded before snapshots)
8959 if (config.canHaveOwnMediaRegistry())
8960 {
8961 // determine machine folder
8962 Utf8Str strMachineFolder = i_getSettingsFileFull();
8963 strMachineFolder.stripFilename();
8964 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8965 config.mediaRegistry,
8966 strMachineFolder);
8967 if (FAILED(rc)) return rc;
8968 }
8969
8970 /* Snapshot node (optional) */
8971 size_t cRootSnapshots;
8972 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8973 {
8974 // there must be only one root snapshot
8975 Assert(cRootSnapshots == 1);
8976 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8977
8978 rc = i_loadSnapshot(snap,
8979 config.uuidCurrentSnapshot);
8980 if (FAILED(rc)) return rc;
8981 }
8982
8983 // hardware data
8984 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8985 if (FAILED(rc)) return rc;
8986
8987 /*
8988 * NOTE: the assignment below must be the last thing to do,
8989 * otherwise it will be not possible to change the settings
8990 * somewhere in the code above because all setters will be
8991 * blocked by i_checkStateDependency(MutableStateDep).
8992 */
8993
8994 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
8995 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
8996 {
8997 /* no need to use i_setMachineState() during init() */
8998 mData->mMachineState = MachineState_AbortedSaved;
8999 }
9000 else if (config.fAborted)
9001 {
9002 mSSData->strStateFilePath.setNull();
9003
9004 /* no need to use i_setMachineState() during init() */
9005 mData->mMachineState = MachineState_Aborted;
9006 }
9007 else if (!mSSData->strStateFilePath.isEmpty())
9008 {
9009 /* no need to use i_setMachineState() during init() */
9010 mData->mMachineState = MachineState_Saved;
9011 }
9012
9013 // after loading settings, we are no longer different from the XML on disk
9014 mData->flModifications = 0;
9015
9016 return S_OK;
9017}
9018
9019/**
9020 * Loads all snapshots starting from the given settings.
9021 *
9022 * @param data snapshot settings.
9023 * @param aCurSnapshotId Current snapshot ID from the settings file.
9024 */
9025HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
9026 const Guid &aCurSnapshotId)
9027{
9028 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
9029 AssertReturn(!i_isSessionMachine(), E_FAIL);
9030
9031 HRESULT rc = S_OK;
9032
9033 std::list<const settings::Snapshot *> llSettingsTodo;
9034 llSettingsTodo.push_back(&data);
9035 std::list<Snapshot *> llParentsTodo;
9036 llParentsTodo.push_back(NULL);
9037
9038 while (llSettingsTodo.size() > 0)
9039 {
9040 const settings::Snapshot *current = llSettingsTodo.front();
9041 llSettingsTodo.pop_front();
9042 Snapshot *pParent = llParentsTodo.front();
9043 llParentsTodo.pop_front();
9044
9045 Utf8Str strStateFile;
9046 if (!current->strStateFile.isEmpty())
9047 {
9048 /* optional */
9049 strStateFile = current->strStateFile;
9050 int vrc = i_calculateFullPath(strStateFile, strStateFile);
9051 if (RT_FAILURE(vrc))
9052 {
9053 setErrorBoth(E_FAIL, vrc,
9054 tr("Invalid saved state file path '%s' (%Rrc)"),
9055 strStateFile.c_str(), vrc);
9056 }
9057 }
9058
9059 /* create a snapshot machine object */
9060 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9061 pSnapshotMachine.createObject();
9062 rc = pSnapshotMachine->initFromSettings(this,
9063 current->hardware,
9064 &current->debugging,
9065 &current->autostart,
9066 current->uuid.ref(),
9067 strStateFile);
9068 if (FAILED(rc)) break;
9069
9070 /* create a snapshot object */
9071 ComObjPtr<Snapshot> pSnapshot;
9072 pSnapshot.createObject();
9073 /* initialize the snapshot */
9074 rc = pSnapshot->init(mParent, // VirtualBox object
9075 current->uuid,
9076 current->strName,
9077 current->strDescription,
9078 current->timestamp,
9079 pSnapshotMachine,
9080 pParent);
9081 if (FAILED(rc)) break;
9082
9083 /* memorize the first snapshot if necessary */
9084 if (!mData->mFirstSnapshot)
9085 {
9086 Assert(pParent == NULL);
9087 mData->mFirstSnapshot = pSnapshot;
9088 }
9089
9090 /* memorize the current snapshot when appropriate */
9091 if ( !mData->mCurrentSnapshot
9092 && pSnapshot->i_getId() == aCurSnapshotId
9093 )
9094 mData->mCurrentSnapshot = pSnapshot;
9095
9096 /* create all children */
9097 std::list<settings::Snapshot>::const_iterator itBegin = current->llChildSnapshots.begin();
9098 std::list<settings::Snapshot>::const_iterator itEnd = current->llChildSnapshots.end();
9099 for (std::list<settings::Snapshot>::const_iterator it = itBegin; it != itEnd; ++it)
9100 {
9101 llSettingsTodo.push_back(&*it);
9102 llParentsTodo.push_back(pSnapshot);
9103 }
9104 }
9105
9106 return rc;
9107}
9108
9109/**
9110 * Loads settings into mHWData.
9111 *
9112 * @param puuidRegistry Registry ID.
9113 * @param puuidSnapshot Snapshot ID
9114 * @param data Reference to the hardware settings.
9115 * @param pDbg Pointer to the debugging settings.
9116 * @param pAutostart Pointer to the autostart settings.
9117 */
9118HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
9119 const Guid *puuidSnapshot,
9120 const settings::Hardware &data,
9121 const settings::Debugging *pDbg,
9122 const settings::Autostart *pAutostart)
9123{
9124 AssertReturn(!i_isSessionMachine(), E_FAIL);
9125
9126 HRESULT rc = S_OK;
9127
9128 try
9129 {
9130 ComObjPtr<GuestOSType> pGuestOSType;
9131 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9132
9133 /* The hardware version attribute (optional). */
9134 mHWData->mHWVersion = data.strVersion;
9135 mHWData->mHardwareUUID = data.uuid;
9136
9137 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9138 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9139 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9140 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9141 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9142 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9143 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
9144 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
9145 mHWData->mPAEEnabled = data.fPAE;
9146 mHWData->mLongMode = data.enmLongMode;
9147 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9148 mHWData->mAPIC = data.fAPIC;
9149 mHWData->mX2APIC = data.fX2APIC;
9150 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9151 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9152 mHWData->mSpecCtrl = data.fSpecCtrl;
9153 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
9154 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
9155 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
9156 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
9157 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
9158 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9159 mHWData->mCPUCount = data.cCPUs;
9160 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9161 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9162 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9163 mHWData->mCpuProfile = data.strCpuProfile;
9164
9165 // cpu
9166 if (mHWData->mCPUHotPlugEnabled)
9167 {
9168 for (settings::CpuList::const_iterator
9169 it = data.llCpus.begin();
9170 it != data.llCpus.end();
9171 ++it)
9172 {
9173 const settings::Cpu &cpu = *it;
9174
9175 mHWData->mCPUAttached[cpu.ulId] = true;
9176 }
9177 }
9178
9179 // cpuid leafs
9180 for (settings::CpuIdLeafsList::const_iterator
9181 it = data.llCpuIdLeafs.begin();
9182 it != data.llCpuIdLeafs.end();
9183 ++it)
9184 {
9185 const settings::CpuIdLeaf &rLeaf= *it;
9186 if ( rLeaf.idx < UINT32_C(0x20)
9187 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9188 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9189 mHWData->mCpuIdLeafList.push_back(rLeaf);
9190 /* else: just ignore */
9191 }
9192
9193 mHWData->mMemorySize = data.ulMemorySizeMB;
9194 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9195
9196 // boot order
9197 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9198 {
9199 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9200 if (it == data.mapBootOrder.end())
9201 mHWData->mBootOrder[i] = DeviceType_Null;
9202 else
9203 mHWData->mBootOrder[i] = it->second;
9204 }
9205
9206 mHWData->mFirmwareType = data.firmwareType;
9207 mHWData->mPointingHIDType = data.pointingHIDType;
9208 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9209 mHWData->mChipsetType = data.chipsetType;
9210 mHWData->mIommuType = data.iommuType;
9211 mHWData->mParavirtProvider = data.paravirtProvider;
9212 mHWData->mParavirtDebug = data.strParavirtDebug;
9213 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9214 mHWData->mHPETEnabled = data.fHPETEnabled;
9215
9216 /* GraphicsAdapter */
9217 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
9218 if (FAILED(rc)) return rc;
9219
9220 /* VRDEServer */
9221 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9222 if (FAILED(rc)) return rc;
9223
9224 /* BIOS */
9225 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9226 if (FAILED(rc)) return rc;
9227
9228 /* Trusted Platform Module */
9229 rc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
9230 if (FAILED(rc)) return rc;
9231
9232 rc = mNvramStore->i_loadSettings(data.nvramSettings);
9233 if (FAILED(rc)) return rc;
9234
9235 /* Recording settings */
9236 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
9237 if (FAILED(rc)) return rc;
9238
9239 // Bandwidth control (must come before network adapters)
9240 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9241 if (FAILED(rc)) return rc;
9242
9243 /* USB controllers */
9244 for (settings::USBControllerList::const_iterator
9245 it = data.usbSettings.llUSBControllers.begin();
9246 it != data.usbSettings.llUSBControllers.end();
9247 ++it)
9248 {
9249 const settings::USBController &settingsCtrl = *it;
9250 ComObjPtr<USBController> newCtrl;
9251
9252 newCtrl.createObject();
9253 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9254 mUSBControllers->push_back(newCtrl);
9255 }
9256
9257 /* USB device filters */
9258 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9259 if (FAILED(rc)) return rc;
9260
9261 // network adapters (establish array size first and apply defaults, to
9262 // ensure reading the same settings as we saved, since the list skips
9263 // adapters having defaults)
9264 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9265 size_t oldCount = mNetworkAdapters.size();
9266 if (newCount > oldCount)
9267 {
9268 mNetworkAdapters.resize(newCount);
9269 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9270 {
9271 unconst(mNetworkAdapters[slot]).createObject();
9272 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9273 }
9274 }
9275 else if (newCount < oldCount)
9276 mNetworkAdapters.resize(newCount);
9277 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9278 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9279 for (settings::NetworkAdaptersList::const_iterator
9280 it = data.llNetworkAdapters.begin();
9281 it != data.llNetworkAdapters.end();
9282 ++it)
9283 {
9284 const settings::NetworkAdapter &nic = *it;
9285
9286 /* slot uniqueness is guaranteed by XML Schema */
9287 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9288 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9289 if (FAILED(rc)) return rc;
9290 }
9291
9292 // serial ports (establish defaults first, to ensure reading the same
9293 // settings as we saved, since the list skips ports having defaults)
9294 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9295 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9296 for (settings::SerialPortsList::const_iterator
9297 it = data.llSerialPorts.begin();
9298 it != data.llSerialPorts.end();
9299 ++it)
9300 {
9301 const settings::SerialPort &s = *it;
9302
9303 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9304 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9305 if (FAILED(rc)) return rc;
9306 }
9307
9308 // parallel ports (establish defaults first, to ensure reading the same
9309 // settings as we saved, since the list skips ports having defaults)
9310 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9311 mParallelPorts[i]->i_applyDefaults();
9312 for (settings::ParallelPortsList::const_iterator
9313 it = data.llParallelPorts.begin();
9314 it != data.llParallelPorts.end();
9315 ++it)
9316 {
9317 const settings::ParallelPort &p = *it;
9318
9319 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9320 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9321 if (FAILED(rc)) return rc;
9322 }
9323
9324 /* AudioAdapter */
9325 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9326 if (FAILED(rc)) return rc;
9327
9328 /* storage controllers */
9329 rc = i_loadStorageControllers(data.storage,
9330 puuidRegistry,
9331 puuidSnapshot);
9332 if (FAILED(rc)) return rc;
9333
9334 /* Shared folders */
9335 for (settings::SharedFoldersList::const_iterator
9336 it = data.llSharedFolders.begin();
9337 it != data.llSharedFolders.end();
9338 ++it)
9339 {
9340 const settings::SharedFolder &sf = *it;
9341
9342 ComObjPtr<SharedFolder> sharedFolder;
9343 /* Check for double entries. Not allowed! */
9344 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9345 if (SUCCEEDED(rc))
9346 return setError(VBOX_E_OBJECT_IN_USE,
9347 tr("Shared folder named '%s' already exists"),
9348 sf.strName.c_str());
9349
9350 /* Create the new shared folder. Don't break on error. This will be
9351 * reported when the machine starts. */
9352 sharedFolder.createObject();
9353 rc = sharedFolder->init(i_getMachine(),
9354 sf.strName,
9355 sf.strHostPath,
9356 RT_BOOL(sf.fWritable),
9357 RT_BOOL(sf.fAutoMount),
9358 sf.strAutoMountPoint,
9359 false /* fFailOnError */);
9360 if (FAILED(rc)) return rc;
9361 mHWData->mSharedFolders.push_back(sharedFolder);
9362 }
9363
9364 // Clipboard
9365 mHWData->mClipboardMode = data.clipboardMode;
9366 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9367
9368 // drag'n'drop
9369 mHWData->mDnDMode = data.dndMode;
9370
9371 // guest settings
9372 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9373
9374 // IO settings
9375 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9376 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9377
9378 // Host PCI devices
9379 for (settings::HostPCIDeviceAttachmentList::const_iterator
9380 it = data.pciAttachments.begin();
9381 it != data.pciAttachments.end();
9382 ++it)
9383 {
9384 const settings::HostPCIDeviceAttachment &hpda = *it;
9385 ComObjPtr<PCIDeviceAttachment> pda;
9386
9387 pda.createObject();
9388 pda->i_loadSettings(this, hpda);
9389 mHWData->mPCIDeviceAssignments.push_back(pda);
9390 }
9391
9392 /*
9393 * (The following isn't really real hardware, but it lives in HWData
9394 * for reasons of convenience.)
9395 */
9396
9397#ifdef VBOX_WITH_GUEST_PROPS
9398 /* Guest properties (optional) */
9399
9400 /* Only load transient guest properties for configs which have saved
9401 * state, because there shouldn't be any for powered off VMs. The same
9402 * logic applies for snapshots, as offline snapshots shouldn't have
9403 * any such properties. They confuse the code in various places.
9404 * Note: can't rely on the machine state, as it isn't set yet. */
9405 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9406 /* apologies for the hacky unconst() usage, but this needs hacking
9407 * actually inconsistent settings into consistency, otherwise there
9408 * will be some corner cases where the inconsistency survives
9409 * surprisingly long without getting fixed, especially for snapshots
9410 * as there are no config changes. */
9411 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9412 for (settings::GuestPropertiesList::iterator
9413 it = llGuestProperties.begin();
9414 it != llGuestProperties.end();
9415 /*nothing*/)
9416 {
9417 const settings::GuestProperty &prop = *it;
9418 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9419 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9420 if ( fSkipTransientGuestProperties
9421 && ( fFlags & GUEST_PROP_F_TRANSIENT
9422 || fFlags & GUEST_PROP_F_TRANSRESET))
9423 {
9424 it = llGuestProperties.erase(it);
9425 continue;
9426 }
9427 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9428 mHWData->mGuestProperties[prop.strName] = property;
9429 ++it;
9430 }
9431#endif /* VBOX_WITH_GUEST_PROPS defined */
9432
9433 rc = i_loadDebugging(pDbg);
9434 if (FAILED(rc))
9435 return rc;
9436
9437 mHWData->mAutostart = *pAutostart;
9438
9439 /* default frontend */
9440 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9441 }
9442 catch (std::bad_alloc &)
9443 {
9444 return E_OUTOFMEMORY;
9445 }
9446
9447 AssertComRC(rc);
9448 return rc;
9449}
9450
9451/**
9452 * Called from i_loadHardware() to load the debugging settings of the
9453 * machine.
9454 *
9455 * @param pDbg Pointer to the settings.
9456 */
9457HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9458{
9459 mHWData->mDebugging = *pDbg;
9460 /* no more processing currently required, this will probably change. */
9461 return S_OK;
9462}
9463
9464/**
9465 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9466 *
9467 * @param data storage settings.
9468 * @param puuidRegistry media registry ID to set media to or NULL;
9469 * see Machine::i_loadMachineDataFromSettings()
9470 * @param puuidSnapshot snapshot ID
9471 * @return
9472 */
9473HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9474 const Guid *puuidRegistry,
9475 const Guid *puuidSnapshot)
9476{
9477 AssertReturn(!i_isSessionMachine(), E_FAIL);
9478
9479 HRESULT rc = S_OK;
9480
9481 for (settings::StorageControllersList::const_iterator
9482 it = data.llStorageControllers.begin();
9483 it != data.llStorageControllers.end();
9484 ++it)
9485 {
9486 const settings::StorageController &ctlData = *it;
9487
9488 ComObjPtr<StorageController> pCtl;
9489 /* Try to find one with the name first. */
9490 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9491 if (SUCCEEDED(rc))
9492 return setError(VBOX_E_OBJECT_IN_USE,
9493 tr("Storage controller named '%s' already exists"),
9494 ctlData.strName.c_str());
9495
9496 pCtl.createObject();
9497 rc = pCtl->init(this,
9498 ctlData.strName,
9499 ctlData.storageBus,
9500 ctlData.ulInstance,
9501 ctlData.fBootable);
9502 if (FAILED(rc)) return rc;
9503
9504 mStorageControllers->push_back(pCtl);
9505
9506 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9507 if (FAILED(rc)) return rc;
9508
9509 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9510 if (FAILED(rc)) return rc;
9511
9512 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9513 if (FAILED(rc)) return rc;
9514
9515 /* Load the attached devices now. */
9516 rc = i_loadStorageDevices(pCtl,
9517 ctlData,
9518 puuidRegistry,
9519 puuidSnapshot);
9520 if (FAILED(rc)) return rc;
9521 }
9522
9523 return S_OK;
9524}
9525
9526/**
9527 * Called from i_loadStorageControllers for a controller's devices.
9528 *
9529 * @param aStorageController
9530 * @param data
9531 * @param puuidRegistry media registry ID to set media to or NULL; see
9532 * Machine::i_loadMachineDataFromSettings()
9533 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9534 * @return
9535 */
9536HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9537 const settings::StorageController &data,
9538 const Guid *puuidRegistry,
9539 const Guid *puuidSnapshot)
9540{
9541 HRESULT rc = S_OK;
9542
9543 /* paranoia: detect duplicate attachments */
9544 for (settings::AttachedDevicesList::const_iterator
9545 it = data.llAttachedDevices.begin();
9546 it != data.llAttachedDevices.end();
9547 ++it)
9548 {
9549 const settings::AttachedDevice &ad = *it;
9550
9551 for (settings::AttachedDevicesList::const_iterator it2 = it;
9552 it2 != data.llAttachedDevices.end();
9553 ++it2)
9554 {
9555 if (it == it2)
9556 continue;
9557
9558 const settings::AttachedDevice &ad2 = *it2;
9559
9560 if ( ad.lPort == ad2.lPort
9561 && ad.lDevice == ad2.lDevice)
9562 {
9563 return setError(E_FAIL,
9564 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9565 aStorageController->i_getName().c_str(),
9566 ad.lPort,
9567 ad.lDevice,
9568 mUserData->s.strName.c_str());
9569 }
9570 }
9571 }
9572
9573 for (settings::AttachedDevicesList::const_iterator
9574 it = data.llAttachedDevices.begin();
9575 it != data.llAttachedDevices.end();
9576 ++it)
9577 {
9578 const settings::AttachedDevice &dev = *it;
9579 ComObjPtr<Medium> medium;
9580
9581 switch (dev.deviceType)
9582 {
9583 case DeviceType_Floppy:
9584 case DeviceType_DVD:
9585 if (dev.strHostDriveSrc.isNotEmpty())
9586 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9587 false /* fRefresh */, medium);
9588 else
9589 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9590 dev.uuid,
9591 false /* fRefresh */,
9592 false /* aSetError */,
9593 medium);
9594 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9595 // This is not an error. The host drive or UUID might have vanished, so just go
9596 // ahead without this removeable medium attachment
9597 rc = S_OK;
9598 break;
9599
9600 case DeviceType_HardDisk:
9601 {
9602 /* find a hard disk by UUID */
9603 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9604 if (FAILED(rc))
9605 {
9606 if (i_isSnapshotMachine())
9607 {
9608 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9609 // so the user knows that the bad disk is in a snapshot somewhere
9610 com::ErrorInfo info;
9611 return setError(E_FAIL,
9612 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9613 puuidSnapshot->raw(),
9614 info.getText().raw());
9615 }
9616 else
9617 return rc;
9618 }
9619
9620 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9621
9622 if (medium->i_getType() == MediumType_Immutable)
9623 {
9624 if (i_isSnapshotMachine())
9625 return setError(E_FAIL,
9626 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9627 "of the virtual machine '%s' ('%s')"),
9628 medium->i_getLocationFull().c_str(),
9629 dev.uuid.raw(),
9630 puuidSnapshot->raw(),
9631 mUserData->s.strName.c_str(),
9632 mData->m_strConfigFileFull.c_str());
9633
9634 return setError(E_FAIL,
9635 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9636 medium->i_getLocationFull().c_str(),
9637 dev.uuid.raw(),
9638 mUserData->s.strName.c_str(),
9639 mData->m_strConfigFileFull.c_str());
9640 }
9641
9642 if (medium->i_getType() == MediumType_MultiAttach)
9643 {
9644 if (i_isSnapshotMachine())
9645 return setError(E_FAIL,
9646 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9647 "of the virtual machine '%s' ('%s')"),
9648 medium->i_getLocationFull().c_str(),
9649 dev.uuid.raw(),
9650 puuidSnapshot->raw(),
9651 mUserData->s.strName.c_str(),
9652 mData->m_strConfigFileFull.c_str());
9653
9654 return setError(E_FAIL,
9655 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9656 medium->i_getLocationFull().c_str(),
9657 dev.uuid.raw(),
9658 mUserData->s.strName.c_str(),
9659 mData->m_strConfigFileFull.c_str());
9660 }
9661
9662 if ( !i_isSnapshotMachine()
9663 && medium->i_getChildren().size() != 0
9664 )
9665 return setError(E_FAIL,
9666 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9667 "because it has %d differencing child hard disks"),
9668 medium->i_getLocationFull().c_str(),
9669 dev.uuid.raw(),
9670 mUserData->s.strName.c_str(),
9671 mData->m_strConfigFileFull.c_str(),
9672 medium->i_getChildren().size());
9673
9674 if (i_findAttachment(*mMediumAttachments.data(),
9675 medium))
9676 return setError(E_FAIL,
9677 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9678 medium->i_getLocationFull().c_str(),
9679 dev.uuid.raw(),
9680 mUserData->s.strName.c_str(),
9681 mData->m_strConfigFileFull.c_str());
9682
9683 break;
9684 }
9685
9686 default:
9687 return setError(E_FAIL,
9688 tr("Controller '%s' port %u unit %u has device with unknown type (%d) - virtual machine '%s' ('%s')"),
9689 data.strName.c_str(), dev.lPort, dev.lDevice, dev.deviceType,
9690 mUserData->s.strName.c_str(), mData->m_strConfigFileFull.c_str());
9691 }
9692
9693 if (FAILED(rc))
9694 break;
9695
9696 /* Bandwidth groups are loaded at this point. */
9697 ComObjPtr<BandwidthGroup> pBwGroup;
9698
9699 if (!dev.strBwGroup.isEmpty())
9700 {
9701 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9702 if (FAILED(rc))
9703 return setError(E_FAIL,
9704 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9705 medium->i_getLocationFull().c_str(),
9706 dev.strBwGroup.c_str(),
9707 mUserData->s.strName.c_str(),
9708 mData->m_strConfigFileFull.c_str());
9709 pBwGroup->i_reference();
9710 }
9711
9712 const Utf8Str controllerName = aStorageController->i_getName();
9713 ComObjPtr<MediumAttachment> pAttachment;
9714 pAttachment.createObject();
9715 rc = pAttachment->init(this,
9716 medium,
9717 controllerName,
9718 dev.lPort,
9719 dev.lDevice,
9720 dev.deviceType,
9721 false,
9722 dev.fPassThrough,
9723 dev.fTempEject,
9724 dev.fNonRotational,
9725 dev.fDiscard,
9726 dev.fHotPluggable,
9727 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9728 if (FAILED(rc)) break;
9729
9730 /* associate the medium with this machine and snapshot */
9731 if (!medium.isNull())
9732 {
9733 AutoCaller medCaller(medium);
9734 if (FAILED(medCaller.rc())) return medCaller.rc();
9735 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9736
9737 if (i_isSnapshotMachine())
9738 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9739 else
9740 rc = medium->i_addBackReference(mData->mUuid);
9741 /* If the medium->addBackReference fails it sets an appropriate
9742 * error message, so no need to do any guesswork here. */
9743
9744 if (puuidRegistry)
9745 // caller wants registry ID to be set on all attached media (OVF import case)
9746 medium->i_addRegistry(*puuidRegistry);
9747 }
9748
9749 if (FAILED(rc))
9750 break;
9751
9752 /* back up mMediumAttachments to let registeredInit() properly rollback
9753 * on failure (= limited accessibility) */
9754 i_setModified(IsModified_Storage);
9755 mMediumAttachments.backup();
9756 mMediumAttachments->push_back(pAttachment);
9757 }
9758
9759 return rc;
9760}
9761
9762/**
9763 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9764 *
9765 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9766 * @param aSnapshot where to return the found snapshot
9767 * @param aSetError true to set extended error info on failure
9768 */
9769HRESULT Machine::i_findSnapshotById(const Guid &aId,
9770 ComObjPtr<Snapshot> &aSnapshot,
9771 bool aSetError /* = false */)
9772{
9773 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9774
9775 if (!mData->mFirstSnapshot)
9776 {
9777 if (aSetError)
9778 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9779 return E_FAIL;
9780 }
9781
9782 if (aId.isZero())
9783 aSnapshot = mData->mFirstSnapshot;
9784 else
9785 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9786
9787 if (!aSnapshot)
9788 {
9789 if (aSetError)
9790 return setError(E_FAIL,
9791 tr("Could not find a snapshot with UUID {%s}"),
9792 aId.toString().c_str());
9793 return E_FAIL;
9794 }
9795
9796 return S_OK;
9797}
9798
9799/**
9800 * Returns the snapshot with the given name or fails of no such snapshot.
9801 *
9802 * @param strName snapshot name to find
9803 * @param aSnapshot where to return the found snapshot
9804 * @param aSetError true to set extended error info on failure
9805 */
9806HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9807 ComObjPtr<Snapshot> &aSnapshot,
9808 bool aSetError /* = false */)
9809{
9810 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9811
9812 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9813
9814 if (!mData->mFirstSnapshot)
9815 {
9816 if (aSetError)
9817 return setError(VBOX_E_OBJECT_NOT_FOUND,
9818 tr("This machine does not have any snapshots"));
9819 return VBOX_E_OBJECT_NOT_FOUND;
9820 }
9821
9822 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9823
9824 if (!aSnapshot)
9825 {
9826 if (aSetError)
9827 return setError(VBOX_E_OBJECT_NOT_FOUND,
9828 tr("Could not find a snapshot named '%s'"), strName.c_str());
9829 return VBOX_E_OBJECT_NOT_FOUND;
9830 }
9831
9832 return S_OK;
9833}
9834
9835/**
9836 * Returns a storage controller object with the given name.
9837 *
9838 * @param aName storage controller name to find
9839 * @param aStorageController where to return the found storage controller
9840 * @param aSetError true to set extended error info on failure
9841 */
9842HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9843 ComObjPtr<StorageController> &aStorageController,
9844 bool aSetError /* = false */)
9845{
9846 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9847
9848 for (StorageControllerList::const_iterator
9849 it = mStorageControllers->begin();
9850 it != mStorageControllers->end();
9851 ++it)
9852 {
9853 if ((*it)->i_getName() == aName)
9854 {
9855 aStorageController = (*it);
9856 return S_OK;
9857 }
9858 }
9859
9860 if (aSetError)
9861 return setError(VBOX_E_OBJECT_NOT_FOUND,
9862 tr("Could not find a storage controller named '%s'"),
9863 aName.c_str());
9864 return VBOX_E_OBJECT_NOT_FOUND;
9865}
9866
9867/**
9868 * Returns a USB controller object with the given name.
9869 *
9870 * @param aName USB controller name to find
9871 * @param aUSBController where to return the found USB controller
9872 * @param aSetError true to set extended error info on failure
9873 */
9874HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9875 ComObjPtr<USBController> &aUSBController,
9876 bool aSetError /* = false */)
9877{
9878 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9879
9880 for (USBControllerList::const_iterator
9881 it = mUSBControllers->begin();
9882 it != mUSBControllers->end();
9883 ++it)
9884 {
9885 if ((*it)->i_getName() == aName)
9886 {
9887 aUSBController = (*it);
9888 return S_OK;
9889 }
9890 }
9891
9892 if (aSetError)
9893 return setError(VBOX_E_OBJECT_NOT_FOUND,
9894 tr("Could not find a storage controller named '%s'"),
9895 aName.c_str());
9896 return VBOX_E_OBJECT_NOT_FOUND;
9897}
9898
9899/**
9900 * Returns the number of USB controller instance of the given type.
9901 *
9902 * @param enmType USB controller type.
9903 */
9904ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9905{
9906 ULONG cCtrls = 0;
9907
9908 for (USBControllerList::const_iterator
9909 it = mUSBControllers->begin();
9910 it != mUSBControllers->end();
9911 ++it)
9912 {
9913 if ((*it)->i_getControllerType() == enmType)
9914 cCtrls++;
9915 }
9916
9917 return cCtrls;
9918}
9919
9920HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9921 MediumAttachmentList &atts)
9922{
9923 AutoCaller autoCaller(this);
9924 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9925
9926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9927
9928 for (MediumAttachmentList::const_iterator
9929 it = mMediumAttachments->begin();
9930 it != mMediumAttachments->end();
9931 ++it)
9932 {
9933 const ComObjPtr<MediumAttachment> &pAtt = *it;
9934 // should never happen, but deal with NULL pointers in the list.
9935 AssertContinue(!pAtt.isNull());
9936
9937 // getControllerName() needs caller+read lock
9938 AutoCaller autoAttCaller(pAtt);
9939 if (FAILED(autoAttCaller.rc()))
9940 {
9941 atts.clear();
9942 return autoAttCaller.rc();
9943 }
9944 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9945
9946 if (pAtt->i_getControllerName() == aName)
9947 atts.push_back(pAtt);
9948 }
9949
9950 return S_OK;
9951}
9952
9953
9954/**
9955 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9956 * file if the machine name was changed and about creating a new settings file
9957 * if this is a new machine.
9958 *
9959 * @note Must be never called directly but only from #saveSettings().
9960 */
9961HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
9962 bool *pfSettingsFileIsNew)
9963{
9964 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9965
9966 HRESULT rc = S_OK;
9967
9968 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9969 /// @todo need to handle primary group change, too
9970
9971 /* attempt to rename the settings file if machine name is changed */
9972 if ( mUserData->s.fNameSync
9973 && mUserData.isBackedUp()
9974 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9975 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9976 )
9977 {
9978 bool dirRenamed = false;
9979 bool fileRenamed = false;
9980
9981 Utf8Str configFile, newConfigFile;
9982 Utf8Str configFilePrev, newConfigFilePrev;
9983 Utf8Str NVRAMFile, newNVRAMFile;
9984 Utf8Str configDir, newConfigDir;
9985
9986 do
9987 {
9988 int vrc = VINF_SUCCESS;
9989
9990 Utf8Str name = mUserData.backedUpData()->s.strName;
9991 Utf8Str newName = mUserData->s.strName;
9992 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9993 if (group == "/")
9994 group.setNull();
9995 Utf8Str newGroup = mUserData->s.llGroups.front();
9996 if (newGroup == "/")
9997 newGroup.setNull();
9998
9999 configFile = mData->m_strConfigFileFull;
10000
10001 /* first, rename the directory if it matches the group and machine name */
10002 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
10003 /** @todo hack, make somehow use of ComposeMachineFilename */
10004 if (mUserData->s.fDirectoryIncludesUUID)
10005 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10006 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
10007 /** @todo hack, make somehow use of ComposeMachineFilename */
10008 if (mUserData->s.fDirectoryIncludesUUID)
10009 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10010 configDir = configFile;
10011 configDir.stripFilename();
10012 newConfigDir = configDir;
10013 if ( configDir.length() >= groupPlusName.length()
10014 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
10015 groupPlusName.c_str()))
10016 {
10017 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10018 Utf8Str newConfigBaseDir(newConfigDir);
10019 newConfigDir.append(newGroupPlusName);
10020 /* consistency: use \ if appropriate on the platform */
10021 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10022 /* new dir and old dir cannot be equal here because of 'if'
10023 * above and because name != newName */
10024 Assert(configDir != newConfigDir);
10025 if (!fSettingsFileIsNew)
10026 {
10027 /* perform real rename only if the machine is not new */
10028 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10029 if ( vrc == VERR_FILE_NOT_FOUND
10030 || vrc == VERR_PATH_NOT_FOUND)
10031 {
10032 /* create the parent directory, then retry renaming */
10033 Utf8Str parent(newConfigDir);
10034 parent.stripFilename();
10035 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10036 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10037 }
10038 if (RT_FAILURE(vrc))
10039 {
10040 rc = setErrorBoth(E_FAIL, vrc,
10041 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10042 configDir.c_str(),
10043 newConfigDir.c_str(),
10044 vrc);
10045 break;
10046 }
10047 /* delete subdirectories which are no longer needed */
10048 Utf8Str dir(configDir);
10049 dir.stripFilename();
10050 while (dir != newConfigBaseDir && dir != ".")
10051 {
10052 vrc = RTDirRemove(dir.c_str());
10053 if (RT_FAILURE(vrc))
10054 break;
10055 dir.stripFilename();
10056 }
10057 dirRenamed = true;
10058 }
10059 }
10060
10061 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10062
10063 /* then try to rename the settings file itself */
10064 if (newConfigFile != configFile)
10065 {
10066 /* get the path to old settings file in renamed directory */
10067 Assert(mData->m_strConfigFileFull == configFile);
10068 configFile.printf("%s%c%s",
10069 newConfigDir.c_str(),
10070 RTPATH_DELIMITER,
10071 RTPathFilename(mData->m_strConfigFileFull.c_str()));
10072 if (!fSettingsFileIsNew)
10073 {
10074 /* perform real rename only if the machine is not new */
10075 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10076 if (RT_FAILURE(vrc))
10077 {
10078 rc = setErrorBoth(E_FAIL, vrc,
10079 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10080 configFile.c_str(),
10081 newConfigFile.c_str(),
10082 vrc);
10083 break;
10084 }
10085 fileRenamed = true;
10086 configFilePrev = configFile;
10087 configFilePrev += "-prev";
10088 newConfigFilePrev = newConfigFile;
10089 newConfigFilePrev += "-prev";
10090 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10091 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
10092 if (NVRAMFile.isNotEmpty())
10093 {
10094 // in the NVRAM file path, replace the old directory with the new directory
10095 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
10096 {
10097 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
10098 NVRAMFile = newConfigDir + strNVRAMFile;
10099 }
10100 newNVRAMFile = newConfigFile;
10101 newNVRAMFile.stripSuffix();
10102 newNVRAMFile += ".nvram";
10103 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
10104 }
10105 }
10106 }
10107
10108 // update m_strConfigFileFull amd mConfigFile
10109 mData->m_strConfigFileFull = newConfigFile;
10110 // compute the relative path too
10111 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10112
10113 // store the old and new so that VirtualBox::i_saveSettings() can update
10114 // the media registry
10115 if ( mData->mRegistered
10116 && (configDir != newConfigDir || configFile != newConfigFile))
10117 {
10118 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10119
10120 if (pfNeedsGlobalSaveSettings)
10121 *pfNeedsGlobalSaveSettings = true;
10122 }
10123
10124 // in the saved state file path, replace the old directory with the new directory
10125 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10126 {
10127 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
10128 mSSData->strStateFilePath = newConfigDir + strStateFileName;
10129 }
10130 if (newNVRAMFile.isNotEmpty())
10131 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
10132
10133 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
10134 if (mData->mFirstSnapshot)
10135 {
10136 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10137 newConfigDir.c_str());
10138 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
10139 newConfigDir.c_str());
10140 }
10141 }
10142 while (0);
10143
10144 if (FAILED(rc))
10145 {
10146 /* silently try to rename everything back */
10147 if (fileRenamed)
10148 {
10149 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10150 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10151 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
10152 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
10153 }
10154 if (dirRenamed)
10155 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10156 }
10157
10158 if (FAILED(rc)) return rc;
10159 }
10160
10161 if (fSettingsFileIsNew)
10162 {
10163 /* create a virgin config file */
10164 int vrc = VINF_SUCCESS;
10165
10166 /* ensure the settings directory exists */
10167 Utf8Str path(mData->m_strConfigFileFull);
10168 path.stripFilename();
10169 if (!RTDirExists(path.c_str()))
10170 {
10171 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10172 if (RT_FAILURE(vrc))
10173 {
10174 return setErrorBoth(E_FAIL, vrc,
10175 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10176 path.c_str(),
10177 vrc);
10178 }
10179 }
10180
10181 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10182 path = mData->m_strConfigFileFull;
10183 RTFILE f = NIL_RTFILE;
10184 vrc = RTFileOpen(&f, path.c_str(),
10185 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10186 if (RT_FAILURE(vrc))
10187 return setErrorBoth(E_FAIL, vrc,
10188 tr("Could not create the settings file '%s' (%Rrc)"),
10189 path.c_str(),
10190 vrc);
10191 RTFileClose(f);
10192 }
10193 if (pfSettingsFileIsNew)
10194 *pfSettingsFileIsNew = fSettingsFileIsNew;
10195
10196 return rc;
10197}
10198
10199/**
10200 * Saves and commits machine data, user data and hardware data.
10201 *
10202 * Note that on failure, the data remains uncommitted.
10203 *
10204 * @a aFlags may combine the following flags:
10205 *
10206 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10207 * Used when saving settings after an operation that makes them 100%
10208 * correspond to the settings from the current snapshot.
10209 * - SaveS_Force: settings will be saved without doing a deep compare of the
10210 * settings structures. This is used when this is called because snapshots
10211 * have changed to avoid the overhead of the deep compare.
10212 *
10213 * @note Must be called from under this object's write lock. Locks children for
10214 * writing.
10215 *
10216 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10217 * initialized to false and that will be set to true by this function if
10218 * the caller must invoke VirtualBox::i_saveSettings() because the global
10219 * settings have changed. This will happen if a machine rename has been
10220 * saved and the global machine and media registries will therefore need
10221 * updating.
10222 * @param alock Reference to the lock for this machine object.
10223 * @param aFlags Flags.
10224 */
10225HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10226 AutoWriteLock &alock,
10227 int aFlags /*= 0*/)
10228{
10229 LogFlowThisFuncEnter();
10230
10231 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10232
10233 /* make sure child objects are unable to modify the settings while we are
10234 * saving them */
10235 i_ensureNoStateDependencies(alock);
10236
10237 AssertReturn(!i_isSnapshotMachine(),
10238 E_FAIL);
10239
10240 if (!mData->mAccessible)
10241 return setError(VBOX_E_INVALID_VM_STATE,
10242 tr("The machine is not accessible, so cannot save settings"));
10243
10244 HRESULT rc = S_OK;
10245 PCVBOXCRYPTOIF pCryptoIf = NULL;
10246 const char *pszPassword = NULL;
10247 SecretKey *pKey = NULL;
10248
10249#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10250 if (mData->mstrKeyId.isNotEmpty())
10251 {
10252 /* VM is going to be encrypted. */
10253 alock.release(); /** @todo Revise the locking. */
10254 rc = mParent->i_retainCryptoIf(&pCryptoIf);
10255 alock.acquire();
10256 if (FAILED(rc)) return rc; /* Error is set. */
10257
10258 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
10259 if (RT_SUCCESS(vrc))
10260 pszPassword = (const char *)pKey->getKeyBuffer();
10261 else
10262 {
10263 mParent->i_releaseCryptoIf(pCryptoIf);
10264 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
10265 tr("Failed to retain VM encryption password using ID '%s' with %Rrc"),
10266 mData->mstrKeyId.c_str(), vrc);
10267 }
10268 }
10269#else
10270 RT_NOREF(pKey);
10271#endif
10272
10273 bool fNeedsWrite = false;
10274 bool fSettingsFileIsNew = false;
10275
10276 /* First, prepare to save settings. It will care about renaming the
10277 * settings directory and file if the machine name was changed and about
10278 * creating a new settings file if this is a new machine. */
10279 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings,
10280 &fSettingsFileIsNew);
10281 if (FAILED(rc))
10282 {
10283#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10284 if (pCryptoIf)
10285 {
10286 alock.release(); /** @todo Revise the locking. */
10287 mParent->i_releaseCryptoIf(pCryptoIf);
10288 alock.acquire();
10289 }
10290 if (pKey)
10291 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10292#endif
10293 return rc;
10294 }
10295
10296 // keep a pointer to the current settings structures
10297 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10298 settings::MachineConfigFile *pNewConfig = NULL;
10299
10300 try
10301 {
10302 // make a fresh one to have everyone write stuff into
10303 pNewConfig = new settings::MachineConfigFile(NULL);
10304 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10305#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10306 pNewConfig->strKeyId = mData->mstrKeyId;
10307 pNewConfig->strKeyStore = mData->mstrKeyStore;
10308#endif
10309
10310 // now go and copy all the settings data from COM to the settings structures
10311 // (this calls i_saveSettings() on all the COM objects in the machine)
10312 i_copyMachineDataToSettings(*pNewConfig);
10313
10314 if (aFlags & SaveS_ResetCurStateModified)
10315 {
10316 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10317 mData->mCurrentStateModified = FALSE;
10318 fNeedsWrite = true; // always, no need to compare
10319 }
10320 else if (aFlags & SaveS_Force)
10321 {
10322 fNeedsWrite = true; // always, no need to compare
10323 }
10324 else
10325 {
10326 if (!mData->mCurrentStateModified)
10327 {
10328 // do a deep compare of the settings that we just saved with the settings
10329 // previously stored in the config file; this invokes MachineConfigFile::operator==
10330 // which does a deep compare of all the settings, which is expensive but less expensive
10331 // than writing out XML in vain
10332 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10333
10334 // could still be modified if any settings changed
10335 mData->mCurrentStateModified = fAnySettingsChanged;
10336
10337 fNeedsWrite = fAnySettingsChanged;
10338 }
10339 else
10340 fNeedsWrite = true;
10341 }
10342
10343 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10344
10345 if (fNeedsWrite)
10346 {
10347 // now spit it all out!
10348 pNewConfig->write(mData->m_strConfigFileFull, pCryptoIf, pszPassword);
10349 if (aFlags & SaveS_RemoveBackup)
10350 RTFileDelete((mData->m_strConfigFileFull + "-prev").c_str());
10351 }
10352
10353 mData->pMachineConfigFile = pNewConfig;
10354 delete pOldConfig;
10355 i_commit();
10356
10357 // after saving settings, we are no longer different from the XML on disk
10358 mData->flModifications = 0;
10359 }
10360 catch (HRESULT err)
10361 {
10362 // we assume that error info is set by the thrower
10363 rc = err;
10364
10365 // delete any newly created settings file
10366 if (fSettingsFileIsNew)
10367 RTFileDelete(mData->m_strConfigFileFull.c_str());
10368
10369 // restore old config
10370 delete pNewConfig;
10371 mData->pMachineConfigFile = pOldConfig;
10372 }
10373 catch (...)
10374 {
10375 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10376 }
10377
10378#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10379 if (pCryptoIf)
10380 {
10381 alock.release(); /** @todo Revise the locking. */
10382 mParent->i_releaseCryptoIf(pCryptoIf);
10383 alock.acquire();
10384 }
10385 if (pKey)
10386 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10387#endif
10388
10389 if (fNeedsWrite)
10390 {
10391 /* Fire the data change event, even on failure (since we've already
10392 * committed all data). This is done only for SessionMachines because
10393 * mutable Machine instances are always not registered (i.e. private
10394 * to the client process that creates them) and thus don't need to
10395 * inform callbacks. */
10396 if (i_isSessionMachine())
10397 mParent->i_onMachineDataChanged(mData->mUuid);
10398 }
10399
10400 LogFlowThisFunc(("rc=%08X\n", rc));
10401 LogFlowThisFuncLeave();
10402 return rc;
10403}
10404
10405/**
10406 * Implementation for saving the machine settings into the given
10407 * settings::MachineConfigFile instance. This copies machine extradata
10408 * from the previous machine config file in the instance data, if any.
10409 *
10410 * This gets called from two locations:
10411 *
10412 * -- Machine::i_saveSettings(), during the regular XML writing;
10413 *
10414 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10415 * exported to OVF and we write the VirtualBox proprietary XML
10416 * into a <vbox:Machine> tag.
10417 *
10418 * This routine fills all the fields in there, including snapshots, *except*
10419 * for the following:
10420 *
10421 * -- fCurrentStateModified. There is some special logic associated with that.
10422 *
10423 * The caller can then call MachineConfigFile::write() or do something else
10424 * with it.
10425 *
10426 * Caller must hold the machine lock!
10427 *
10428 * This throws XML errors and HRESULT, so the caller must have a catch block!
10429 */
10430void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10431{
10432 // deep copy extradata, being extra careful with self assignment (the STL
10433 // map assignment on Mac OS X clang based Xcode isn't checking)
10434 if (&config != mData->pMachineConfigFile)
10435 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10436
10437 config.uuid = mData->mUuid;
10438
10439 // copy name, description, OS type, teleport, UTC etc.
10440 config.machineUserData = mUserData->s;
10441
10442 if ( mData->mMachineState == MachineState_Saved
10443 || mData->mMachineState == MachineState_AbortedSaved
10444 || mData->mMachineState == MachineState_Restoring
10445 // when doing certain snapshot operations we may or may not have
10446 // a saved state in the current state, so keep everything as is
10447 || ( ( mData->mMachineState == MachineState_Snapshotting
10448 || mData->mMachineState == MachineState_DeletingSnapshot
10449 || mData->mMachineState == MachineState_RestoringSnapshot)
10450 && (!mSSData->strStateFilePath.isEmpty())
10451 )
10452 )
10453 {
10454 Assert(!mSSData->strStateFilePath.isEmpty());
10455 /* try to make the file name relative to the settings file dir */
10456 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10457 }
10458 else
10459 {
10460 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10461 config.strStateFile.setNull();
10462 }
10463
10464 if (mData->mCurrentSnapshot)
10465 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10466 else
10467 config.uuidCurrentSnapshot.clear();
10468
10469 config.timeLastStateChange = mData->mLastStateChange;
10470 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10471 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10472
10473 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10474 if (FAILED(rc)) throw rc;
10475
10476 // save machine's media registry if this is VirtualBox 4.0 or later
10477 if (config.canHaveOwnMediaRegistry())
10478 {
10479 // determine machine folder
10480 Utf8Str strMachineFolder = i_getSettingsFileFull();
10481 strMachineFolder.stripFilename();
10482 mParent->i_saveMediaRegistry(config.mediaRegistry,
10483 i_getId(), // only media with registry ID == machine UUID
10484 strMachineFolder);
10485 // this throws HRESULT
10486 }
10487
10488 // save snapshots
10489 rc = i_saveAllSnapshots(config);
10490 if (FAILED(rc)) throw rc;
10491}
10492
10493/**
10494 * Saves all snapshots of the machine into the given machine config file. Called
10495 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10496 * @param config
10497 * @return
10498 */
10499HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10500{
10501 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10502
10503 HRESULT rc = S_OK;
10504
10505 try
10506 {
10507 config.llFirstSnapshot.clear();
10508
10509 if (mData->mFirstSnapshot)
10510 {
10511 // the settings use a list for "the first snapshot"
10512 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10513
10514 // get reference to the snapshot on the list and work on that
10515 // element straight in the list to avoid excessive copying later
10516 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10517 if (FAILED(rc)) throw rc;
10518 }
10519
10520// if (mType == IsSessionMachine)
10521// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10522
10523 }
10524 catch (HRESULT err)
10525 {
10526 /* we assume that error info is set by the thrower */
10527 rc = err;
10528 }
10529 catch (...)
10530 {
10531 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10532 }
10533
10534 return rc;
10535}
10536
10537/**
10538 * Saves the VM hardware configuration. It is assumed that the
10539 * given node is empty.
10540 *
10541 * @param data Reference to the settings object for the hardware config.
10542 * @param pDbg Pointer to the settings object for the debugging config
10543 * which happens to live in mHWData.
10544 * @param pAutostart Pointer to the settings object for the autostart config
10545 * which happens to live in mHWData.
10546 */
10547HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10548 settings::Autostart *pAutostart)
10549{
10550 HRESULT rc = S_OK;
10551
10552 try
10553 {
10554 /* The hardware version attribute (optional).
10555 Automatically upgrade from 1 to current default hardware version
10556 when there is no saved state. (ugly!) */
10557 if ( mHWData->mHWVersion == "1"
10558 && mSSData->strStateFilePath.isEmpty()
10559 )
10560 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10561
10562 data.strVersion = mHWData->mHWVersion;
10563 data.uuid = mHWData->mHardwareUUID;
10564
10565 // CPU
10566 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10567 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10568 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10569 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10570 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10571 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10572 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10573 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10574 data.fPAE = !!mHWData->mPAEEnabled;
10575 data.enmLongMode = mHWData->mLongMode;
10576 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10577 data.fAPIC = !!mHWData->mAPIC;
10578 data.fX2APIC = !!mHWData->mX2APIC;
10579 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10580 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10581 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10582 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10583 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10584 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10585 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10586 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10587 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10588 data.cCPUs = mHWData->mCPUCount;
10589 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10590 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10591 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10592 data.strCpuProfile = mHWData->mCpuProfile;
10593
10594 data.llCpus.clear();
10595 if (data.fCpuHotPlug)
10596 {
10597 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10598 {
10599 if (mHWData->mCPUAttached[idx])
10600 {
10601 settings::Cpu cpu;
10602 cpu.ulId = idx;
10603 data.llCpus.push_back(cpu);
10604 }
10605 }
10606 }
10607
10608 /* Standard and Extended CPUID leafs. */
10609 data.llCpuIdLeafs.clear();
10610 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10611
10612 // memory
10613 data.ulMemorySizeMB = mHWData->mMemorySize;
10614 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10615
10616 // firmware
10617 data.firmwareType = mHWData->mFirmwareType;
10618
10619 // HID
10620 data.pointingHIDType = mHWData->mPointingHIDType;
10621 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10622
10623 // chipset
10624 data.chipsetType = mHWData->mChipsetType;
10625
10626 // iommu
10627 data.iommuType = mHWData->mIommuType;
10628
10629 // paravirt
10630 data.paravirtProvider = mHWData->mParavirtProvider;
10631 data.strParavirtDebug = mHWData->mParavirtDebug;
10632
10633 // emulated USB card reader
10634 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10635
10636 // HPET
10637 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10638
10639 // boot order
10640 data.mapBootOrder.clear();
10641 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10642 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10643
10644 /* VRDEServer settings (optional) */
10645 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10646 if (FAILED(rc)) throw rc;
10647
10648 /* BIOS settings (required) */
10649 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10650 if (FAILED(rc)) throw rc;
10651
10652 /* Trusted Platform Module settings (required) */
10653 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10654 if (FAILED(rc)) throw rc;
10655
10656 /* NVRAM settings (required) */
10657 rc = mNvramStore->i_saveSettings(data.nvramSettings);
10658 if (FAILED(rc)) throw rc;
10659
10660 /* Recording settings (required) */
10661 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10662 if (FAILED(rc)) throw rc;
10663
10664 /* GraphicsAdapter settings (required) */
10665 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10666 if (FAILED(rc)) throw rc;
10667
10668 /* USB Controller (required) */
10669 data.usbSettings.llUSBControllers.clear();
10670 for (USBControllerList::const_iterator
10671 it = mUSBControllers->begin();
10672 it != mUSBControllers->end();
10673 ++it)
10674 {
10675 ComObjPtr<USBController> ctrl = *it;
10676 settings::USBController settingsCtrl;
10677
10678 settingsCtrl.strName = ctrl->i_getName();
10679 settingsCtrl.enmType = ctrl->i_getControllerType();
10680
10681 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10682 }
10683
10684 /* USB device filters (required) */
10685 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10686 if (FAILED(rc)) throw rc;
10687
10688 /* Network adapters (required) */
10689 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10690 data.llNetworkAdapters.clear();
10691 /* Write out only the nominal number of network adapters for this
10692 * chipset type. Since Machine::commit() hasn't been called there
10693 * may be extra NIC settings in the vector. */
10694 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10695 {
10696 settings::NetworkAdapter nic;
10697 nic.ulSlot = (uint32_t)slot;
10698 /* paranoia check... must not be NULL, but must not crash either. */
10699 if (mNetworkAdapters[slot])
10700 {
10701 if (mNetworkAdapters[slot]->i_hasDefaults())
10702 continue;
10703
10704 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10705 if (FAILED(rc)) throw rc;
10706
10707 data.llNetworkAdapters.push_back(nic);
10708 }
10709 }
10710
10711 /* Serial ports */
10712 data.llSerialPorts.clear();
10713 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10714 {
10715 if (mSerialPorts[slot]->i_hasDefaults())
10716 continue;
10717
10718 settings::SerialPort s;
10719 s.ulSlot = slot;
10720 rc = mSerialPorts[slot]->i_saveSettings(s);
10721 if (FAILED(rc)) return rc;
10722
10723 data.llSerialPorts.push_back(s);
10724 }
10725
10726 /* Parallel ports */
10727 data.llParallelPorts.clear();
10728 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10729 {
10730 if (mParallelPorts[slot]->i_hasDefaults())
10731 continue;
10732
10733 settings::ParallelPort p;
10734 p.ulSlot = slot;
10735 rc = mParallelPorts[slot]->i_saveSettings(p);
10736 if (FAILED(rc)) return rc;
10737
10738 data.llParallelPorts.push_back(p);
10739 }
10740
10741 /* Audio adapter */
10742 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10743 if (FAILED(rc)) return rc;
10744
10745 rc = i_saveStorageControllers(data.storage);
10746 if (FAILED(rc)) return rc;
10747
10748 /* Shared folders */
10749 data.llSharedFolders.clear();
10750 for (HWData::SharedFolderList::const_iterator
10751 it = mHWData->mSharedFolders.begin();
10752 it != mHWData->mSharedFolders.end();
10753 ++it)
10754 {
10755 SharedFolder *pSF = *it;
10756 AutoCaller sfCaller(pSF);
10757 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10758 settings::SharedFolder sf;
10759 sf.strName = pSF->i_getName();
10760 sf.strHostPath = pSF->i_getHostPath();
10761 sf.fWritable = !!pSF->i_isWritable();
10762 sf.fAutoMount = !!pSF->i_isAutoMounted();
10763 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10764
10765 data.llSharedFolders.push_back(sf);
10766 }
10767
10768 // clipboard
10769 data.clipboardMode = mHWData->mClipboardMode;
10770 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10771
10772 // drag'n'drop
10773 data.dndMode = mHWData->mDnDMode;
10774
10775 /* Guest */
10776 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10777
10778 // IO settings
10779 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10780 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10781
10782 /* BandwidthControl (required) */
10783 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10784 if (FAILED(rc)) throw rc;
10785
10786 /* Host PCI devices */
10787 data.pciAttachments.clear();
10788 for (HWData::PCIDeviceAssignmentList::const_iterator
10789 it = mHWData->mPCIDeviceAssignments.begin();
10790 it != mHWData->mPCIDeviceAssignments.end();
10791 ++it)
10792 {
10793 ComObjPtr<PCIDeviceAttachment> pda = *it;
10794 settings::HostPCIDeviceAttachment hpda;
10795
10796 rc = pda->i_saveSettings(hpda);
10797 if (FAILED(rc)) throw rc;
10798
10799 data.pciAttachments.push_back(hpda);
10800 }
10801
10802 // guest properties
10803 data.llGuestProperties.clear();
10804#ifdef VBOX_WITH_GUEST_PROPS
10805 for (HWData::GuestPropertyMap::const_iterator
10806 it = mHWData->mGuestProperties.begin();
10807 it != mHWData->mGuestProperties.end();
10808 ++it)
10809 {
10810 HWData::GuestProperty property = it->second;
10811
10812 /* Remove transient guest properties at shutdown unless we
10813 * are saving state. Note that restoring snapshot intentionally
10814 * keeps them, they will be removed if appropriate once the final
10815 * machine state is set (as crashes etc. need to work). */
10816 if ( ( mData->mMachineState == MachineState_PoweredOff
10817 || mData->mMachineState == MachineState_Aborted
10818 || mData->mMachineState == MachineState_Teleported)
10819 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10820 continue;
10821 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10822 prop.strName = it->first;
10823 prop.strValue = property.strValue;
10824 prop.timestamp = (uint64_t)property.mTimestamp;
10825 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10826 GuestPropWriteFlags(property.mFlags, szFlags);
10827 prop.strFlags = szFlags;
10828
10829 data.llGuestProperties.push_back(prop);
10830 }
10831
10832 /* I presume this doesn't require a backup(). */
10833 mData->mGuestPropertiesModified = FALSE;
10834#endif /* VBOX_WITH_GUEST_PROPS defined */
10835
10836 *pDbg = mHWData->mDebugging;
10837 *pAutostart = mHWData->mAutostart;
10838
10839 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10840 }
10841 catch (std::bad_alloc &)
10842 {
10843 return E_OUTOFMEMORY;
10844 }
10845
10846 AssertComRC(rc);
10847 return rc;
10848}
10849
10850/**
10851 * Saves the storage controller configuration.
10852 *
10853 * @param data storage settings.
10854 */
10855HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10856{
10857 data.llStorageControllers.clear();
10858
10859 for (StorageControllerList::const_iterator
10860 it = mStorageControllers->begin();
10861 it != mStorageControllers->end();
10862 ++it)
10863 {
10864 HRESULT rc;
10865 ComObjPtr<StorageController> pCtl = *it;
10866
10867 settings::StorageController ctl;
10868 ctl.strName = pCtl->i_getName();
10869 ctl.controllerType = pCtl->i_getControllerType();
10870 ctl.storageBus = pCtl->i_getStorageBus();
10871 ctl.ulInstance = pCtl->i_getInstance();
10872 ctl.fBootable = pCtl->i_getBootable();
10873
10874 /* Save the port count. */
10875 ULONG portCount;
10876 rc = pCtl->COMGETTER(PortCount)(&portCount);
10877 ComAssertComRCRet(rc, rc);
10878 ctl.ulPortCount = portCount;
10879
10880 /* Save fUseHostIOCache */
10881 BOOL fUseHostIOCache;
10882 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10883 ComAssertComRCRet(rc, rc);
10884 ctl.fUseHostIOCache = !!fUseHostIOCache;
10885
10886 /* save the devices now. */
10887 rc = i_saveStorageDevices(pCtl, ctl);
10888 ComAssertComRCRet(rc, rc);
10889
10890 data.llStorageControllers.push_back(ctl);
10891 }
10892
10893 return S_OK;
10894}
10895
10896/**
10897 * Saves the hard disk configuration.
10898 */
10899HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10900 settings::StorageController &data)
10901{
10902 MediumAttachmentList atts;
10903
10904 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10905 if (FAILED(rc)) return rc;
10906
10907 data.llAttachedDevices.clear();
10908 for (MediumAttachmentList::const_iterator
10909 it = atts.begin();
10910 it != atts.end();
10911 ++it)
10912 {
10913 settings::AttachedDevice dev;
10914 IMediumAttachment *iA = *it;
10915 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10916 Medium *pMedium = pAttach->i_getMedium();
10917
10918 dev.deviceType = pAttach->i_getType();
10919 dev.lPort = pAttach->i_getPort();
10920 dev.lDevice = pAttach->i_getDevice();
10921 dev.fPassThrough = pAttach->i_getPassthrough();
10922 dev.fHotPluggable = pAttach->i_getHotPluggable();
10923 if (pMedium)
10924 {
10925 if (pMedium->i_isHostDrive())
10926 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10927 else
10928 dev.uuid = pMedium->i_getId();
10929 dev.fTempEject = pAttach->i_getTempEject();
10930 dev.fNonRotational = pAttach->i_getNonRotational();
10931 dev.fDiscard = pAttach->i_getDiscard();
10932 }
10933
10934 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10935
10936 data.llAttachedDevices.push_back(dev);
10937 }
10938
10939 return S_OK;
10940}
10941
10942/**
10943 * Saves machine state settings as defined by aFlags
10944 * (SaveSTS_* values).
10945 *
10946 * @param aFlags Combination of SaveSTS_* flags.
10947 *
10948 * @note Locks objects for writing.
10949 */
10950HRESULT Machine::i_saveStateSettings(int aFlags)
10951{
10952 if (aFlags == 0)
10953 return S_OK;
10954
10955 AutoCaller autoCaller(this);
10956 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10957
10958 /* This object's write lock is also necessary to serialize file access
10959 * (prevent concurrent reads and writes) */
10960 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10961
10962 HRESULT rc = S_OK;
10963
10964 Assert(mData->pMachineConfigFile);
10965
10966 try
10967 {
10968 if (aFlags & SaveSTS_CurStateModified)
10969 mData->pMachineConfigFile->fCurrentStateModified = true;
10970
10971 if (aFlags & SaveSTS_StateFilePath)
10972 {
10973 if (!mSSData->strStateFilePath.isEmpty())
10974 /* try to make the file name relative to the settings file dir */
10975 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10976 else
10977 mData->pMachineConfigFile->strStateFile.setNull();
10978 }
10979
10980 if (aFlags & SaveSTS_StateTimeStamp)
10981 {
10982 Assert( mData->mMachineState != MachineState_Aborted
10983 || mSSData->strStateFilePath.isEmpty());
10984
10985 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10986
10987 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
10988 || mData->mMachineState == MachineState_AbortedSaved);
10989/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10990 }
10991
10992 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10993 }
10994 catch (...)
10995 {
10996 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10997 }
10998
10999 return rc;
11000}
11001
11002/**
11003 * Ensures that the given medium is added to a media registry. If this machine
11004 * was created with 4.0 or later, then the machine registry is used. Otherwise
11005 * the global VirtualBox media registry is used.
11006 *
11007 * Caller must NOT hold machine lock, media tree or any medium locks!
11008 *
11009 * @param pMedium
11010 */
11011void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
11012{
11013 /* Paranoia checks: do not hold machine or media tree locks. */
11014 AssertReturnVoid(!isWriteLockOnCurrentThread());
11015 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
11016
11017 ComObjPtr<Medium> pBase;
11018 {
11019 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11020 pBase = pMedium->i_getBase();
11021 }
11022
11023 /* Paranoia checks: do not hold medium locks. */
11024 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
11025 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
11026
11027 // decide which medium registry to use now that the medium is attached:
11028 Guid uuid;
11029 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
11030 if (fCanHaveOwnMediaRegistry)
11031 // machine XML is VirtualBox 4.0 or higher:
11032 uuid = i_getId(); // machine UUID
11033 else
11034 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
11035
11036 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
11037 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11038 if (pMedium->i_addRegistry(uuid))
11039 mParent->i_markRegistryModified(uuid);
11040
11041 /* For more complex hard disk structures it can happen that the base
11042 * medium isn't yet associated with any medium registry. Do that now. */
11043 if (pMedium != pBase)
11044 {
11045 /* Tree lock needed by Medium::addRegistryAll. */
11046 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11047 if (fCanHaveOwnMediaRegistry && pBase->i_removeRegistryAll(mParent->i_getGlobalRegistryId()))
11048 {
11049 treeLock.release();
11050 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11051 treeLock.acquire();
11052 }
11053 if (pBase->i_addRegistryAll(uuid))
11054 {
11055 treeLock.release();
11056 mParent->i_markRegistryModified(uuid);
11057 }
11058 }
11059}
11060
11061/**
11062 * Creates differencing hard disks for all normal hard disks attached to this
11063 * machine and a new set of attachments to refer to created disks.
11064 *
11065 * Used when taking a snapshot or when deleting the current state. Gets called
11066 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
11067 *
11068 * This method assumes that mMediumAttachments contains the original hard disk
11069 * attachments it needs to create diffs for. On success, these attachments will
11070 * be replaced with the created diffs.
11071 *
11072 * Attachments with non-normal hard disks are left as is.
11073 *
11074 * If @a aOnline is @c false then the original hard disks that require implicit
11075 * diffs will be locked for reading. Otherwise it is assumed that they are
11076 * already locked for writing (when the VM was started). Note that in the latter
11077 * case it is responsibility of the caller to lock the newly created diffs for
11078 * writing if this method succeeds.
11079 *
11080 * @param aProgress Progress object to run (must contain at least as
11081 * many operations left as the number of hard disks
11082 * attached).
11083 * @param aWeight Weight of this operation.
11084 * @param aOnline Whether the VM was online prior to this operation.
11085 *
11086 * @note The progress object is not marked as completed, neither on success nor
11087 * on failure. This is a responsibility of the caller.
11088 *
11089 * @note Locks this object and the media tree for writing.
11090 */
11091HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
11092 ULONG aWeight,
11093 bool aOnline)
11094{
11095 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11096
11097 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
11098 AssertReturn(!!pProgressControl, E_INVALIDARG);
11099
11100 AutoCaller autoCaller(this);
11101 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11102
11103 AutoMultiWriteLock2 alock(this->lockHandle(),
11104 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11105
11106 /* must be in a protective state because we release the lock below */
11107 AssertReturn( mData->mMachineState == MachineState_Snapshotting
11108 || mData->mMachineState == MachineState_OnlineSnapshotting
11109 || mData->mMachineState == MachineState_LiveSnapshotting
11110 || mData->mMachineState == MachineState_RestoringSnapshot
11111 || mData->mMachineState == MachineState_DeletingSnapshot
11112 , E_FAIL);
11113
11114 HRESULT rc = S_OK;
11115
11116 // use appropriate locked media map (online or offline)
11117 MediumLockListMap lockedMediaOffline;
11118 MediumLockListMap *lockedMediaMap;
11119 if (aOnline)
11120 lockedMediaMap = &mData->mSession.mLockedMedia;
11121 else
11122 lockedMediaMap = &lockedMediaOffline;
11123
11124 try
11125 {
11126 if (!aOnline)
11127 {
11128 /* lock all attached hard disks early to detect "in use"
11129 * situations before creating actual diffs */
11130 for (MediumAttachmentList::const_iterator
11131 it = mMediumAttachments->begin();
11132 it != mMediumAttachments->end();
11133 ++it)
11134 {
11135 MediumAttachment *pAtt = *it;
11136 if (pAtt->i_getType() == DeviceType_HardDisk)
11137 {
11138 Medium *pMedium = pAtt->i_getMedium();
11139 Assert(pMedium);
11140
11141 MediumLockList *pMediumLockList(new MediumLockList());
11142 alock.release();
11143 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11144 NULL /* pToLockWrite */,
11145 false /* fMediumLockWriteAll */,
11146 NULL,
11147 *pMediumLockList);
11148 alock.acquire();
11149 if (FAILED(rc))
11150 {
11151 delete pMediumLockList;
11152 throw rc;
11153 }
11154 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11155 if (FAILED(rc))
11156 {
11157 throw setError(rc,
11158 tr("Collecting locking information for all attached media failed"));
11159 }
11160 }
11161 }
11162
11163 /* Now lock all media. If this fails, nothing is locked. */
11164 alock.release();
11165 rc = lockedMediaMap->Lock();
11166 alock.acquire();
11167 if (FAILED(rc))
11168 {
11169 throw setError(rc,
11170 tr("Locking of attached media failed"));
11171 }
11172 }
11173
11174 /* remember the current list (note that we don't use backup() since
11175 * mMediumAttachments may be already backed up) */
11176 MediumAttachmentList atts = *mMediumAttachments.data();
11177
11178 /* start from scratch */
11179 mMediumAttachments->clear();
11180
11181 /* go through remembered attachments and create diffs for normal hard
11182 * disks and attach them */
11183 for (MediumAttachmentList::const_iterator
11184 it = atts.begin();
11185 it != atts.end();
11186 ++it)
11187 {
11188 MediumAttachment *pAtt = *it;
11189
11190 DeviceType_T devType = pAtt->i_getType();
11191 Medium *pMedium = pAtt->i_getMedium();
11192
11193 if ( devType != DeviceType_HardDisk
11194 || pMedium == NULL
11195 || pMedium->i_getType() != MediumType_Normal)
11196 {
11197 /* copy the attachment as is */
11198
11199 /** @todo the progress object created in SessionMachine::TakeSnaphot
11200 * only expects operations for hard disks. Later other
11201 * device types need to show up in the progress as well. */
11202 if (devType == DeviceType_HardDisk)
11203 {
11204 if (pMedium == NULL)
11205 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11206 aWeight); // weight
11207 else
11208 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11209 pMedium->i_getBase()->i_getName().c_str()).raw(),
11210 aWeight); // weight
11211 }
11212
11213 mMediumAttachments->push_back(pAtt);
11214 continue;
11215 }
11216
11217 /* need a diff */
11218 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11219 pMedium->i_getBase()->i_getName().c_str()).raw(),
11220 aWeight); // weight
11221
11222 Utf8Str strFullSnapshotFolder;
11223 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11224
11225 ComObjPtr<Medium> diff;
11226 diff.createObject();
11227 // store the diff in the same registry as the parent
11228 // (this cannot fail here because we can't create implicit diffs for
11229 // unregistered images)
11230 Guid uuidRegistryParent;
11231 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11232 Assert(fInRegistry); NOREF(fInRegistry);
11233 rc = diff->init(mParent,
11234 pMedium->i_getPreferredDiffFormat(),
11235 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11236 uuidRegistryParent,
11237 DeviceType_HardDisk);
11238 if (FAILED(rc)) throw rc;
11239
11240 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11241 * the push_back? Looks like we're going to release medium with the
11242 * wrong kind of lock (general issue with if we fail anywhere at all)
11243 * and an orphaned VDI in the snapshots folder. */
11244
11245 /* update the appropriate lock list */
11246 MediumLockList *pMediumLockList;
11247 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11248 AssertComRCThrowRC(rc);
11249 if (aOnline)
11250 {
11251 alock.release();
11252 /* The currently attached medium will be read-only, change
11253 * the lock type to read. */
11254 rc = pMediumLockList->Update(pMedium, false);
11255 alock.acquire();
11256 AssertComRCThrowRC(rc);
11257 }
11258
11259 /* release the locks before the potentially lengthy operation */
11260 alock.release();
11261 rc = pMedium->i_createDiffStorage(diff,
11262 pMedium->i_getPreferredDiffVariant(),
11263 pMediumLockList,
11264 NULL /* aProgress */,
11265 true /* aWait */,
11266 false /* aNotify */);
11267 alock.acquire();
11268 if (FAILED(rc)) throw rc;
11269
11270 /* actual lock list update is done in Machine::i_commitMedia */
11271
11272 rc = diff->i_addBackReference(mData->mUuid);
11273 AssertComRCThrowRC(rc);
11274
11275 /* add a new attachment */
11276 ComObjPtr<MediumAttachment> attachment;
11277 attachment.createObject();
11278 rc = attachment->init(this,
11279 diff,
11280 pAtt->i_getControllerName(),
11281 pAtt->i_getPort(),
11282 pAtt->i_getDevice(),
11283 DeviceType_HardDisk,
11284 true /* aImplicit */,
11285 false /* aPassthrough */,
11286 false /* aTempEject */,
11287 pAtt->i_getNonRotational(),
11288 pAtt->i_getDiscard(),
11289 pAtt->i_getHotPluggable(),
11290 pAtt->i_getBandwidthGroup());
11291 if (FAILED(rc)) throw rc;
11292
11293 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11294 AssertComRCThrowRC(rc);
11295 mMediumAttachments->push_back(attachment);
11296 }
11297 }
11298 catch (HRESULT aRC) { rc = aRC; }
11299
11300 /* unlock all hard disks we locked when there is no VM */
11301 if (!aOnline)
11302 {
11303 ErrorInfoKeeper eik;
11304
11305 HRESULT rc1 = lockedMediaMap->Clear();
11306 AssertComRC(rc1);
11307 }
11308
11309 return rc;
11310}
11311
11312/**
11313 * Deletes implicit differencing hard disks created either by
11314 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11315 * mMediumAttachments.
11316 *
11317 * Note that to delete hard disks created by #attachDevice() this method is
11318 * called from #i_rollbackMedia() when the changes are rolled back.
11319 *
11320 * @note Locks this object and the media tree for writing.
11321 */
11322HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11323{
11324 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11325
11326 AutoCaller autoCaller(this);
11327 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11328
11329 AutoMultiWriteLock2 alock(this->lockHandle(),
11330 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11331
11332 /* We absolutely must have backed up state. */
11333 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11334
11335 /* Check if there are any implicitly created diff images. */
11336 bool fImplicitDiffs = false;
11337 for (MediumAttachmentList::const_iterator
11338 it = mMediumAttachments->begin();
11339 it != mMediumAttachments->end();
11340 ++it)
11341 {
11342 const ComObjPtr<MediumAttachment> &pAtt = *it;
11343 if (pAtt->i_isImplicit())
11344 {
11345 fImplicitDiffs = true;
11346 break;
11347 }
11348 }
11349 /* If there is nothing to do, leave early. This saves lots of image locking
11350 * effort. It also avoids a MachineStateChanged event without real reason.
11351 * This is important e.g. when loading a VM config, because there should be
11352 * no events. Otherwise API clients can become thoroughly confused for
11353 * inaccessible VMs (the code for loading VM configs uses this method for
11354 * cleanup if the config makes no sense), as they take such events as an
11355 * indication that the VM is alive, and they would force the VM config to
11356 * be reread, leading to an endless loop. */
11357 if (!fImplicitDiffs)
11358 return S_OK;
11359
11360 HRESULT rc = S_OK;
11361 MachineState_T oldState = mData->mMachineState;
11362
11363 /* will release the lock before the potentially lengthy operation,
11364 * so protect with the special state (unless already protected) */
11365 if ( oldState != MachineState_Snapshotting
11366 && oldState != MachineState_OnlineSnapshotting
11367 && oldState != MachineState_LiveSnapshotting
11368 && oldState != MachineState_RestoringSnapshot
11369 && oldState != MachineState_DeletingSnapshot
11370 && oldState != MachineState_DeletingSnapshotOnline
11371 && oldState != MachineState_DeletingSnapshotPaused
11372 )
11373 i_setMachineState(MachineState_SettingUp);
11374
11375 // use appropriate locked media map (online or offline)
11376 MediumLockListMap lockedMediaOffline;
11377 MediumLockListMap *lockedMediaMap;
11378 if (aOnline)
11379 lockedMediaMap = &mData->mSession.mLockedMedia;
11380 else
11381 lockedMediaMap = &lockedMediaOffline;
11382
11383 try
11384 {
11385 if (!aOnline)
11386 {
11387 /* lock all attached hard disks early to detect "in use"
11388 * situations before deleting actual diffs */
11389 for (MediumAttachmentList::const_iterator
11390 it = mMediumAttachments->begin();
11391 it != mMediumAttachments->end();
11392 ++it)
11393 {
11394 MediumAttachment *pAtt = *it;
11395 if (pAtt->i_getType() == DeviceType_HardDisk)
11396 {
11397 Medium *pMedium = pAtt->i_getMedium();
11398 Assert(pMedium);
11399
11400 MediumLockList *pMediumLockList(new MediumLockList());
11401 alock.release();
11402 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11403 NULL /* pToLockWrite */,
11404 false /* fMediumLockWriteAll */,
11405 NULL,
11406 *pMediumLockList);
11407 alock.acquire();
11408
11409 if (FAILED(rc))
11410 {
11411 delete pMediumLockList;
11412 throw rc;
11413 }
11414
11415 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11416 if (FAILED(rc))
11417 throw rc;
11418 }
11419 }
11420
11421 if (FAILED(rc))
11422 throw rc;
11423 } // end of offline
11424
11425 /* Lock lists are now up to date and include implicitly created media */
11426
11427 /* Go through remembered attachments and delete all implicitly created
11428 * diffs and fix up the attachment information */
11429 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11430 MediumAttachmentList implicitAtts;
11431 for (MediumAttachmentList::const_iterator
11432 it = mMediumAttachments->begin();
11433 it != mMediumAttachments->end();
11434 ++it)
11435 {
11436 ComObjPtr<MediumAttachment> pAtt = *it;
11437 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11438 if (pMedium.isNull())
11439 continue;
11440
11441 // Implicit attachments go on the list for deletion and back references are removed.
11442 if (pAtt->i_isImplicit())
11443 {
11444 /* Deassociate and mark for deletion */
11445 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11446 rc = pMedium->i_removeBackReference(mData->mUuid);
11447 if (FAILED(rc))
11448 throw rc;
11449 implicitAtts.push_back(pAtt);
11450 continue;
11451 }
11452
11453 /* Was this medium attached before? */
11454 if (!i_findAttachment(oldAtts, pMedium))
11455 {
11456 /* no: de-associate */
11457 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11458 rc = pMedium->i_removeBackReference(mData->mUuid);
11459 if (FAILED(rc))
11460 throw rc;
11461 continue;
11462 }
11463 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11464 }
11465
11466 /* If there are implicit attachments to delete, throw away the lock
11467 * map contents (which will unlock all media) since the medium
11468 * attachments will be rolled back. Below we need to completely
11469 * recreate the lock map anyway since it is infinitely complex to
11470 * do this incrementally (would need reconstructing each attachment
11471 * change, which would be extremely hairy). */
11472 if (implicitAtts.size() != 0)
11473 {
11474 ErrorInfoKeeper eik;
11475
11476 HRESULT rc1 = lockedMediaMap->Clear();
11477 AssertComRC(rc1);
11478 }
11479
11480 /* rollback hard disk changes */
11481 mMediumAttachments.rollback();
11482
11483 MultiResult mrc(S_OK);
11484
11485 // Delete unused implicit diffs.
11486 if (implicitAtts.size() != 0)
11487 {
11488 alock.release();
11489
11490 for (MediumAttachmentList::const_iterator
11491 it = implicitAtts.begin();
11492 it != implicitAtts.end();
11493 ++it)
11494 {
11495 // Remove medium associated with this attachment.
11496 ComObjPtr<MediumAttachment> pAtt = *it;
11497 Assert(pAtt);
11498 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11499 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11500 Assert(pMedium);
11501
11502 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11503 // continue on delete failure, just collect error messages
11504 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11505 pMedium->i_getLocationFull().c_str() ));
11506 mrc = rc;
11507 }
11508 // Clear the list of deleted implicit attachments now, while not
11509 // holding the lock, as it will ultimately trigger Medium::uninit()
11510 // calls which assume that the media tree lock isn't held.
11511 implicitAtts.clear();
11512
11513 alock.acquire();
11514
11515 /* if there is a VM recreate media lock map as mentioned above,
11516 * otherwise it is a waste of time and we leave things unlocked */
11517 if (aOnline)
11518 {
11519 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11520 /* must never be NULL, but better safe than sorry */
11521 if (!pMachine.isNull())
11522 {
11523 alock.release();
11524 rc = mData->mSession.mMachine->i_lockMedia();
11525 alock.acquire();
11526 if (FAILED(rc))
11527 throw rc;
11528 }
11529 }
11530 }
11531 }
11532 catch (HRESULT aRC) {rc = aRC;}
11533
11534 if (mData->mMachineState == MachineState_SettingUp)
11535 i_setMachineState(oldState);
11536
11537 /* unlock all hard disks we locked when there is no VM */
11538 if (!aOnline)
11539 {
11540 ErrorInfoKeeper eik;
11541
11542 HRESULT rc1 = lockedMediaMap->Clear();
11543 AssertComRC(rc1);
11544 }
11545
11546 return rc;
11547}
11548
11549
11550/**
11551 * Looks through the given list of media attachments for one with the given parameters
11552 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11553 * can be searched as well if needed.
11554 *
11555 * @param ll
11556 * @param aControllerName
11557 * @param aControllerPort
11558 * @param aDevice
11559 * @return
11560 */
11561MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11562 const Utf8Str &aControllerName,
11563 LONG aControllerPort,
11564 LONG aDevice)
11565{
11566 for (MediumAttachmentList::const_iterator
11567 it = ll.begin();
11568 it != ll.end();
11569 ++it)
11570 {
11571 MediumAttachment *pAttach = *it;
11572 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11573 return pAttach;
11574 }
11575
11576 return NULL;
11577}
11578
11579/**
11580 * Looks through the given list of media attachments for one with the given parameters
11581 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11582 * can be searched as well if needed.
11583 *
11584 * @param ll
11585 * @param pMedium
11586 * @return
11587 */
11588MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11589 ComObjPtr<Medium> pMedium)
11590{
11591 for (MediumAttachmentList::const_iterator
11592 it = ll.begin();
11593 it != ll.end();
11594 ++it)
11595 {
11596 MediumAttachment *pAttach = *it;
11597 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11598 if (pMediumThis == pMedium)
11599 return pAttach;
11600 }
11601
11602 return NULL;
11603}
11604
11605/**
11606 * Looks through the given list of media attachments for one with the given parameters
11607 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11608 * can be searched as well if needed.
11609 *
11610 * @param ll
11611 * @param id
11612 * @return
11613 */
11614MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11615 Guid &id)
11616{
11617 for (MediumAttachmentList::const_iterator
11618 it = ll.begin();
11619 it != ll.end();
11620 ++it)
11621 {
11622 MediumAttachment *pAttach = *it;
11623 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11624 if (pMediumThis->i_getId() == id)
11625 return pAttach;
11626 }
11627
11628 return NULL;
11629}
11630
11631/**
11632 * Main implementation for Machine::DetachDevice. This also gets called
11633 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11634 *
11635 * @param pAttach Medium attachment to detach.
11636 * @param writeLock Machine write lock which the caller must have locked once.
11637 * This may be released temporarily in here.
11638 * @param pSnapshot If NULL, then the detachment is for the current machine.
11639 * Otherwise this is for a SnapshotMachine, and this must be
11640 * its snapshot.
11641 * @return
11642 */
11643HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11644 AutoWriteLock &writeLock,
11645 Snapshot *pSnapshot)
11646{
11647 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11648 DeviceType_T mediumType = pAttach->i_getType();
11649
11650 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11651
11652 if (pAttach->i_isImplicit())
11653 {
11654 /* attempt to implicitly delete the implicitly created diff */
11655
11656 /// @todo move the implicit flag from MediumAttachment to Medium
11657 /// and forbid any hard disk operation when it is implicit. Or maybe
11658 /// a special media state for it to make it even more simple.
11659
11660 Assert(mMediumAttachments.isBackedUp());
11661
11662 /* will release the lock before the potentially lengthy operation, so
11663 * protect with the special state */
11664 MachineState_T oldState = mData->mMachineState;
11665 i_setMachineState(MachineState_SettingUp);
11666
11667 writeLock.release();
11668
11669 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11670 true /*aWait*/,
11671 false /*aNotify*/);
11672
11673 writeLock.acquire();
11674
11675 i_setMachineState(oldState);
11676
11677 if (FAILED(rc)) return rc;
11678 }
11679
11680 i_setModified(IsModified_Storage);
11681 mMediumAttachments.backup();
11682 mMediumAttachments->remove(pAttach);
11683
11684 if (!oldmedium.isNull())
11685 {
11686 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11687 if (pSnapshot)
11688 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11689 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11690 else if (mediumType != DeviceType_HardDisk)
11691 oldmedium->i_removeBackReference(mData->mUuid);
11692 }
11693
11694 return S_OK;
11695}
11696
11697/**
11698 * Goes thru all media of the given list and
11699 *
11700 * 1) calls i_detachDevice() on each of them for this machine and
11701 * 2) adds all Medium objects found in the process to the given list,
11702 * depending on cleanupMode.
11703 *
11704 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11705 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11706 * media to the list.
11707 *
11708 * This gets called from Machine::Unregister, both for the actual Machine and
11709 * the SnapshotMachine objects that might be found in the snapshots.
11710 *
11711 * Requires caller and locking. The machine lock must be passed in because it
11712 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11713 *
11714 * @param writeLock Machine lock from top-level caller; this gets passed to
11715 * i_detachDevice.
11716 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11717 * object if called for a SnapshotMachine.
11718 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11719 * added to llMedia; if Full, then all media get added;
11720 * otherwise no media get added.
11721 * @param llMedia Caller's list to receive Medium objects which got detached so
11722 * caller can close() them, depending on cleanupMode.
11723 * @return
11724 */
11725HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11726 Snapshot *pSnapshot,
11727 CleanupMode_T cleanupMode,
11728 MediaList &llMedia)
11729{
11730 Assert(isWriteLockOnCurrentThread());
11731
11732 HRESULT rc;
11733
11734 // make a temporary list because i_detachDevice invalidates iterators into
11735 // mMediumAttachments
11736 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11737
11738 for (MediumAttachmentList::iterator
11739 it = llAttachments2.begin();
11740 it != llAttachments2.end();
11741 ++it)
11742 {
11743 ComObjPtr<MediumAttachment> &pAttach = *it;
11744 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11745
11746 if (!pMedium.isNull())
11747 {
11748 AutoCaller mac(pMedium);
11749 if (FAILED(mac.rc())) return mac.rc();
11750 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11751 DeviceType_T devType = pMedium->i_getDeviceType();
11752 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11753 && devType == DeviceType_HardDisk)
11754 || (cleanupMode == CleanupMode_Full)
11755 )
11756 {
11757 llMedia.push_back(pMedium);
11758 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11759 /* Not allowed to keep this lock as below we need the parent
11760 * medium lock, and the lock order is parent to child. */
11761 lock.release();
11762 /*
11763 * Search for medias which are not attached to any machine, but
11764 * in the chain to an attached disk. Mediums are only consided
11765 * if they are:
11766 * - have only one child
11767 * - no references to any machines
11768 * - are of normal medium type
11769 */
11770 while (!pParent.isNull())
11771 {
11772 AutoCaller mac1(pParent);
11773 if (FAILED(mac1.rc())) return mac1.rc();
11774 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11775 if (pParent->i_getChildren().size() == 1)
11776 {
11777 if ( pParent->i_getMachineBackRefCount() == 0
11778 && pParent->i_getType() == MediumType_Normal
11779 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11780 llMedia.push_back(pParent);
11781 }
11782 else
11783 break;
11784 pParent = pParent->i_getParent();
11785 }
11786 }
11787 }
11788
11789 // real machine: then we need to use the proper method
11790 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11791
11792 if (FAILED(rc))
11793 return rc;
11794 }
11795
11796 return S_OK;
11797}
11798
11799/**
11800 * Perform deferred hard disk detachments.
11801 *
11802 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11803 * changed (not backed up).
11804 *
11805 * If @a aOnline is @c true then this method will also unlock the old hard
11806 * disks for which the new implicit diffs were created and will lock these new
11807 * diffs for writing.
11808 *
11809 * @param aOnline Whether the VM was online prior to this operation.
11810 *
11811 * @note Locks this object for writing!
11812 */
11813void Machine::i_commitMedia(bool aOnline /*= false*/)
11814{
11815 AutoCaller autoCaller(this);
11816 AssertComRCReturnVoid(autoCaller.rc());
11817
11818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11819
11820 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11821
11822 HRESULT rc = S_OK;
11823
11824 /* no attach/detach operations -- nothing to do */
11825 if (!mMediumAttachments.isBackedUp())
11826 return;
11827
11828 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11829 bool fMediaNeedsLocking = false;
11830
11831 /* enumerate new attachments */
11832 for (MediumAttachmentList::const_iterator
11833 it = mMediumAttachments->begin();
11834 it != mMediumAttachments->end();
11835 ++it)
11836 {
11837 MediumAttachment *pAttach = *it;
11838
11839 pAttach->i_commit();
11840
11841 Medium *pMedium = pAttach->i_getMedium();
11842 bool fImplicit = pAttach->i_isImplicit();
11843
11844 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11845 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11846 fImplicit));
11847
11848 /** @todo convert all this Machine-based voodoo to MediumAttachment
11849 * based commit logic. */
11850 if (fImplicit)
11851 {
11852 /* convert implicit attachment to normal */
11853 pAttach->i_setImplicit(false);
11854
11855 if ( aOnline
11856 && pMedium
11857 && pAttach->i_getType() == DeviceType_HardDisk
11858 )
11859 {
11860 /* update the appropriate lock list */
11861 MediumLockList *pMediumLockList;
11862 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11863 AssertComRC(rc);
11864 if (pMediumLockList)
11865 {
11866 /* unlock if there's a need to change the locking */
11867 if (!fMediaNeedsLocking)
11868 {
11869 rc = mData->mSession.mLockedMedia.Unlock();
11870 AssertComRC(rc);
11871 fMediaNeedsLocking = true;
11872 }
11873 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11874 AssertComRC(rc);
11875 rc = pMediumLockList->Append(pMedium, true);
11876 AssertComRC(rc);
11877 }
11878 }
11879
11880 continue;
11881 }
11882
11883 if (pMedium)
11884 {
11885 /* was this medium attached before? */
11886 for (MediumAttachmentList::iterator
11887 oldIt = oldAtts.begin();
11888 oldIt != oldAtts.end();
11889 ++oldIt)
11890 {
11891 MediumAttachment *pOldAttach = *oldIt;
11892 if (pOldAttach->i_getMedium() == pMedium)
11893 {
11894 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11895
11896 /* yes: remove from old to avoid de-association */
11897 oldAtts.erase(oldIt);
11898 break;
11899 }
11900 }
11901 }
11902 }
11903
11904 /* enumerate remaining old attachments and de-associate from the
11905 * current machine state */
11906 for (MediumAttachmentList::const_iterator
11907 it = oldAtts.begin();
11908 it != oldAtts.end();
11909 ++it)
11910 {
11911 MediumAttachment *pAttach = *it;
11912 Medium *pMedium = pAttach->i_getMedium();
11913
11914 /* Detach only hard disks, since DVD/floppy media is detached
11915 * instantly in MountMedium. */
11916 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11917 {
11918 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11919
11920 /* now de-associate from the current machine state */
11921 rc = pMedium->i_removeBackReference(mData->mUuid);
11922 AssertComRC(rc);
11923
11924 if (aOnline)
11925 {
11926 /* unlock since medium is not used anymore */
11927 MediumLockList *pMediumLockList;
11928 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11929 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11930 {
11931 /* this happens for online snapshots, there the attachment
11932 * is changing, but only to a diff image created under
11933 * the old one, so there is no separate lock list */
11934 Assert(!pMediumLockList);
11935 }
11936 else
11937 {
11938 AssertComRC(rc);
11939 if (pMediumLockList)
11940 {
11941 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11942 AssertComRC(rc);
11943 }
11944 }
11945 }
11946 }
11947 }
11948
11949 /* take media locks again so that the locking state is consistent */
11950 if (fMediaNeedsLocking)
11951 {
11952 Assert(aOnline);
11953 rc = mData->mSession.mLockedMedia.Lock();
11954 AssertComRC(rc);
11955 }
11956
11957 /* commit the hard disk changes */
11958 mMediumAttachments.commit();
11959
11960 if (i_isSessionMachine())
11961 {
11962 /*
11963 * Update the parent machine to point to the new owner.
11964 * This is necessary because the stored parent will point to the
11965 * session machine otherwise and cause crashes or errors later
11966 * when the session machine gets invalid.
11967 */
11968 /** @todo Change the MediumAttachment class to behave like any other
11969 * class in this regard by creating peer MediumAttachment
11970 * objects for session machines and share the data with the peer
11971 * machine.
11972 */
11973 for (MediumAttachmentList::const_iterator
11974 it = mMediumAttachments->begin();
11975 it != mMediumAttachments->end();
11976 ++it)
11977 (*it)->i_updateParentMachine(mPeer);
11978
11979 /* attach new data to the primary machine and reshare it */
11980 mPeer->mMediumAttachments.attach(mMediumAttachments);
11981 }
11982
11983 return;
11984}
11985
11986/**
11987 * Perform deferred deletion of implicitly created diffs.
11988 *
11989 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11990 * changed (not backed up).
11991 *
11992 * @note Locks this object for writing!
11993 */
11994void Machine::i_rollbackMedia()
11995{
11996 AutoCaller autoCaller(this);
11997 AssertComRCReturnVoid(autoCaller.rc());
11998
11999 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12000 LogFlowThisFunc(("Entering rollbackMedia\n"));
12001
12002 HRESULT rc = S_OK;
12003
12004 /* no attach/detach operations -- nothing to do */
12005 if (!mMediumAttachments.isBackedUp())
12006 return;
12007
12008 /* enumerate new attachments */
12009 for (MediumAttachmentList::const_iterator
12010 it = mMediumAttachments->begin();
12011 it != mMediumAttachments->end();
12012 ++it)
12013 {
12014 MediumAttachment *pAttach = *it;
12015 /* Fix up the backrefs for DVD/floppy media. */
12016 if (pAttach->i_getType() != DeviceType_HardDisk)
12017 {
12018 Medium *pMedium = pAttach->i_getMedium();
12019 if (pMedium)
12020 {
12021 rc = pMedium->i_removeBackReference(mData->mUuid);
12022 AssertComRC(rc);
12023 }
12024 }
12025
12026 (*it)->i_rollback();
12027
12028 pAttach = *it;
12029 /* Fix up the backrefs for DVD/floppy media. */
12030 if (pAttach->i_getType() != DeviceType_HardDisk)
12031 {
12032 Medium *pMedium = pAttach->i_getMedium();
12033 if (pMedium)
12034 {
12035 rc = pMedium->i_addBackReference(mData->mUuid);
12036 AssertComRC(rc);
12037 }
12038 }
12039 }
12040
12041 /** @todo convert all this Machine-based voodoo to MediumAttachment
12042 * based rollback logic. */
12043 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
12044
12045 return;
12046}
12047
12048/**
12049 * Returns true if the settings file is located in the directory named exactly
12050 * as the machine; this means, among other things, that the machine directory
12051 * should be auto-renamed.
12052 *
12053 * @param aSettingsDir if not NULL, the full machine settings file directory
12054 * name will be assigned there.
12055 *
12056 * @note Doesn't lock anything.
12057 * @note Not thread safe (must be called from this object's lock).
12058 */
12059bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
12060{
12061 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12062 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
12063 if (aSettingsDir)
12064 *aSettingsDir = strMachineDirName;
12065 strMachineDirName.stripPath(); // vmname
12066 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12067 strConfigFileOnly.stripPath() // vmname.vbox
12068 .stripSuffix(); // vmname
12069 /** @todo hack, make somehow use of ComposeMachineFilename */
12070 if (mUserData->s.fDirectoryIncludesUUID)
12071 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
12072
12073 AssertReturn(!strMachineDirName.isEmpty(), false);
12074 AssertReturn(!strConfigFileOnly.isEmpty(), false);
12075
12076 return strMachineDirName == strConfigFileOnly;
12077}
12078
12079/**
12080 * Discards all changes to machine settings.
12081 *
12082 * @param aNotify Whether to notify the direct session about changes or not.
12083 *
12084 * @note Locks objects for writing!
12085 */
12086void Machine::i_rollback(bool aNotify)
12087{
12088 AutoCaller autoCaller(this);
12089 AssertComRCReturn(autoCaller.rc(), (void)0);
12090
12091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12092
12093 if (!mStorageControllers.isNull())
12094 {
12095 if (mStorageControllers.isBackedUp())
12096 {
12097 /* unitialize all new devices (absent in the backed up list). */
12098 StorageControllerList *backedList = mStorageControllers.backedUpData();
12099 for (StorageControllerList::const_iterator
12100 it = mStorageControllers->begin();
12101 it != mStorageControllers->end();
12102 ++it)
12103 {
12104 if ( std::find(backedList->begin(), backedList->end(), *it)
12105 == backedList->end()
12106 )
12107 {
12108 (*it)->uninit();
12109 }
12110 }
12111
12112 /* restore the list */
12113 mStorageControllers.rollback();
12114 }
12115
12116 /* rollback any changes to devices after restoring the list */
12117 if (mData->flModifications & IsModified_Storage)
12118 {
12119 for (StorageControllerList::const_iterator
12120 it = mStorageControllers->begin();
12121 it != mStorageControllers->end();
12122 ++it)
12123 {
12124 (*it)->i_rollback();
12125 }
12126 }
12127 }
12128
12129 if (!mUSBControllers.isNull())
12130 {
12131 if (mUSBControllers.isBackedUp())
12132 {
12133 /* unitialize all new devices (absent in the backed up list). */
12134 USBControllerList *backedList = mUSBControllers.backedUpData();
12135 for (USBControllerList::const_iterator
12136 it = mUSBControllers->begin();
12137 it != mUSBControllers->end();
12138 ++it)
12139 {
12140 if ( std::find(backedList->begin(), backedList->end(), *it)
12141 == backedList->end()
12142 )
12143 {
12144 (*it)->uninit();
12145 }
12146 }
12147
12148 /* restore the list */
12149 mUSBControllers.rollback();
12150 }
12151
12152 /* rollback any changes to devices after restoring the list */
12153 if (mData->flModifications & IsModified_USB)
12154 {
12155 for (USBControllerList::const_iterator
12156 it = mUSBControllers->begin();
12157 it != mUSBControllers->end();
12158 ++it)
12159 {
12160 (*it)->i_rollback();
12161 }
12162 }
12163 }
12164
12165 mUserData.rollback();
12166
12167 mHWData.rollback();
12168
12169 if (mData->flModifications & IsModified_Storage)
12170 i_rollbackMedia();
12171
12172 if (mBIOSSettings)
12173 mBIOSSettings->i_rollback();
12174
12175 if (mTrustedPlatformModule)
12176 mTrustedPlatformModule->i_rollback();
12177
12178 if (mNvramStore)
12179 mNvramStore->i_rollback();
12180
12181 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
12182 mRecordingSettings->i_rollback();
12183
12184 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
12185 mGraphicsAdapter->i_rollback();
12186
12187 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12188 mVRDEServer->i_rollback();
12189
12190 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
12191 mAudioAdapter->i_rollback();
12192
12193 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12194 mUSBDeviceFilters->i_rollback();
12195
12196 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12197 mBandwidthControl->i_rollback();
12198
12199 if (!mHWData.isNull())
12200 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12201 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12202 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12203 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12204
12205 if (mData->flModifications & IsModified_NetworkAdapters)
12206 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12207 if ( mNetworkAdapters[slot]
12208 && mNetworkAdapters[slot]->i_isModified())
12209 {
12210 mNetworkAdapters[slot]->i_rollback();
12211 networkAdapters[slot] = mNetworkAdapters[slot];
12212 }
12213
12214 if (mData->flModifications & IsModified_SerialPorts)
12215 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12216 if ( mSerialPorts[slot]
12217 && mSerialPorts[slot]->i_isModified())
12218 {
12219 mSerialPorts[slot]->i_rollback();
12220 serialPorts[slot] = mSerialPorts[slot];
12221 }
12222
12223 if (mData->flModifications & IsModified_ParallelPorts)
12224 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12225 if ( mParallelPorts[slot]
12226 && mParallelPorts[slot]->i_isModified())
12227 {
12228 mParallelPorts[slot]->i_rollback();
12229 parallelPorts[slot] = mParallelPorts[slot];
12230 }
12231
12232 if (aNotify)
12233 {
12234 /* inform the direct session about changes */
12235
12236 ComObjPtr<Machine> that = this;
12237 uint32_t flModifications = mData->flModifications;
12238 alock.release();
12239
12240 if (flModifications & IsModified_SharedFolders)
12241 that->i_onSharedFolderChange();
12242
12243 if (flModifications & IsModified_VRDEServer)
12244 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12245 if (flModifications & IsModified_USB)
12246 that->i_onUSBControllerChange();
12247
12248 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12249 if (networkAdapters[slot])
12250 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12251 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12252 if (serialPorts[slot])
12253 that->i_onSerialPortChange(serialPorts[slot]);
12254 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12255 if (parallelPorts[slot])
12256 that->i_onParallelPortChange(parallelPorts[slot]);
12257
12258 if (flModifications & IsModified_Storage)
12259 {
12260 for (StorageControllerList::const_iterator
12261 it = mStorageControllers->begin();
12262 it != mStorageControllers->end();
12263 ++it)
12264 {
12265 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
12266 }
12267 }
12268
12269
12270#if 0
12271 if (flModifications & IsModified_BandwidthControl)
12272 that->onBandwidthControlChange();
12273#endif
12274 }
12275}
12276
12277/**
12278 * Commits all the changes to machine settings.
12279 *
12280 * Note that this operation is supposed to never fail.
12281 *
12282 * @note Locks this object and children for writing.
12283 */
12284void Machine::i_commit()
12285{
12286 AutoCaller autoCaller(this);
12287 AssertComRCReturnVoid(autoCaller.rc());
12288
12289 AutoCaller peerCaller(mPeer);
12290 AssertComRCReturnVoid(peerCaller.rc());
12291
12292 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12293
12294 /*
12295 * use safe commit to ensure Snapshot machines (that share mUserData)
12296 * will still refer to a valid memory location
12297 */
12298 mUserData.commitCopy();
12299
12300 mHWData.commit();
12301
12302 if (mMediumAttachments.isBackedUp())
12303 i_commitMedia(Global::IsOnline(mData->mMachineState));
12304
12305 mBIOSSettings->i_commit();
12306 mTrustedPlatformModule->i_commit();
12307 mNvramStore->i_commit();
12308 mRecordingSettings->i_commit();
12309 mGraphicsAdapter->i_commit();
12310 mVRDEServer->i_commit();
12311 mAudioAdapter->i_commit();
12312 mUSBDeviceFilters->i_commit();
12313 mBandwidthControl->i_commit();
12314
12315 /* Since mNetworkAdapters is a list which might have been changed (resized)
12316 * without using the Backupable<> template we need to handle the copying
12317 * of the list entries manually, including the creation of peers for the
12318 * new objects. */
12319 bool commitNetworkAdapters = false;
12320 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12321 if (mPeer)
12322 {
12323 /* commit everything, even the ones which will go away */
12324 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12325 mNetworkAdapters[slot]->i_commit();
12326 /* copy over the new entries, creating a peer and uninit the original */
12327 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12328 for (size_t slot = 0; slot < newSize; slot++)
12329 {
12330 /* look if this adapter has a peer device */
12331 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12332 if (!peer)
12333 {
12334 /* no peer means the adapter is a newly created one;
12335 * create a peer owning data this data share it with */
12336 peer.createObject();
12337 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12338 }
12339 mPeer->mNetworkAdapters[slot] = peer;
12340 }
12341 /* uninit any no longer needed network adapters */
12342 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12343 mNetworkAdapters[slot]->uninit();
12344 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12345 {
12346 if (mPeer->mNetworkAdapters[slot])
12347 mPeer->mNetworkAdapters[slot]->uninit();
12348 }
12349 /* Keep the original network adapter count until this point, so that
12350 * discarding a chipset type change will not lose settings. */
12351 mNetworkAdapters.resize(newSize);
12352 mPeer->mNetworkAdapters.resize(newSize);
12353 }
12354 else
12355 {
12356 /* we have no peer (our parent is the newly created machine);
12357 * just commit changes to the network adapters */
12358 commitNetworkAdapters = true;
12359 }
12360 if (commitNetworkAdapters)
12361 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12362 mNetworkAdapters[slot]->i_commit();
12363
12364 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12365 mSerialPorts[slot]->i_commit();
12366 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12367 mParallelPorts[slot]->i_commit();
12368
12369 bool commitStorageControllers = false;
12370
12371 if (mStorageControllers.isBackedUp())
12372 {
12373 mStorageControllers.commit();
12374
12375 if (mPeer)
12376 {
12377 /* Commit all changes to new controllers (this will reshare data with
12378 * peers for those who have peers) */
12379 StorageControllerList *newList = new StorageControllerList();
12380 for (StorageControllerList::const_iterator
12381 it = mStorageControllers->begin();
12382 it != mStorageControllers->end();
12383 ++it)
12384 {
12385 (*it)->i_commit();
12386
12387 /* look if this controller has a peer device */
12388 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12389 if (!peer)
12390 {
12391 /* no peer means the device is a newly created one;
12392 * create a peer owning data this device share it with */
12393 peer.createObject();
12394 peer->init(mPeer, *it, true /* aReshare */);
12395 }
12396 else
12397 {
12398 /* remove peer from the old list */
12399 mPeer->mStorageControllers->remove(peer);
12400 }
12401 /* and add it to the new list */
12402 newList->push_back(peer);
12403 }
12404
12405 /* uninit old peer's controllers that are left */
12406 for (StorageControllerList::const_iterator
12407 it = mPeer->mStorageControllers->begin();
12408 it != mPeer->mStorageControllers->end();
12409 ++it)
12410 {
12411 (*it)->uninit();
12412 }
12413
12414 /* attach new list of controllers to our peer */
12415 mPeer->mStorageControllers.attach(newList);
12416 }
12417 else
12418 {
12419 /* we have no peer (our parent is the newly created machine);
12420 * just commit changes to devices */
12421 commitStorageControllers = true;
12422 }
12423 }
12424 else
12425 {
12426 /* the list of controllers itself is not changed,
12427 * just commit changes to controllers themselves */
12428 commitStorageControllers = true;
12429 }
12430
12431 if (commitStorageControllers)
12432 {
12433 for (StorageControllerList::const_iterator
12434 it = mStorageControllers->begin();
12435 it != mStorageControllers->end();
12436 ++it)
12437 {
12438 (*it)->i_commit();
12439 }
12440 }
12441
12442 bool commitUSBControllers = false;
12443
12444 if (mUSBControllers.isBackedUp())
12445 {
12446 mUSBControllers.commit();
12447
12448 if (mPeer)
12449 {
12450 /* Commit all changes to new controllers (this will reshare data with
12451 * peers for those who have peers) */
12452 USBControllerList *newList = new USBControllerList();
12453 for (USBControllerList::const_iterator
12454 it = mUSBControllers->begin();
12455 it != mUSBControllers->end();
12456 ++it)
12457 {
12458 (*it)->i_commit();
12459
12460 /* look if this controller has a peer device */
12461 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12462 if (!peer)
12463 {
12464 /* no peer means the device is a newly created one;
12465 * create a peer owning data this device share it with */
12466 peer.createObject();
12467 peer->init(mPeer, *it, true /* aReshare */);
12468 }
12469 else
12470 {
12471 /* remove peer from the old list */
12472 mPeer->mUSBControllers->remove(peer);
12473 }
12474 /* and add it to the new list */
12475 newList->push_back(peer);
12476 }
12477
12478 /* uninit old peer's controllers that are left */
12479 for (USBControllerList::const_iterator
12480 it = mPeer->mUSBControllers->begin();
12481 it != mPeer->mUSBControllers->end();
12482 ++it)
12483 {
12484 (*it)->uninit();
12485 }
12486
12487 /* attach new list of controllers to our peer */
12488 mPeer->mUSBControllers.attach(newList);
12489 }
12490 else
12491 {
12492 /* we have no peer (our parent is the newly created machine);
12493 * just commit changes to devices */
12494 commitUSBControllers = true;
12495 }
12496 }
12497 else
12498 {
12499 /* the list of controllers itself is not changed,
12500 * just commit changes to controllers themselves */
12501 commitUSBControllers = true;
12502 }
12503
12504 if (commitUSBControllers)
12505 {
12506 for (USBControllerList::const_iterator
12507 it = mUSBControllers->begin();
12508 it != mUSBControllers->end();
12509 ++it)
12510 {
12511 (*it)->i_commit();
12512 }
12513 }
12514
12515 if (i_isSessionMachine())
12516 {
12517 /* attach new data to the primary machine and reshare it */
12518 mPeer->mUserData.attach(mUserData);
12519 mPeer->mHWData.attach(mHWData);
12520 /* mmMediumAttachments is reshared by fixupMedia */
12521 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12522 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12523 }
12524}
12525
12526/**
12527 * Copies all the hardware data from the given machine.
12528 *
12529 * Currently, only called when the VM is being restored from a snapshot. In
12530 * particular, this implies that the VM is not running during this method's
12531 * call.
12532 *
12533 * @note This method must be called from under this object's lock.
12534 *
12535 * @note This method doesn't call #i_commit(), so all data remains backed up and
12536 * unsaved.
12537 */
12538void Machine::i_copyFrom(Machine *aThat)
12539{
12540 AssertReturnVoid(!i_isSnapshotMachine());
12541 AssertReturnVoid(aThat->i_isSnapshotMachine());
12542
12543 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12544
12545 mHWData.assignCopy(aThat->mHWData);
12546
12547 // create copies of all shared folders (mHWData after attaching a copy
12548 // contains just references to original objects)
12549 for (HWData::SharedFolderList::iterator
12550 it = mHWData->mSharedFolders.begin();
12551 it != mHWData->mSharedFolders.end();
12552 ++it)
12553 {
12554 ComObjPtr<SharedFolder> folder;
12555 folder.createObject();
12556 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12557 AssertComRC(rc);
12558 *it = folder;
12559 }
12560
12561 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12562 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12563 mNvramStore->i_copyFrom(aThat->mNvramStore);
12564 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12565 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12566 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12567 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12568 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12569 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12570
12571 /* create private copies of all controllers */
12572 mStorageControllers.backup();
12573 mStorageControllers->clear();
12574 for (StorageControllerList::const_iterator
12575 it = aThat->mStorageControllers->begin();
12576 it != aThat->mStorageControllers->end();
12577 ++it)
12578 {
12579 ComObjPtr<StorageController> ctrl;
12580 ctrl.createObject();
12581 ctrl->initCopy(this, *it);
12582 mStorageControllers->push_back(ctrl);
12583 }
12584
12585 /* create private copies of all USB controllers */
12586 mUSBControllers.backup();
12587 mUSBControllers->clear();
12588 for (USBControllerList::const_iterator
12589 it = aThat->mUSBControllers->begin();
12590 it != aThat->mUSBControllers->end();
12591 ++it)
12592 {
12593 ComObjPtr<USBController> ctrl;
12594 ctrl.createObject();
12595 ctrl->initCopy(this, *it);
12596 mUSBControllers->push_back(ctrl);
12597 }
12598
12599 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12600 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12601 {
12602 if (mNetworkAdapters[slot].isNotNull())
12603 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12604 else
12605 {
12606 unconst(mNetworkAdapters[slot]).createObject();
12607 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12608 }
12609 }
12610 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12611 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12612 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12613 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12614}
12615
12616/**
12617 * Returns whether the given storage controller is hotplug capable.
12618 *
12619 * @returns true if the controller supports hotplugging
12620 * false otherwise.
12621 * @param enmCtrlType The controller type to check for.
12622 */
12623bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12624{
12625 ComPtr<ISystemProperties> systemProperties;
12626 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12627 if (FAILED(rc))
12628 return false;
12629
12630 BOOL aHotplugCapable = FALSE;
12631 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12632
12633 return RT_BOOL(aHotplugCapable);
12634}
12635
12636#ifdef VBOX_WITH_RESOURCE_USAGE_API
12637
12638void Machine::i_getDiskList(MediaList &list)
12639{
12640 for (MediumAttachmentList::const_iterator
12641 it = mMediumAttachments->begin();
12642 it != mMediumAttachments->end();
12643 ++it)
12644 {
12645 MediumAttachment *pAttach = *it;
12646 /* just in case */
12647 AssertContinue(pAttach);
12648
12649 AutoCaller localAutoCallerA(pAttach);
12650 if (FAILED(localAutoCallerA.rc())) continue;
12651
12652 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12653
12654 if (pAttach->i_getType() == DeviceType_HardDisk)
12655 list.push_back(pAttach->i_getMedium());
12656 }
12657}
12658
12659void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12660{
12661 AssertReturnVoid(isWriteLockOnCurrentThread());
12662 AssertPtrReturnVoid(aCollector);
12663
12664 pm::CollectorHAL *hal = aCollector->getHAL();
12665 /* Create sub metrics */
12666 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12667 "Percentage of processor time spent in user mode by the VM process.");
12668 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12669 "Percentage of processor time spent in kernel mode by the VM process.");
12670 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12671 "Size of resident portion of VM process in memory.");
12672 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12673 "Actual size of all VM disks combined.");
12674 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12675 "Network receive rate.");
12676 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12677 "Network transmit rate.");
12678 /* Create and register base metrics */
12679 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12680 cpuLoadUser, cpuLoadKernel);
12681 aCollector->registerBaseMetric(cpuLoad);
12682 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12683 ramUsageUsed);
12684 aCollector->registerBaseMetric(ramUsage);
12685 MediaList disks;
12686 i_getDiskList(disks);
12687 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12688 diskUsageUsed);
12689 aCollector->registerBaseMetric(diskUsage);
12690
12691 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12692 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12693 new pm::AggregateAvg()));
12694 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12695 new pm::AggregateMin()));
12696 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12697 new pm::AggregateMax()));
12698 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12699 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12700 new pm::AggregateAvg()));
12701 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12702 new pm::AggregateMin()));
12703 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12704 new pm::AggregateMax()));
12705
12706 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12707 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12708 new pm::AggregateAvg()));
12709 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12710 new pm::AggregateMin()));
12711 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12712 new pm::AggregateMax()));
12713
12714 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12715 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12716 new pm::AggregateAvg()));
12717 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12718 new pm::AggregateMin()));
12719 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12720 new pm::AggregateMax()));
12721
12722
12723 /* Guest metrics collector */
12724 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12725 aCollector->registerGuest(mCollectorGuest);
12726 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12727
12728 /* Create sub metrics */
12729 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12730 "Percentage of processor time spent in user mode as seen by the guest.");
12731 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12732 "Percentage of processor time spent in kernel mode as seen by the guest.");
12733 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12734 "Percentage of processor time spent idling as seen by the guest.");
12735
12736 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12737 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12738 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12739 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12740 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12741 pm::SubMetric *guestMemCache = new pm::SubMetric(
12742 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12743
12744 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12745 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12746
12747 /* Create and register base metrics */
12748 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12749 machineNetRx, machineNetTx);
12750 aCollector->registerBaseMetric(machineNetRate);
12751
12752 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12753 guestLoadUser, guestLoadKernel, guestLoadIdle);
12754 aCollector->registerBaseMetric(guestCpuLoad);
12755
12756 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12757 guestMemTotal, guestMemFree,
12758 guestMemBalloon, guestMemShared,
12759 guestMemCache, guestPagedTotal);
12760 aCollector->registerBaseMetric(guestCpuMem);
12761
12762 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12763 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12764 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12765 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12766
12767 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12768 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12769 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12770 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12771
12772 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12773 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12774 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12775 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12776
12777 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12778 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12779 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12780 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12781
12782 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12783 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12784 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12785 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12786
12787 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12788 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12789 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12790 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12791
12792 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12793 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12794 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12795 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12796
12797 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12798 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12799 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12800 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12801
12802 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12803 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12804 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12805 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12806
12807 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12808 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12809 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12810 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12811
12812 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12813 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12814 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12815 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12816}
12817
12818void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12819{
12820 AssertReturnVoid(isWriteLockOnCurrentThread());
12821
12822 if (aCollector)
12823 {
12824 aCollector->unregisterMetricsFor(aMachine);
12825 aCollector->unregisterBaseMetricsFor(aMachine);
12826 }
12827}
12828
12829#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12830
12831
12832////////////////////////////////////////////////////////////////////////////////
12833
12834DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12835
12836HRESULT SessionMachine::FinalConstruct()
12837{
12838 LogFlowThisFunc(("\n"));
12839
12840 mClientToken = NULL;
12841
12842 return BaseFinalConstruct();
12843}
12844
12845void SessionMachine::FinalRelease()
12846{
12847 LogFlowThisFunc(("\n"));
12848
12849 Assert(!mClientToken);
12850 /* paranoia, should not hang around any more */
12851 if (mClientToken)
12852 {
12853 delete mClientToken;
12854 mClientToken = NULL;
12855 }
12856
12857 uninit(Uninit::Unexpected);
12858
12859 BaseFinalRelease();
12860}
12861
12862/**
12863 * @note Must be called only by Machine::LockMachine() from its own write lock.
12864 */
12865HRESULT SessionMachine::init(Machine *aMachine)
12866{
12867 LogFlowThisFuncEnter();
12868 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12869
12870 AssertReturn(aMachine, E_INVALIDARG);
12871
12872 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12873
12874 /* Enclose the state transition NotReady->InInit->Ready */
12875 AutoInitSpan autoInitSpan(this);
12876 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12877
12878 HRESULT rc = S_OK;
12879
12880 RT_ZERO(mAuthLibCtx);
12881
12882 /* create the machine client token */
12883 try
12884 {
12885 mClientToken = new ClientToken(aMachine, this);
12886 if (!mClientToken->isReady())
12887 {
12888 delete mClientToken;
12889 mClientToken = NULL;
12890 rc = E_FAIL;
12891 }
12892 }
12893 catch (std::bad_alloc &)
12894 {
12895 rc = E_OUTOFMEMORY;
12896 }
12897 if (FAILED(rc))
12898 return rc;
12899
12900 /* memorize the peer Machine */
12901 unconst(mPeer) = aMachine;
12902 /* share the parent pointer */
12903 unconst(mParent) = aMachine->mParent;
12904
12905 /* take the pointers to data to share */
12906 mData.share(aMachine->mData);
12907 mSSData.share(aMachine->mSSData);
12908
12909 mUserData.share(aMachine->mUserData);
12910 mHWData.share(aMachine->mHWData);
12911 mMediumAttachments.share(aMachine->mMediumAttachments);
12912
12913 mStorageControllers.allocate();
12914 for (StorageControllerList::const_iterator
12915 it = aMachine->mStorageControllers->begin();
12916 it != aMachine->mStorageControllers->end();
12917 ++it)
12918 {
12919 ComObjPtr<StorageController> ctl;
12920 ctl.createObject();
12921 ctl->init(this, *it);
12922 mStorageControllers->push_back(ctl);
12923 }
12924
12925 mUSBControllers.allocate();
12926 for (USBControllerList::const_iterator
12927 it = aMachine->mUSBControllers->begin();
12928 it != aMachine->mUSBControllers->end();
12929 ++it)
12930 {
12931 ComObjPtr<USBController> ctl;
12932 ctl.createObject();
12933 ctl->init(this, *it);
12934 mUSBControllers->push_back(ctl);
12935 }
12936
12937 unconst(mBIOSSettings).createObject();
12938 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12939
12940 unconst(mTrustedPlatformModule).createObject();
12941 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
12942
12943 unconst(mNvramStore).createObject();
12944 mNvramStore->init(this, aMachine->mNvramStore);
12945
12946 unconst(mRecordingSettings).createObject();
12947 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12948 /* create another GraphicsAdapter object that will be mutable */
12949 unconst(mGraphicsAdapter).createObject();
12950 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12951 /* create another VRDEServer object that will be mutable */
12952 unconst(mVRDEServer).createObject();
12953 mVRDEServer->init(this, aMachine->mVRDEServer);
12954 /* create another audio adapter object that will be mutable */
12955 unconst(mAudioAdapter).createObject();
12956 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12957 /* create a list of serial ports that will be mutable */
12958 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12959 {
12960 unconst(mSerialPorts[slot]).createObject();
12961 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12962 }
12963 /* create a list of parallel ports that will be mutable */
12964 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12965 {
12966 unconst(mParallelPorts[slot]).createObject();
12967 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12968 }
12969
12970 /* create another USB device filters object that will be mutable */
12971 unconst(mUSBDeviceFilters).createObject();
12972 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12973
12974 /* create a list of network adapters that will be mutable */
12975 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12976 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12977 {
12978 unconst(mNetworkAdapters[slot]).createObject();
12979 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12980 }
12981
12982 /* create another bandwidth control object that will be mutable */
12983 unconst(mBandwidthControl).createObject();
12984 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12985
12986 /* default is to delete saved state on Saved -> PoweredOff transition */
12987 mRemoveSavedState = true;
12988
12989 /* Confirm a successful initialization when it's the case */
12990 autoInitSpan.setSucceeded();
12991
12992 miNATNetworksStarted = 0;
12993
12994 LogFlowThisFuncLeave();
12995 return rc;
12996}
12997
12998/**
12999 * Uninitializes this session object. If the reason is other than
13000 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
13001 * or the client watcher code.
13002 *
13003 * @param aReason uninitialization reason
13004 *
13005 * @note Locks mParent + this object for writing.
13006 */
13007void SessionMachine::uninit(Uninit::Reason aReason)
13008{
13009 LogFlowThisFuncEnter();
13010 LogFlowThisFunc(("reason=%d\n", aReason));
13011
13012 /*
13013 * Strongly reference ourselves to prevent this object deletion after
13014 * mData->mSession.mMachine.setNull() below (which can release the last
13015 * reference and call the destructor). Important: this must be done before
13016 * accessing any members (and before AutoUninitSpan that does it as well).
13017 * This self reference will be released as the very last step on return.
13018 */
13019 ComObjPtr<SessionMachine> selfRef;
13020 if (aReason != Uninit::Unexpected)
13021 selfRef = this;
13022
13023 /* Enclose the state transition Ready->InUninit->NotReady */
13024 AutoUninitSpan autoUninitSpan(this);
13025 if (autoUninitSpan.uninitDone())
13026 {
13027 LogFlowThisFunc(("Already uninitialized\n"));
13028 LogFlowThisFuncLeave();
13029 return;
13030 }
13031
13032 if (autoUninitSpan.initFailed())
13033 {
13034 /* We've been called by init() because it's failed. It's not really
13035 * necessary (nor it's safe) to perform the regular uninit sequence
13036 * below, the following is enough.
13037 */
13038 LogFlowThisFunc(("Initialization failed.\n"));
13039 /* destroy the machine client token */
13040 if (mClientToken)
13041 {
13042 delete mClientToken;
13043 mClientToken = NULL;
13044 }
13045 uninitDataAndChildObjects();
13046 mData.free();
13047 unconst(mParent) = NULL;
13048 unconst(mPeer) = NULL;
13049 LogFlowThisFuncLeave();
13050 return;
13051 }
13052
13053 MachineState_T lastState;
13054 {
13055 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
13056 lastState = mData->mMachineState;
13057 }
13058 NOREF(lastState);
13059
13060#ifdef VBOX_WITH_USB
13061 // release all captured USB devices, but do this before requesting the locks below
13062 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
13063 {
13064 /* Console::captureUSBDevices() is called in the VM process only after
13065 * setting the machine state to Starting or Restoring.
13066 * Console::detachAllUSBDevices() will be called upon successful
13067 * termination. So, we need to release USB devices only if there was
13068 * an abnormal termination of a running VM.
13069 *
13070 * This is identical to SessionMachine::DetachAllUSBDevices except
13071 * for the aAbnormal argument. */
13072 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13073 AssertComRC(rc);
13074 NOREF(rc);
13075
13076 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13077 if (service)
13078 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
13079 }
13080#endif /* VBOX_WITH_USB */
13081
13082 // we need to lock this object in uninit() because the lock is shared
13083 // with mPeer (as well as data we modify below). mParent lock is needed
13084 // by several calls to it.
13085 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
13086
13087#ifdef VBOX_WITH_RESOURCE_USAGE_API
13088 /*
13089 * It is safe to call Machine::i_unregisterMetrics() here because
13090 * PerformanceCollector::samplerCallback no longer accesses guest methods
13091 * holding the lock.
13092 */
13093 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
13094 /* The guest must be unregistered after its metrics (@bugref{5949}). */
13095 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
13096 if (mCollectorGuest)
13097 {
13098 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
13099 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
13100 mCollectorGuest = NULL;
13101 }
13102#endif
13103
13104 if (aReason == Uninit::Abnormal)
13105 {
13106 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
13107
13108 /*
13109 * Move the VM to the 'Aborted' machine state unless we are restoring a
13110 * VM that was in the 'Saved' machine state. In that case, if the VM
13111 * fails before reaching either the 'Restoring' machine state or the
13112 * 'Running' machine state then we set the machine state to
13113 * 'AbortedSaved' in order to preserve the saved state file so that the
13114 * VM can be restored in the future.
13115 */
13116 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
13117 i_setMachineState(MachineState_AbortedSaved);
13118 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
13119 i_setMachineState(MachineState_Aborted);
13120 }
13121
13122 // any machine settings modified?
13123 if (mData->flModifications)
13124 {
13125 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
13126 i_rollback(false /* aNotify */);
13127 }
13128
13129 mData->mSession.mPID = NIL_RTPROCESS;
13130
13131 if (aReason == Uninit::Unexpected)
13132 {
13133 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
13134 * client watcher thread to update the set of machines that have open
13135 * sessions. */
13136 mParent->i_updateClientWatcher();
13137 }
13138
13139 /* uninitialize all remote controls */
13140 if (mData->mSession.mRemoteControls.size())
13141 {
13142 LogFlowThisFunc(("Closing remote sessions (%d):\n",
13143 mData->mSession.mRemoteControls.size()));
13144
13145 /* Always restart a the beginning, since the iterator is invalidated
13146 * by using erase(). */
13147 for (Data::Session::RemoteControlList::iterator
13148 it = mData->mSession.mRemoteControls.begin();
13149 it != mData->mSession.mRemoteControls.end();
13150 it = mData->mSession.mRemoteControls.begin())
13151 {
13152 ComPtr<IInternalSessionControl> pControl = *it;
13153 mData->mSession.mRemoteControls.erase(it);
13154 multilock.release();
13155 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
13156 HRESULT rc = pControl->Uninitialize();
13157 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
13158 if (FAILED(rc))
13159 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
13160 multilock.acquire();
13161 }
13162 mData->mSession.mRemoteControls.clear();
13163 }
13164
13165 /* Remove all references to the NAT network service. The service will stop
13166 * if all references (also from other VMs) are removed. */
13167 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
13168 {
13169 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13170 {
13171 BOOL enabled;
13172 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13173 if ( FAILED(hrc)
13174 || !enabled)
13175 continue;
13176
13177 NetworkAttachmentType_T type;
13178 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13179 if ( SUCCEEDED(hrc)
13180 && type == NetworkAttachmentType_NATNetwork)
13181 {
13182 Bstr name;
13183 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13184 if (SUCCEEDED(hrc))
13185 {
13186 multilock.release();
13187 Utf8Str strName(name);
13188 LogRel(("VM '%s' stops using NAT network '%s'\n",
13189 mUserData->s.strName.c_str(), strName.c_str()));
13190 mParent->i_natNetworkRefDec(strName);
13191 multilock.acquire();
13192 }
13193 }
13194 }
13195 }
13196
13197 /*
13198 * An expected uninitialization can come only from #i_checkForDeath().
13199 * Otherwise it means that something's gone really wrong (for example,
13200 * the Session implementation has released the VirtualBox reference
13201 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
13202 * etc). However, it's also possible, that the client releases the IPC
13203 * semaphore correctly (i.e. before it releases the VirtualBox reference),
13204 * but the VirtualBox release event comes first to the server process.
13205 * This case is practically possible, so we should not assert on an
13206 * unexpected uninit, just log a warning.
13207 */
13208
13209 if (aReason == Uninit::Unexpected)
13210 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
13211
13212 if (aReason != Uninit::Normal)
13213 {
13214 mData->mSession.mDirectControl.setNull();
13215 }
13216 else
13217 {
13218 /* this must be null here (see #OnSessionEnd()) */
13219 Assert(mData->mSession.mDirectControl.isNull());
13220 Assert(mData->mSession.mState == SessionState_Unlocking);
13221 Assert(!mData->mSession.mProgress.isNull());
13222 }
13223 if (mData->mSession.mProgress)
13224 {
13225 if (aReason == Uninit::Normal)
13226 mData->mSession.mProgress->i_notifyComplete(S_OK);
13227 else
13228 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
13229 COM_IIDOF(ISession),
13230 getComponentName(),
13231 tr("The VM session was aborted"));
13232 mData->mSession.mProgress.setNull();
13233 }
13234
13235 if (mConsoleTaskData.mProgress)
13236 {
13237 Assert(aReason == Uninit::Abnormal);
13238 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
13239 COM_IIDOF(ISession),
13240 getComponentName(),
13241 tr("The VM session was aborted"));
13242 mConsoleTaskData.mProgress.setNull();
13243 }
13244
13245 /* remove the association between the peer machine and this session machine */
13246 Assert( (SessionMachine*)mData->mSession.mMachine == this
13247 || aReason == Uninit::Unexpected);
13248
13249 /* reset the rest of session data */
13250 mData->mSession.mLockType = LockType_Null;
13251 mData->mSession.mMachine.setNull();
13252 mData->mSession.mState = SessionState_Unlocked;
13253 mData->mSession.mName.setNull();
13254
13255 /* destroy the machine client token before leaving the exclusive lock */
13256 if (mClientToken)
13257 {
13258 delete mClientToken;
13259 mClientToken = NULL;
13260 }
13261
13262 /* fire an event */
13263 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
13264
13265 uninitDataAndChildObjects();
13266
13267 /* free the essential data structure last */
13268 mData.free();
13269
13270 /* release the exclusive lock before setting the below two to NULL */
13271 multilock.release();
13272
13273 unconst(mParent) = NULL;
13274 unconst(mPeer) = NULL;
13275
13276 AuthLibUnload(&mAuthLibCtx);
13277
13278 LogFlowThisFuncLeave();
13279}
13280
13281// util::Lockable interface
13282////////////////////////////////////////////////////////////////////////////////
13283
13284/**
13285 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13286 * with the primary Machine instance (mPeer).
13287 */
13288RWLockHandle *SessionMachine::lockHandle() const
13289{
13290 AssertReturn(mPeer != NULL, NULL);
13291 return mPeer->lockHandle();
13292}
13293
13294// IInternalMachineControl methods
13295////////////////////////////////////////////////////////////////////////////////
13296
13297/**
13298 * Passes collected guest statistics to performance collector object
13299 */
13300HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13301 ULONG aCpuKernel, ULONG aCpuIdle,
13302 ULONG aMemTotal, ULONG aMemFree,
13303 ULONG aMemBalloon, ULONG aMemShared,
13304 ULONG aMemCache, ULONG aPageTotal,
13305 ULONG aAllocVMM, ULONG aFreeVMM,
13306 ULONG aBalloonedVMM, ULONG aSharedVMM,
13307 ULONG aVmNetRx, ULONG aVmNetTx)
13308{
13309#ifdef VBOX_WITH_RESOURCE_USAGE_API
13310 if (mCollectorGuest)
13311 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13312 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13313 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13314 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13315
13316 return S_OK;
13317#else
13318 NOREF(aValidStats);
13319 NOREF(aCpuUser);
13320 NOREF(aCpuKernel);
13321 NOREF(aCpuIdle);
13322 NOREF(aMemTotal);
13323 NOREF(aMemFree);
13324 NOREF(aMemBalloon);
13325 NOREF(aMemShared);
13326 NOREF(aMemCache);
13327 NOREF(aPageTotal);
13328 NOREF(aAllocVMM);
13329 NOREF(aFreeVMM);
13330 NOREF(aBalloonedVMM);
13331 NOREF(aSharedVMM);
13332 NOREF(aVmNetRx);
13333 NOREF(aVmNetTx);
13334 return E_NOTIMPL;
13335#endif
13336}
13337
13338////////////////////////////////////////////////////////////////////////////////
13339//
13340// SessionMachine task records
13341//
13342////////////////////////////////////////////////////////////////////////////////
13343
13344/**
13345 * Task record for saving the machine state.
13346 */
13347class SessionMachine::SaveStateTask
13348 : public Machine::Task
13349{
13350public:
13351 SaveStateTask(SessionMachine *m,
13352 Progress *p,
13353 const Utf8Str &t,
13354 Reason_T enmReason,
13355 const Utf8Str &strStateFilePath)
13356 : Task(m, p, t),
13357 m_enmReason(enmReason),
13358 m_strStateFilePath(strStateFilePath)
13359 {}
13360
13361private:
13362 void handler()
13363 {
13364 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13365 }
13366
13367 Reason_T m_enmReason;
13368 Utf8Str m_strStateFilePath;
13369
13370 friend class SessionMachine;
13371};
13372
13373/**
13374 * Task thread implementation for SessionMachine::SaveState(), called from
13375 * SessionMachine::taskHandler().
13376 *
13377 * @note Locks this object for writing.
13378 *
13379 * @param task
13380 * @return
13381 */
13382void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13383{
13384 LogFlowThisFuncEnter();
13385
13386 AutoCaller autoCaller(this);
13387 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13388 if (FAILED(autoCaller.rc()))
13389 {
13390 /* we might have been uninitialized because the session was accidentally
13391 * closed by the client, so don't assert */
13392 HRESULT rc = setError(E_FAIL,
13393 tr("The session has been accidentally closed"));
13394 task.m_pProgress->i_notifyComplete(rc);
13395 LogFlowThisFuncLeave();
13396 return;
13397 }
13398
13399 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13400
13401 HRESULT rc = S_OK;
13402
13403 try
13404 {
13405 ComPtr<IInternalSessionControl> directControl;
13406 if (mData->mSession.mLockType == LockType_VM)
13407 directControl = mData->mSession.mDirectControl;
13408 if (directControl.isNull())
13409 throw setError(VBOX_E_INVALID_VM_STATE,
13410 tr("Trying to save state without a running VM"));
13411 alock.release();
13412 BOOL fSuspendedBySave;
13413 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13414 Assert(!fSuspendedBySave);
13415 alock.acquire();
13416
13417 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13418 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13419 throw E_FAIL);
13420
13421 if (SUCCEEDED(rc))
13422 {
13423 mSSData->strStateFilePath = task.m_strStateFilePath;
13424
13425 /* save all VM settings */
13426 rc = i_saveSettings(NULL, alock);
13427 // no need to check whether VirtualBox.xml needs saving also since
13428 // we can't have a name change pending at this point
13429 }
13430 else
13431 {
13432 // On failure, set the state to the state we had at the beginning.
13433 i_setMachineState(task.m_machineStateBackup);
13434 i_updateMachineStateOnClient();
13435
13436 // Delete the saved state file (might have been already created).
13437 // No need to check whether this is shared with a snapshot here
13438 // because we certainly created a fresh saved state file here.
13439 RTFileDelete(task.m_strStateFilePath.c_str());
13440 }
13441 }
13442 catch (HRESULT aRC) { rc = aRC; }
13443
13444 task.m_pProgress->i_notifyComplete(rc);
13445
13446 LogFlowThisFuncLeave();
13447}
13448
13449/**
13450 * @note Locks this object for writing.
13451 */
13452HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13453{
13454 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13455}
13456
13457HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13458{
13459 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13460
13461 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13462 if (FAILED(rc)) return rc;
13463
13464 if ( mData->mMachineState != MachineState_Running
13465 && mData->mMachineState != MachineState_Paused
13466 )
13467 return setError(VBOX_E_INVALID_VM_STATE,
13468 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13469 Global::stringifyMachineState(mData->mMachineState));
13470
13471 ComObjPtr<Progress> pProgress;
13472 pProgress.createObject();
13473 rc = pProgress->init(i_getVirtualBox(),
13474 static_cast<IMachine *>(this) /* aInitiator */,
13475 tr("Saving the execution state of the virtual machine"),
13476 FALSE /* aCancelable */);
13477 if (FAILED(rc))
13478 return rc;
13479
13480 Utf8Str strStateFilePath;
13481 i_composeSavedStateFilename(strStateFilePath);
13482
13483 /* create and start the task on a separate thread (note that it will not
13484 * start working until we release alock) */
13485 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13486 rc = pTask->createThread();
13487 if (FAILED(rc))
13488 return rc;
13489
13490 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13491 i_setMachineState(MachineState_Saving);
13492 i_updateMachineStateOnClient();
13493
13494 pProgress.queryInterfaceTo(aProgress.asOutParam());
13495
13496 return S_OK;
13497}
13498
13499/**
13500 * @note Locks this object for writing.
13501 */
13502HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13503{
13504 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13505
13506 HRESULT rc = i_checkStateDependency(MutableStateDep);
13507 if (FAILED(rc)) return rc;
13508
13509 if ( mData->mMachineState != MachineState_PoweredOff
13510 && mData->mMachineState != MachineState_Teleported
13511 && mData->mMachineState != MachineState_Aborted
13512 )
13513 return setError(VBOX_E_INVALID_VM_STATE,
13514 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13515 Global::stringifyMachineState(mData->mMachineState));
13516
13517 com::Utf8Str stateFilePathFull;
13518 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13519 if (RT_FAILURE(vrc))
13520 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13521 tr("Invalid saved state file path '%s' (%Rrc)"),
13522 aSavedStateFile.c_str(),
13523 vrc);
13524
13525 mSSData->strStateFilePath = stateFilePathFull;
13526
13527 /* The below i_setMachineState() will detect the state transition and will
13528 * update the settings file */
13529
13530 return i_setMachineState(MachineState_Saved);
13531}
13532
13533/**
13534 * @note Locks this object for writing.
13535 */
13536HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13537{
13538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13539
13540 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13541 if (FAILED(rc)) return rc;
13542
13543 if ( mData->mMachineState != MachineState_Saved
13544 && mData->mMachineState != MachineState_AbortedSaved)
13545 return setError(VBOX_E_INVALID_VM_STATE,
13546 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13547 Global::stringifyMachineState(mData->mMachineState));
13548
13549 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13550
13551 /*
13552 * Saved -> PoweredOff transition will be detected in the SessionMachine
13553 * and properly handled.
13554 */
13555 rc = i_setMachineState(MachineState_PoweredOff);
13556 return rc;
13557}
13558
13559
13560/**
13561 * @note Locks the same as #i_setMachineState() does.
13562 */
13563HRESULT SessionMachine::updateState(MachineState_T aState)
13564{
13565 return i_setMachineState(aState);
13566}
13567
13568/**
13569 * @note Locks this object for writing.
13570 */
13571HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13572{
13573 IProgress *pProgress(aProgress);
13574
13575 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13576
13577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13578
13579 if (mData->mSession.mState != SessionState_Locked)
13580 return VBOX_E_INVALID_OBJECT_STATE;
13581
13582 if (!mData->mSession.mProgress.isNull())
13583 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13584
13585 /* If we didn't reference the NAT network service yet, add a reference to
13586 * force a start */
13587 if (miNATNetworksStarted < 1)
13588 {
13589 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13590 {
13591 BOOL enabled;
13592 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13593 if ( FAILED(hrc)
13594 || !enabled)
13595 continue;
13596
13597 NetworkAttachmentType_T type;
13598 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13599 if ( SUCCEEDED(hrc)
13600 && type == NetworkAttachmentType_NATNetwork)
13601 {
13602 Bstr name;
13603 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13604 if (SUCCEEDED(hrc))
13605 {
13606 Utf8Str strName(name);
13607 LogRel(("VM '%s' starts using NAT network '%s'\n",
13608 mUserData->s.strName.c_str(), strName.c_str()));
13609 mPeer->lockHandle()->unlockWrite();
13610 mParent->i_natNetworkRefInc(strName);
13611#ifdef RT_LOCK_STRICT
13612 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13613#else
13614 mPeer->lockHandle()->lockWrite();
13615#endif
13616 }
13617 }
13618 }
13619 miNATNetworksStarted++;
13620 }
13621
13622 LogFlowThisFunc(("returns S_OK.\n"));
13623 return S_OK;
13624}
13625
13626/**
13627 * @note Locks this object for writing.
13628 */
13629HRESULT SessionMachine::endPowerUp(LONG aResult)
13630{
13631 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13632
13633 if (mData->mSession.mState != SessionState_Locked)
13634 return VBOX_E_INVALID_OBJECT_STATE;
13635
13636 /* Finalize the LaunchVMProcess progress object. */
13637 if (mData->mSession.mProgress)
13638 {
13639 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13640 mData->mSession.mProgress.setNull();
13641 }
13642
13643 if (SUCCEEDED((HRESULT)aResult))
13644 {
13645#ifdef VBOX_WITH_RESOURCE_USAGE_API
13646 /* The VM has been powered up successfully, so it makes sense
13647 * now to offer the performance metrics for a running machine
13648 * object. Doing it earlier wouldn't be safe. */
13649 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13650 mData->mSession.mPID);
13651#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13652 }
13653
13654 return S_OK;
13655}
13656
13657/**
13658 * @note Locks this object for writing.
13659 */
13660HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13661{
13662 LogFlowThisFuncEnter();
13663
13664 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13665
13666 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13667 E_FAIL);
13668
13669 /* create a progress object to track operation completion */
13670 ComObjPtr<Progress> pProgress;
13671 pProgress.createObject();
13672 pProgress->init(i_getVirtualBox(),
13673 static_cast<IMachine *>(this) /* aInitiator */,
13674 tr("Stopping the virtual machine"),
13675 FALSE /* aCancelable */);
13676
13677 /* fill in the console task data */
13678 mConsoleTaskData.mLastState = mData->mMachineState;
13679 mConsoleTaskData.mProgress = pProgress;
13680
13681 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13682 i_setMachineState(MachineState_Stopping);
13683
13684 pProgress.queryInterfaceTo(aProgress.asOutParam());
13685
13686 return S_OK;
13687}
13688
13689/**
13690 * @note Locks this object for writing.
13691 */
13692HRESULT SessionMachine::endPoweringDown(LONG aResult,
13693 const com::Utf8Str &aErrMsg)
13694{
13695 HRESULT const hrcResult = (HRESULT)aResult;
13696 LogFlowThisFuncEnter();
13697
13698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13699
13700 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13701 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13702 && mConsoleTaskData.mLastState != MachineState_Null,
13703 E_FAIL);
13704
13705 /*
13706 * On failure, set the state to the state we had when BeginPoweringDown()
13707 * was called (this is expected by Console::PowerDown() and the associated
13708 * task). On success the VM process already changed the state to
13709 * MachineState_PoweredOff, so no need to do anything.
13710 */
13711 if (FAILED(hrcResult))
13712 i_setMachineState(mConsoleTaskData.mLastState);
13713
13714 /* notify the progress object about operation completion */
13715 Assert(mConsoleTaskData.mProgress);
13716 if (SUCCEEDED(hrcResult))
13717 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13718 else
13719 {
13720 if (aErrMsg.length())
13721 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13722 COM_IIDOF(ISession),
13723 getComponentName(),
13724 aErrMsg.c_str());
13725 else
13726 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13727 }
13728
13729 /* clear out the temporary saved state data */
13730 mConsoleTaskData.mLastState = MachineState_Null;
13731 mConsoleTaskData.mProgress.setNull();
13732
13733 LogFlowThisFuncLeave();
13734 return S_OK;
13735}
13736
13737
13738/**
13739 * Goes through the USB filters of the given machine to see if the given
13740 * device matches any filter or not.
13741 *
13742 * @note Locks the same as USBController::hasMatchingFilter() does.
13743 */
13744HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13745 BOOL *aMatched,
13746 ULONG *aMaskedInterfaces)
13747{
13748 LogFlowThisFunc(("\n"));
13749
13750#ifdef VBOX_WITH_USB
13751 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13752#else
13753 NOREF(aDevice);
13754 NOREF(aMaskedInterfaces);
13755 *aMatched = FALSE;
13756#endif
13757
13758 return S_OK;
13759}
13760
13761/**
13762 * @note Locks the same as Host::captureUSBDevice() does.
13763 */
13764HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13765{
13766 LogFlowThisFunc(("\n"));
13767
13768#ifdef VBOX_WITH_USB
13769 /* if captureDeviceForVM() fails, it must have set extended error info */
13770 clearError();
13771 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13772 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13773 return rc;
13774
13775 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13776 AssertReturn(service, E_FAIL);
13777 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13778#else
13779 RT_NOREF(aId, aCaptureFilename);
13780 return E_NOTIMPL;
13781#endif
13782}
13783
13784/**
13785 * @note Locks the same as Host::detachUSBDevice() does.
13786 */
13787HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13788 BOOL aDone)
13789{
13790 LogFlowThisFunc(("\n"));
13791
13792#ifdef VBOX_WITH_USB
13793 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13794 AssertReturn(service, E_FAIL);
13795 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13796#else
13797 NOREF(aId);
13798 NOREF(aDone);
13799 return E_NOTIMPL;
13800#endif
13801}
13802
13803/**
13804 * Inserts all machine filters to the USB proxy service and then calls
13805 * Host::autoCaptureUSBDevices().
13806 *
13807 * Called by Console from the VM process upon VM startup.
13808 *
13809 * @note Locks what called methods lock.
13810 */
13811HRESULT SessionMachine::autoCaptureUSBDevices()
13812{
13813 LogFlowThisFunc(("\n"));
13814
13815#ifdef VBOX_WITH_USB
13816 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13817 AssertComRC(rc);
13818 NOREF(rc);
13819
13820 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13821 AssertReturn(service, E_FAIL);
13822 return service->autoCaptureDevicesForVM(this);
13823#else
13824 return S_OK;
13825#endif
13826}
13827
13828/**
13829 * Removes all machine filters from the USB proxy service and then calls
13830 * Host::detachAllUSBDevices().
13831 *
13832 * Called by Console from the VM process upon normal VM termination or by
13833 * SessionMachine::uninit() upon abnormal VM termination (from under the
13834 * Machine/SessionMachine lock).
13835 *
13836 * @note Locks what called methods lock.
13837 */
13838HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13839{
13840 LogFlowThisFunc(("\n"));
13841
13842#ifdef VBOX_WITH_USB
13843 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13844 AssertComRC(rc);
13845 NOREF(rc);
13846
13847 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13848 AssertReturn(service, E_FAIL);
13849 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13850#else
13851 NOREF(aDone);
13852 return S_OK;
13853#endif
13854}
13855
13856/**
13857 * @note Locks this object for writing.
13858 */
13859HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13860 ComPtr<IProgress> &aProgress)
13861{
13862 LogFlowThisFuncEnter();
13863
13864 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13865 /*
13866 * We don't assert below because it might happen that a non-direct session
13867 * informs us it is closed right after we've been uninitialized -- it's ok.
13868 */
13869
13870 /* get IInternalSessionControl interface */
13871 ComPtr<IInternalSessionControl> control(aSession);
13872
13873 ComAssertRet(!control.isNull(), E_INVALIDARG);
13874
13875 /* Creating a Progress object requires the VirtualBox lock, and
13876 * thus locking it here is required by the lock order rules. */
13877 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13878
13879 if (control == mData->mSession.mDirectControl)
13880 {
13881 /* The direct session is being normally closed by the client process
13882 * ----------------------------------------------------------------- */
13883
13884 /* go to the closing state (essential for all open*Session() calls and
13885 * for #i_checkForDeath()) */
13886 Assert(mData->mSession.mState == SessionState_Locked);
13887 mData->mSession.mState = SessionState_Unlocking;
13888
13889 /* set direct control to NULL to release the remote instance */
13890 mData->mSession.mDirectControl.setNull();
13891 LogFlowThisFunc(("Direct control is set to NULL\n"));
13892
13893 if (mData->mSession.mProgress)
13894 {
13895 /* finalize the progress, someone might wait if a frontend
13896 * closes the session before powering on the VM. */
13897 mData->mSession.mProgress->notifyComplete(E_FAIL,
13898 COM_IIDOF(ISession),
13899 getComponentName(),
13900 tr("The VM session was closed before any attempt to power it on"));
13901 mData->mSession.mProgress.setNull();
13902 }
13903
13904 /* Create the progress object the client will use to wait until
13905 * #i_checkForDeath() is called to uninitialize this session object after
13906 * it releases the IPC semaphore.
13907 * Note! Because we're "reusing" mProgress here, this must be a proxy
13908 * object just like for LaunchVMProcess. */
13909 Assert(mData->mSession.mProgress.isNull());
13910 ComObjPtr<ProgressProxy> progress;
13911 progress.createObject();
13912 ComPtr<IUnknown> pPeer(mPeer);
13913 progress->init(mParent, pPeer,
13914 Bstr(tr("Closing session")).raw(),
13915 FALSE /* aCancelable */);
13916 progress.queryInterfaceTo(aProgress.asOutParam());
13917 mData->mSession.mProgress = progress;
13918 }
13919 else
13920 {
13921 /* the remote session is being normally closed */
13922 bool found = false;
13923 for (Data::Session::RemoteControlList::iterator
13924 it = mData->mSession.mRemoteControls.begin();
13925 it != mData->mSession.mRemoteControls.end();
13926 ++it)
13927 {
13928 if (control == *it)
13929 {
13930 found = true;
13931 // This MUST be erase(it), not remove(*it) as the latter
13932 // triggers a very nasty use after free due to the place where
13933 // the value "lives".
13934 mData->mSession.mRemoteControls.erase(it);
13935 break;
13936 }
13937 }
13938 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
13939 E_INVALIDARG);
13940 }
13941
13942 /* signal the client watcher thread, because the client is going away */
13943 mParent->i_updateClientWatcher();
13944
13945 LogFlowThisFuncLeave();
13946 return S_OK;
13947}
13948
13949HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13950 std::vector<com::Utf8Str> &aValues,
13951 std::vector<LONG64> &aTimestamps,
13952 std::vector<com::Utf8Str> &aFlags)
13953{
13954 LogFlowThisFunc(("\n"));
13955
13956#ifdef VBOX_WITH_GUEST_PROPS
13957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13958
13959 size_t cEntries = mHWData->mGuestProperties.size();
13960 aNames.resize(cEntries);
13961 aValues.resize(cEntries);
13962 aTimestamps.resize(cEntries);
13963 aFlags.resize(cEntries);
13964
13965 size_t i = 0;
13966 for (HWData::GuestPropertyMap::const_iterator
13967 it = mHWData->mGuestProperties.begin();
13968 it != mHWData->mGuestProperties.end();
13969 ++it, ++i)
13970 {
13971 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13972 aNames[i] = it->first;
13973 aValues[i] = it->second.strValue;
13974 aTimestamps[i] = it->second.mTimestamp;
13975
13976 /* If it is NULL, keep it NULL. */
13977 if (it->second.mFlags)
13978 {
13979 GuestPropWriteFlags(it->second.mFlags, szFlags);
13980 aFlags[i] = szFlags;
13981 }
13982 else
13983 aFlags[i] = "";
13984
13985 AssertReturn(RT_SUCCESS(GuestPropValidateName(aNames[i].c_str(), (uint32_t)aNames[i].length() + 1 /* '\0' */)), E_INVALIDARG);
13986 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValues[i].c_str(), (uint32_t)aValues[i].length() + 1 /* '\0' */)), E_INVALIDARG);
13987 }
13988 return S_OK;
13989#else
13990 ReturnComNotImplemented();
13991#endif
13992}
13993
13994HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13995 const com::Utf8Str &aValue,
13996 LONG64 aTimestamp,
13997 const com::Utf8Str &aFlags,
13998 BOOL fWasDeleted)
13999{
14000 LogFlowThisFunc(("\n"));
14001
14002#ifdef VBOX_WITH_GUEST_PROPS
14003 try
14004 {
14005 /*
14006 * Convert input up front.
14007 */
14008 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
14009 if (aFlags.length())
14010 {
14011 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
14012 AssertRCReturn(vrc, E_INVALIDARG);
14013 }
14014
14015 /*
14016 * Now grab the object lock, validate the state and do the update.
14017 */
14018
14019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14020
14021 if (!Global::IsOnline(mData->mMachineState))
14022 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
14023
14024 i_setModified(IsModified_MachineData);
14025 mHWData.backup();
14026
14027 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
14028 if (it != mHWData->mGuestProperties.end())
14029 {
14030 if (!fWasDeleted)
14031 {
14032 it->second.strValue = aValue;
14033 it->second.mTimestamp = aTimestamp;
14034 it->second.mFlags = fFlags;
14035 }
14036 else
14037 mHWData->mGuestProperties.erase(it);
14038
14039 mData->mGuestPropertiesModified = TRUE;
14040 }
14041 else if (!fWasDeleted)
14042 {
14043 HWData::GuestProperty prop;
14044 prop.strValue = aValue;
14045 prop.mTimestamp = aTimestamp;
14046 prop.mFlags = fFlags;
14047
14048 mHWData->mGuestProperties[aName] = prop;
14049 mData->mGuestPropertiesModified = TRUE;
14050 }
14051
14052 alock.release();
14053
14054 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fWasDeleted);
14055 }
14056 catch (...)
14057 {
14058 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
14059 }
14060 return S_OK;
14061#else
14062 ReturnComNotImplemented();
14063#endif
14064}
14065
14066
14067HRESULT SessionMachine::lockMedia()
14068{
14069 AutoMultiWriteLock2 alock(this->lockHandle(),
14070 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14071
14072 AssertReturn( mData->mMachineState == MachineState_Starting
14073 || mData->mMachineState == MachineState_Restoring
14074 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
14075
14076 clearError();
14077 alock.release();
14078 return i_lockMedia();
14079}
14080
14081HRESULT SessionMachine::unlockMedia()
14082{
14083 HRESULT hrc = i_unlockMedia();
14084 return hrc;
14085}
14086
14087HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14088 ComPtr<IMediumAttachment> &aNewAttachment)
14089{
14090 // request the host lock first, since might be calling Host methods for getting host drives;
14091 // next, protect the media tree all the while we're in here, as well as our member variables
14092 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
14093 this->lockHandle(),
14094 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14095
14096 IMediumAttachment *iAttach = aAttachment;
14097 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
14098
14099 Utf8Str ctrlName;
14100 LONG lPort;
14101 LONG lDevice;
14102 bool fTempEject;
14103 {
14104 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14105
14106 /* Need to query the details first, as the IMediumAttachment reference
14107 * might be to the original settings, which we are going to change. */
14108 ctrlName = pAttach->i_getControllerName();
14109 lPort = pAttach->i_getPort();
14110 lDevice = pAttach->i_getDevice();
14111 fTempEject = pAttach->i_getTempEject();
14112 }
14113
14114 if (!fTempEject)
14115 {
14116 /* Remember previously mounted medium. The medium before taking the
14117 * backup is not necessarily the same thing. */
14118 ComObjPtr<Medium> oldmedium;
14119 oldmedium = pAttach->i_getMedium();
14120
14121 i_setModified(IsModified_Storage);
14122 mMediumAttachments.backup();
14123
14124 // The backup operation makes the pAttach reference point to the
14125 // old settings. Re-get the correct reference.
14126 pAttach = i_findAttachment(*mMediumAttachments.data(),
14127 ctrlName,
14128 lPort,
14129 lDevice);
14130
14131 {
14132 AutoCaller autoAttachCaller(this);
14133 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
14134
14135 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14136 if (!oldmedium.isNull())
14137 oldmedium->i_removeBackReference(mData->mUuid);
14138
14139 pAttach->i_updateMedium(NULL);
14140 pAttach->i_updateEjected();
14141 }
14142
14143 i_setModified(IsModified_Storage);
14144 }
14145 else
14146 {
14147 {
14148 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14149 pAttach->i_updateEjected();
14150 }
14151 }
14152
14153 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
14154
14155 return S_OK;
14156}
14157
14158HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
14159 com::Utf8Str &aResult)
14160{
14161 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14162
14163 HRESULT hr = S_OK;
14164
14165 if (!mAuthLibCtx.hAuthLibrary)
14166 {
14167 /* Load the external authentication library. */
14168 Bstr authLibrary;
14169 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
14170
14171 Utf8Str filename = authLibrary;
14172
14173 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
14174 if (RT_FAILURE(vrc))
14175 hr = setErrorBoth(E_FAIL, vrc,
14176 tr("Could not load the external authentication library '%s' (%Rrc)"),
14177 filename.c_str(), vrc);
14178 }
14179
14180 /* The auth library might need the machine lock. */
14181 alock.release();
14182
14183 if (FAILED(hr))
14184 return hr;
14185
14186 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
14187 {
14188 enum VRDEAuthParams
14189 {
14190 parmUuid = 1,
14191 parmGuestJudgement,
14192 parmUser,
14193 parmPassword,
14194 parmDomain,
14195 parmClientId
14196 };
14197
14198 AuthResult result = AuthResultAccessDenied;
14199
14200 Guid uuid(aAuthParams[parmUuid]);
14201 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
14202 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
14203
14204 result = AuthLibAuthenticate(&mAuthLibCtx,
14205 uuid.raw(), guestJudgement,
14206 aAuthParams[parmUser].c_str(),
14207 aAuthParams[parmPassword].c_str(),
14208 aAuthParams[parmDomain].c_str(),
14209 u32ClientId);
14210
14211 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
14212 size_t cbPassword = aAuthParams[parmPassword].length();
14213 if (cbPassword)
14214 {
14215 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
14216 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
14217 }
14218
14219 if (result == AuthResultAccessGranted)
14220 aResult = "granted";
14221 else
14222 aResult = "denied";
14223
14224 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
14225 aAuthParams[parmUser].c_str(), aResult.c_str()));
14226 }
14227 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
14228 {
14229 enum VRDEAuthDisconnectParams
14230 {
14231 parmUuid = 1,
14232 parmClientId
14233 };
14234
14235 Guid uuid(aAuthParams[parmUuid]);
14236 uint32_t u32ClientId = 0;
14237 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
14238 }
14239 else
14240 {
14241 hr = E_INVALIDARG;
14242 }
14243
14244 return hr;
14245}
14246
14247// public methods only for internal purposes
14248/////////////////////////////////////////////////////////////////////////////
14249
14250#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14251/**
14252 * Called from the client watcher thread to check for expected or unexpected
14253 * death of the client process that has a direct session to this machine.
14254 *
14255 * On Win32 and on OS/2, this method is called only when we've got the
14256 * mutex (i.e. the client has either died or terminated normally) so it always
14257 * returns @c true (the client is terminated, the session machine is
14258 * uninitialized).
14259 *
14260 * On other platforms, the method returns @c true if the client process has
14261 * terminated normally or abnormally and the session machine was uninitialized,
14262 * and @c false if the client process is still alive.
14263 *
14264 * @note Locks this object for writing.
14265 */
14266bool SessionMachine::i_checkForDeath()
14267{
14268 Uninit::Reason reason;
14269 bool terminated = false;
14270
14271 /* Enclose autoCaller with a block because calling uninit() from under it
14272 * will deadlock. */
14273 {
14274 AutoCaller autoCaller(this);
14275 if (!autoCaller.isOk())
14276 {
14277 /* return true if not ready, to cause the client watcher to exclude
14278 * the corresponding session from watching */
14279 LogFlowThisFunc(("Already uninitialized!\n"));
14280 return true;
14281 }
14282
14283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14284
14285 /* Determine the reason of death: if the session state is Closing here,
14286 * everything is fine. Otherwise it means that the client did not call
14287 * OnSessionEnd() before it released the IPC semaphore. This may happen
14288 * either because the client process has abnormally terminated, or
14289 * because it simply forgot to call ISession::Close() before exiting. We
14290 * threat the latter also as an abnormal termination (see
14291 * Session::uninit() for details). */
14292 reason = mData->mSession.mState == SessionState_Unlocking ?
14293 Uninit::Normal :
14294 Uninit::Abnormal;
14295
14296 if (mClientToken)
14297 terminated = mClientToken->release();
14298 } /* AutoCaller block */
14299
14300 if (terminated)
14301 uninit(reason);
14302
14303 return terminated;
14304}
14305
14306void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14307{
14308 LogFlowThisFunc(("\n"));
14309
14310 strTokenId.setNull();
14311
14312 AutoCaller autoCaller(this);
14313 AssertComRCReturnVoid(autoCaller.rc());
14314
14315 Assert(mClientToken);
14316 if (mClientToken)
14317 mClientToken->getId(strTokenId);
14318}
14319#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14320IToken *SessionMachine::i_getToken()
14321{
14322 LogFlowThisFunc(("\n"));
14323
14324 AutoCaller autoCaller(this);
14325 AssertComRCReturn(autoCaller.rc(), NULL);
14326
14327 Assert(mClientToken);
14328 if (mClientToken)
14329 return mClientToken->getToken();
14330 else
14331 return NULL;
14332}
14333#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14334
14335Machine::ClientToken *SessionMachine::i_getClientToken()
14336{
14337 LogFlowThisFunc(("\n"));
14338
14339 AutoCaller autoCaller(this);
14340 AssertComRCReturn(autoCaller.rc(), NULL);
14341
14342 return mClientToken;
14343}
14344
14345
14346/**
14347 * @note Locks this object for reading.
14348 */
14349HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14350{
14351 LogFlowThisFunc(("\n"));
14352
14353 AutoCaller autoCaller(this);
14354 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14355
14356 ComPtr<IInternalSessionControl> directControl;
14357 {
14358 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14359 if (mData->mSession.mLockType == LockType_VM)
14360 directControl = mData->mSession.mDirectControl;
14361 }
14362
14363 /* ignore notifications sent after #OnSessionEnd() is called */
14364 if (!directControl)
14365 return S_OK;
14366
14367 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14368}
14369
14370/**
14371 * @note Locks this object for reading.
14372 */
14373HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
14374 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
14375 const Utf8Str &aGuestIp, LONG aGuestPort)
14376{
14377 LogFlowThisFunc(("\n"));
14378
14379 AutoCaller autoCaller(this);
14380 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14381
14382 ComPtr<IInternalSessionControl> directControl;
14383 {
14384 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14385 if (mData->mSession.mLockType == LockType_VM)
14386 directControl = mData->mSession.mDirectControl;
14387 }
14388
14389 /* ignore notifications sent after #OnSessionEnd() is called */
14390 if (!directControl)
14391 return S_OK;
14392 /*
14393 * instead acting like callback we ask IVirtualBox deliver corresponding event
14394 */
14395
14396 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14397 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14398 return S_OK;
14399}
14400
14401/**
14402 * @note Locks this object for reading.
14403 */
14404HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14405{
14406 LogFlowThisFunc(("\n"));
14407
14408 AutoCaller autoCaller(this);
14409 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14410
14411 ComPtr<IInternalSessionControl> directControl;
14412 {
14413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14414 if (mData->mSession.mLockType == LockType_VM)
14415 directControl = mData->mSession.mDirectControl;
14416 }
14417
14418 /* ignore notifications sent after #OnSessionEnd() is called */
14419 if (!directControl)
14420 return S_OK;
14421
14422 return directControl->OnAudioAdapterChange(audioAdapter);
14423}
14424
14425/**
14426 * @note Locks this object for reading.
14427 */
14428HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14429{
14430 LogFlowThisFunc(("\n"));
14431
14432 AutoCaller autoCaller(this);
14433 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14434
14435 ComPtr<IInternalSessionControl> directControl;
14436 {
14437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14438 if (mData->mSession.mLockType == LockType_VM)
14439 directControl = mData->mSession.mDirectControl;
14440 }
14441
14442 /* ignore notifications sent after #OnSessionEnd() is called */
14443 if (!directControl)
14444 return S_OK;
14445
14446 return directControl->OnSerialPortChange(serialPort);
14447}
14448
14449/**
14450 * @note Locks this object for reading.
14451 */
14452HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14453{
14454 LogFlowThisFunc(("\n"));
14455
14456 AutoCaller autoCaller(this);
14457 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14458
14459 ComPtr<IInternalSessionControl> directControl;
14460 {
14461 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14462 if (mData->mSession.mLockType == LockType_VM)
14463 directControl = mData->mSession.mDirectControl;
14464 }
14465
14466 /* ignore notifications sent after #OnSessionEnd() is called */
14467 if (!directControl)
14468 return S_OK;
14469
14470 return directControl->OnParallelPortChange(parallelPort);
14471}
14472
14473/**
14474 * @note Locks this object for reading.
14475 */
14476HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14477{
14478 LogFlowThisFunc(("\n"));
14479
14480 AutoCaller autoCaller(this);
14481 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14482
14483 ComPtr<IInternalSessionControl> directControl;
14484 {
14485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14486 if (mData->mSession.mLockType == LockType_VM)
14487 directControl = mData->mSession.mDirectControl;
14488 }
14489
14490 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14491
14492 /* ignore notifications sent after #OnSessionEnd() is called */
14493 if (!directControl)
14494 return S_OK;
14495
14496 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14497}
14498
14499/**
14500 * @note Locks this object for reading.
14501 */
14502HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14503{
14504 LogFlowThisFunc(("\n"));
14505
14506 AutoCaller autoCaller(this);
14507 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14508
14509 ComPtr<IInternalSessionControl> directControl;
14510 {
14511 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14512 if (mData->mSession.mLockType == LockType_VM)
14513 directControl = mData->mSession.mDirectControl;
14514 }
14515
14516 mParent->i_onMediumChanged(aAttachment);
14517
14518 /* ignore notifications sent after #OnSessionEnd() is called */
14519 if (!directControl)
14520 return S_OK;
14521
14522 return directControl->OnMediumChange(aAttachment, aForce);
14523}
14524
14525HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14526{
14527 LogFlowThisFunc(("\n"));
14528
14529 AutoCaller autoCaller(this);
14530 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14531
14532 ComPtr<IInternalSessionControl> directControl;
14533 {
14534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14535 if (mData->mSession.mLockType == LockType_VM)
14536 directControl = mData->mSession.mDirectControl;
14537 }
14538
14539 /* ignore notifications sent after #OnSessionEnd() is called */
14540 if (!directControl)
14541 return S_OK;
14542
14543 return directControl->OnVMProcessPriorityChange(aPriority);
14544}
14545
14546/**
14547 * @note Locks this object for reading.
14548 */
14549HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14550{
14551 LogFlowThisFunc(("\n"));
14552
14553 AutoCaller autoCaller(this);
14554 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14555
14556 ComPtr<IInternalSessionControl> directControl;
14557 {
14558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14559 if (mData->mSession.mLockType == LockType_VM)
14560 directControl = mData->mSession.mDirectControl;
14561 }
14562
14563 /* ignore notifications sent after #OnSessionEnd() is called */
14564 if (!directControl)
14565 return S_OK;
14566
14567 return directControl->OnCPUChange(aCPU, aRemove);
14568}
14569
14570HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14571{
14572 LogFlowThisFunc(("\n"));
14573
14574 AutoCaller autoCaller(this);
14575 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14576
14577 ComPtr<IInternalSessionControl> directControl;
14578 {
14579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14580 if (mData->mSession.mLockType == LockType_VM)
14581 directControl = mData->mSession.mDirectControl;
14582 }
14583
14584 /* ignore notifications sent after #OnSessionEnd() is called */
14585 if (!directControl)
14586 return S_OK;
14587
14588 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14589}
14590
14591/**
14592 * @note Locks this object for reading.
14593 */
14594HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14595{
14596 LogFlowThisFunc(("\n"));
14597
14598 AutoCaller autoCaller(this);
14599 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14600
14601 ComPtr<IInternalSessionControl> directControl;
14602 {
14603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14604 if (mData->mSession.mLockType == LockType_VM)
14605 directControl = mData->mSession.mDirectControl;
14606 }
14607
14608 /* ignore notifications sent after #OnSessionEnd() is called */
14609 if (!directControl)
14610 return S_OK;
14611
14612 return directControl->OnVRDEServerChange(aRestart);
14613}
14614
14615/**
14616 * @note Locks this object for reading.
14617 */
14618HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14619{
14620 LogFlowThisFunc(("\n"));
14621
14622 AutoCaller autoCaller(this);
14623 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14624
14625 ComPtr<IInternalSessionControl> directControl;
14626 {
14627 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14628 if (mData->mSession.mLockType == LockType_VM)
14629 directControl = mData->mSession.mDirectControl;
14630 }
14631
14632 /* ignore notifications sent after #OnSessionEnd() is called */
14633 if (!directControl)
14634 return S_OK;
14635
14636 return directControl->OnRecordingChange(aEnable);
14637}
14638
14639/**
14640 * @note Locks this object for reading.
14641 */
14642HRESULT SessionMachine::i_onUSBControllerChange()
14643{
14644 LogFlowThisFunc(("\n"));
14645
14646 AutoCaller autoCaller(this);
14647 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14648
14649 ComPtr<IInternalSessionControl> directControl;
14650 {
14651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14652 if (mData->mSession.mLockType == LockType_VM)
14653 directControl = mData->mSession.mDirectControl;
14654 }
14655
14656 /* ignore notifications sent after #OnSessionEnd() is called */
14657 if (!directControl)
14658 return S_OK;
14659
14660 return directControl->OnUSBControllerChange();
14661}
14662
14663/**
14664 * @note Locks this object for reading.
14665 */
14666HRESULT SessionMachine::i_onSharedFolderChange()
14667{
14668 LogFlowThisFunc(("\n"));
14669
14670 AutoCaller autoCaller(this);
14671 AssertComRCReturnRC(autoCaller.rc());
14672
14673 ComPtr<IInternalSessionControl> directControl;
14674 {
14675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14676 if (mData->mSession.mLockType == LockType_VM)
14677 directControl = mData->mSession.mDirectControl;
14678 }
14679
14680 /* ignore notifications sent after #OnSessionEnd() is called */
14681 if (!directControl)
14682 return S_OK;
14683
14684 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14685}
14686
14687/**
14688 * @note Locks this object for reading.
14689 */
14690HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14691{
14692 LogFlowThisFunc(("\n"));
14693
14694 AutoCaller autoCaller(this);
14695 AssertComRCReturnRC(autoCaller.rc());
14696
14697 ComPtr<IInternalSessionControl> directControl;
14698 {
14699 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14700 if (mData->mSession.mLockType == LockType_VM)
14701 directControl = mData->mSession.mDirectControl;
14702 }
14703
14704 /* ignore notifications sent after #OnSessionEnd() is called */
14705 if (!directControl)
14706 return S_OK;
14707
14708 return directControl->OnClipboardModeChange(aClipboardMode);
14709}
14710
14711/**
14712 * @note Locks this object for reading.
14713 */
14714HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14715{
14716 LogFlowThisFunc(("\n"));
14717
14718 AutoCaller autoCaller(this);
14719 AssertComRCReturnRC(autoCaller.rc());
14720
14721 ComPtr<IInternalSessionControl> directControl;
14722 {
14723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14724 if (mData->mSession.mLockType == LockType_VM)
14725 directControl = mData->mSession.mDirectControl;
14726 }
14727
14728 /* ignore notifications sent after #OnSessionEnd() is called */
14729 if (!directControl)
14730 return S_OK;
14731
14732 return directControl->OnClipboardFileTransferModeChange(aEnable);
14733}
14734
14735/**
14736 * @note Locks this object for reading.
14737 */
14738HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14739{
14740 LogFlowThisFunc(("\n"));
14741
14742 AutoCaller autoCaller(this);
14743 AssertComRCReturnRC(autoCaller.rc());
14744
14745 ComPtr<IInternalSessionControl> directControl;
14746 {
14747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14748 if (mData->mSession.mLockType == LockType_VM)
14749 directControl = mData->mSession.mDirectControl;
14750 }
14751
14752 /* ignore notifications sent after #OnSessionEnd() is called */
14753 if (!directControl)
14754 return S_OK;
14755
14756 return directControl->OnDnDModeChange(aDnDMode);
14757}
14758
14759/**
14760 * @note Locks this object for reading.
14761 */
14762HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14763{
14764 LogFlowThisFunc(("\n"));
14765
14766 AutoCaller autoCaller(this);
14767 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14768
14769 ComPtr<IInternalSessionControl> directControl;
14770 {
14771 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14772 if (mData->mSession.mLockType == LockType_VM)
14773 directControl = mData->mSession.mDirectControl;
14774 }
14775
14776 /* ignore notifications sent after #OnSessionEnd() is called */
14777 if (!directControl)
14778 return S_OK;
14779
14780 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14781}
14782
14783/**
14784 * @note Locks this object for reading.
14785 */
14786HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14787{
14788 LogFlowThisFunc(("\n"));
14789
14790 AutoCaller autoCaller(this);
14791 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14792
14793 ComPtr<IInternalSessionControl> directControl;
14794 {
14795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14796 if (mData->mSession.mLockType == LockType_VM)
14797 directControl = mData->mSession.mDirectControl;
14798 }
14799
14800 /* ignore notifications sent after #OnSessionEnd() is called */
14801 if (!directControl)
14802 return S_OK;
14803
14804 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14805}
14806
14807/**
14808 * Returns @c true if this machine's USB controller reports it has a matching
14809 * filter for the given USB device and @c false otherwise.
14810 *
14811 * @note locks this object for reading.
14812 */
14813bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14814{
14815 AutoCaller autoCaller(this);
14816 /* silently return if not ready -- this method may be called after the
14817 * direct machine session has been called */
14818 if (!autoCaller.isOk())
14819 return false;
14820
14821#ifdef VBOX_WITH_USB
14822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14823
14824 switch (mData->mMachineState)
14825 {
14826 case MachineState_Starting:
14827 case MachineState_Restoring:
14828 case MachineState_TeleportingIn:
14829 case MachineState_Paused:
14830 case MachineState_Running:
14831 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14832 * elsewhere... */
14833 alock.release();
14834 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14835 default: break;
14836 }
14837#else
14838 NOREF(aDevice);
14839 NOREF(aMaskedIfs);
14840#endif
14841 return false;
14842}
14843
14844/**
14845 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14846 */
14847HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14848 IVirtualBoxErrorInfo *aError,
14849 ULONG aMaskedIfs,
14850 const com::Utf8Str &aCaptureFilename)
14851{
14852 LogFlowThisFunc(("\n"));
14853
14854 AutoCaller autoCaller(this);
14855
14856 /* This notification may happen after the machine object has been
14857 * uninitialized (the session was closed), so don't assert. */
14858 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14859
14860 ComPtr<IInternalSessionControl> directControl;
14861 {
14862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14863 if (mData->mSession.mLockType == LockType_VM)
14864 directControl = mData->mSession.mDirectControl;
14865 }
14866
14867 /* fail on notifications sent after #OnSessionEnd() is called, it is
14868 * expected by the caller */
14869 if (!directControl)
14870 return E_FAIL;
14871
14872 /* No locks should be held at this point. */
14873 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14874 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14875
14876 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14877}
14878
14879/**
14880 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14881 */
14882HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14883 IVirtualBoxErrorInfo *aError)
14884{
14885 LogFlowThisFunc(("\n"));
14886
14887 AutoCaller autoCaller(this);
14888
14889 /* This notification may happen after the machine object has been
14890 * uninitialized (the session was closed), so don't assert. */
14891 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14892
14893 ComPtr<IInternalSessionControl> directControl;
14894 {
14895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14896 if (mData->mSession.mLockType == LockType_VM)
14897 directControl = mData->mSession.mDirectControl;
14898 }
14899
14900 /* fail on notifications sent after #OnSessionEnd() is called, it is
14901 * expected by the caller */
14902 if (!directControl)
14903 return E_FAIL;
14904
14905 /* No locks should be held at this point. */
14906 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14907 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14908
14909 return directControl->OnUSBDeviceDetach(aId, aError);
14910}
14911
14912// protected methods
14913/////////////////////////////////////////////////////////////////////////////
14914
14915/**
14916 * Deletes the given file if it is no longer in use by either the current machine state
14917 * (if the machine is "saved") or any of the machine's snapshots.
14918 *
14919 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14920 * but is different for each SnapshotMachine. When calling this, the order of calling this
14921 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14922 * is therefore critical. I know, it's all rather messy.
14923 *
14924 * @param strStateFile
14925 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14926 * the test for whether the saved state file is in use.
14927 */
14928void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14929 Snapshot *pSnapshotToIgnore)
14930{
14931 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14932 if ( (strStateFile.isNotEmpty())
14933 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14934 )
14935 // ... and it must also not be shared with other snapshots
14936 if ( !mData->mFirstSnapshot
14937 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14938 // this checks the SnapshotMachine's state file paths
14939 )
14940 RTFileDelete(strStateFile.c_str());
14941}
14942
14943/**
14944 * Locks the attached media.
14945 *
14946 * All attached hard disks are locked for writing and DVD/floppy are locked for
14947 * reading. Parents of attached hard disks (if any) are locked for reading.
14948 *
14949 * This method also performs accessibility check of all media it locks: if some
14950 * media is inaccessible, the method will return a failure and a bunch of
14951 * extended error info objects per each inaccessible medium.
14952 *
14953 * Note that this method is atomic: if it returns a success, all media are
14954 * locked as described above; on failure no media is locked at all (all
14955 * succeeded individual locks will be undone).
14956 *
14957 * The caller is responsible for doing the necessary state sanity checks.
14958 *
14959 * The locks made by this method must be undone by calling #unlockMedia() when
14960 * no more needed.
14961 */
14962HRESULT SessionMachine::i_lockMedia()
14963{
14964 AutoCaller autoCaller(this);
14965 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14966
14967 AutoMultiWriteLock2 alock(this->lockHandle(),
14968 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14969
14970 /* bail out if trying to lock things with already set up locking */
14971 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14972
14973 MultiResult mrc(S_OK);
14974
14975 /* Collect locking information for all medium objects attached to the VM. */
14976 for (MediumAttachmentList::const_iterator
14977 it = mMediumAttachments->begin();
14978 it != mMediumAttachments->end();
14979 ++it)
14980 {
14981 MediumAttachment *pAtt = *it;
14982 DeviceType_T devType = pAtt->i_getType();
14983 Medium *pMedium = pAtt->i_getMedium();
14984
14985 MediumLockList *pMediumLockList(new MediumLockList());
14986 // There can be attachments without a medium (floppy/dvd), and thus
14987 // it's impossible to create a medium lock list. It still makes sense
14988 // to have the empty medium lock list in the map in case a medium is
14989 // attached later.
14990 if (pMedium != NULL)
14991 {
14992 MediumType_T mediumType = pMedium->i_getType();
14993 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14994 || mediumType == MediumType_Shareable;
14995 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14996
14997 alock.release();
14998 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14999 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
15000 false /* fMediumLockWriteAll */,
15001 NULL,
15002 *pMediumLockList);
15003 alock.acquire();
15004 if (FAILED(mrc))
15005 {
15006 delete pMediumLockList;
15007 mData->mSession.mLockedMedia.Clear();
15008 break;
15009 }
15010 }
15011
15012 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
15013 if (FAILED(rc))
15014 {
15015 mData->mSession.mLockedMedia.Clear();
15016 mrc = setError(rc,
15017 tr("Collecting locking information for all attached media failed"));
15018 break;
15019 }
15020 }
15021
15022 if (SUCCEEDED(mrc))
15023 {
15024 /* Now lock all media. If this fails, nothing is locked. */
15025 alock.release();
15026 HRESULT rc = mData->mSession.mLockedMedia.Lock();
15027 alock.acquire();
15028 if (FAILED(rc))
15029 {
15030 mrc = setError(rc,
15031 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
15032 }
15033 }
15034
15035 return mrc;
15036}
15037
15038/**
15039 * Undoes the locks made by by #lockMedia().
15040 */
15041HRESULT SessionMachine::i_unlockMedia()
15042{
15043 AutoCaller autoCaller(this);
15044 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15045
15046 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15047
15048 /* we may be holding important error info on the current thread;
15049 * preserve it */
15050 ErrorInfoKeeper eik;
15051
15052 HRESULT rc = mData->mSession.mLockedMedia.Clear();
15053 AssertComRC(rc);
15054 return rc;
15055}
15056
15057/**
15058 * Helper to change the machine state (reimplementation).
15059 *
15060 * @note Locks this object for writing.
15061 * @note This method must not call i_saveSettings or SaveSettings, otherwise
15062 * it can cause crashes in random places due to unexpectedly committing
15063 * the current settings. The caller is responsible for that. The call
15064 * to saveStateSettings is fine, because this method does not commit.
15065 */
15066HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
15067{
15068 LogFlowThisFuncEnter();
15069
15070 AutoCaller autoCaller(this);
15071 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15072
15073 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15074
15075 MachineState_T oldMachineState = mData->mMachineState;
15076
15077 AssertMsgReturn(oldMachineState != aMachineState,
15078 ("oldMachineState=%s, aMachineState=%s\n",
15079 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
15080 E_FAIL);
15081
15082 HRESULT rc = S_OK;
15083
15084 int stsFlags = 0;
15085 bool deleteSavedState = false;
15086
15087 /* detect some state transitions */
15088
15089 if ( ( ( oldMachineState == MachineState_Saved
15090 || oldMachineState == MachineState_AbortedSaved
15091 )
15092 && aMachineState == MachineState_Restoring
15093 )
15094 || ( ( oldMachineState == MachineState_PoweredOff
15095 || oldMachineState == MachineState_Teleported
15096 || oldMachineState == MachineState_Aborted
15097 )
15098 && ( aMachineState == MachineState_TeleportingIn
15099 || aMachineState == MachineState_Starting
15100 )
15101 )
15102 )
15103 {
15104 /* The EMT thread is about to start */
15105
15106 /* Nothing to do here for now... */
15107
15108 /// @todo NEWMEDIA don't let mDVDDrive and other children
15109 /// change anything when in the Starting/Restoring state
15110 }
15111 else if ( ( oldMachineState == MachineState_Running
15112 || oldMachineState == MachineState_Paused
15113 || oldMachineState == MachineState_Teleporting
15114 || oldMachineState == MachineState_OnlineSnapshotting
15115 || oldMachineState == MachineState_LiveSnapshotting
15116 || oldMachineState == MachineState_Stuck
15117 || oldMachineState == MachineState_Starting
15118 || oldMachineState == MachineState_Stopping
15119 || oldMachineState == MachineState_Saving
15120 || oldMachineState == MachineState_Restoring
15121 || oldMachineState == MachineState_TeleportingPausedVM
15122 || oldMachineState == MachineState_TeleportingIn
15123 )
15124 && ( aMachineState == MachineState_PoweredOff
15125 || aMachineState == MachineState_Saved
15126 || aMachineState == MachineState_Teleported
15127 || aMachineState == MachineState_Aborted
15128 || aMachineState == MachineState_AbortedSaved
15129 )
15130 )
15131 {
15132 /* The EMT thread has just stopped, unlock attached media. Note that as
15133 * opposed to locking that is done from Console, we do unlocking here
15134 * because the VM process may have aborted before having a chance to
15135 * properly unlock all media it locked. */
15136
15137 unlockMedia();
15138 }
15139
15140 if (oldMachineState == MachineState_Restoring)
15141 {
15142 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
15143 {
15144 /*
15145 * delete the saved state file once the machine has finished
15146 * restoring from it (note that Console sets the state from
15147 * Restoring to AbortedSaved if the VM couldn't restore successfully,
15148 * to give the user an ability to fix an error and retry --
15149 * we keep the saved state file in this case)
15150 */
15151 deleteSavedState = true;
15152 }
15153 }
15154 else if ( oldMachineState == MachineState_Saved
15155 && ( aMachineState == MachineState_PoweredOff
15156 || aMachineState == MachineState_Teleported
15157 )
15158 )
15159 {
15160 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
15161 deleteSavedState = true;
15162 mData->mCurrentStateModified = TRUE;
15163 stsFlags |= SaveSTS_CurStateModified;
15164 }
15165 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
15166 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
15167
15168 if ( aMachineState == MachineState_Starting
15169 || aMachineState == MachineState_Restoring
15170 || aMachineState == MachineState_TeleportingIn
15171 )
15172 {
15173 /* set the current state modified flag to indicate that the current
15174 * state is no more identical to the state in the
15175 * current snapshot */
15176 if (!mData->mCurrentSnapshot.isNull())
15177 {
15178 mData->mCurrentStateModified = TRUE;
15179 stsFlags |= SaveSTS_CurStateModified;
15180 }
15181 }
15182
15183 if (deleteSavedState)
15184 {
15185 if (mRemoveSavedState)
15186 {
15187 Assert(!mSSData->strStateFilePath.isEmpty());
15188
15189 // it is safe to delete the saved state file if ...
15190 if ( !mData->mFirstSnapshot // ... we have no snapshots or
15191 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
15192 // ... none of the snapshots share the saved state file
15193 )
15194 RTFileDelete(mSSData->strStateFilePath.c_str());
15195 }
15196
15197 mSSData->strStateFilePath.setNull();
15198 stsFlags |= SaveSTS_StateFilePath;
15199 }
15200
15201 /* redirect to the underlying peer machine */
15202 mPeer->i_setMachineState(aMachineState);
15203
15204 if ( oldMachineState != MachineState_RestoringSnapshot
15205 && ( aMachineState == MachineState_PoweredOff
15206 || aMachineState == MachineState_Teleported
15207 || aMachineState == MachineState_Aborted
15208 || aMachineState == MachineState_AbortedSaved
15209 || aMachineState == MachineState_Saved))
15210 {
15211 /* the machine has stopped execution
15212 * (or the saved state file was adopted) */
15213 stsFlags |= SaveSTS_StateTimeStamp;
15214 }
15215
15216 if ( ( oldMachineState == MachineState_PoweredOff
15217 || oldMachineState == MachineState_Aborted
15218 || oldMachineState == MachineState_Teleported
15219 )
15220 && aMachineState == MachineState_Saved)
15221 {
15222 /* the saved state file was adopted */
15223 Assert(!mSSData->strStateFilePath.isEmpty());
15224 stsFlags |= SaveSTS_StateFilePath;
15225 }
15226
15227#ifdef VBOX_WITH_GUEST_PROPS
15228 if ( aMachineState == MachineState_PoweredOff
15229 || aMachineState == MachineState_Aborted
15230 || aMachineState == MachineState_Teleported)
15231 {
15232 /* Make sure any transient guest properties get removed from the
15233 * property store on shutdown. */
15234 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
15235
15236 /* remove it from the settings representation */
15237 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
15238 for (settings::GuestPropertiesList::iterator
15239 it = llGuestProperties.begin();
15240 it != llGuestProperties.end();
15241 /*nothing*/)
15242 {
15243 const settings::GuestProperty &prop = *it;
15244 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
15245 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
15246 {
15247 it = llGuestProperties.erase(it);
15248 fNeedsSaving = true;
15249 }
15250 else
15251 {
15252 ++it;
15253 }
15254 }
15255
15256 /* Additionally remove it from the HWData representation. Required to
15257 * keep everything in sync, as this is what the API keeps using. */
15258 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
15259 for (HWData::GuestPropertyMap::iterator
15260 it = llHWGuestProperties.begin();
15261 it != llHWGuestProperties.end();
15262 /*nothing*/)
15263 {
15264 uint32_t fFlags = it->second.mFlags;
15265 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
15266 {
15267 /* iterator where we need to continue after the erase call
15268 * (C++03 is a fact still, and it doesn't return the iterator
15269 * which would allow continuing) */
15270 HWData::GuestPropertyMap::iterator it2 = it;
15271 ++it2;
15272 llHWGuestProperties.erase(it);
15273 it = it2;
15274 fNeedsSaving = true;
15275 }
15276 else
15277 {
15278 ++it;
15279 }
15280 }
15281
15282 if (fNeedsSaving)
15283 {
15284 mData->mCurrentStateModified = TRUE;
15285 stsFlags |= SaveSTS_CurStateModified;
15286 }
15287 }
15288#endif /* VBOX_WITH_GUEST_PROPS */
15289
15290 rc = i_saveStateSettings(stsFlags);
15291
15292 if ( ( oldMachineState != MachineState_PoweredOff
15293 && oldMachineState != MachineState_Aborted
15294 && oldMachineState != MachineState_Teleported
15295 )
15296 && ( aMachineState == MachineState_PoweredOff
15297 || aMachineState == MachineState_Aborted
15298 || aMachineState == MachineState_Teleported
15299 )
15300 )
15301 {
15302 /* we've been shut down for any reason */
15303 /* no special action so far */
15304 }
15305
15306 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, ::stringifyMachineState(mData->mMachineState) ));
15307 LogFlowThisFuncLeave();
15308 return rc;
15309}
15310
15311/**
15312 * Sends the current machine state value to the VM process.
15313 *
15314 * @note Locks this object for reading, then calls a client process.
15315 */
15316HRESULT SessionMachine::i_updateMachineStateOnClient()
15317{
15318 AutoCaller autoCaller(this);
15319 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15320
15321 ComPtr<IInternalSessionControl> directControl;
15322 {
15323 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15324 AssertReturn(!!mData, E_FAIL);
15325 if (mData->mSession.mLockType == LockType_VM)
15326 directControl = mData->mSession.mDirectControl;
15327
15328 /* directControl may be already set to NULL here in #OnSessionEnd()
15329 * called too early by the direct session process while there is still
15330 * some operation (like deleting the snapshot) in progress. The client
15331 * process in this case is waiting inside Session::close() for the
15332 * "end session" process object to complete, while #uninit() called by
15333 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15334 * operation to complete. For now, we accept this inconsistent behavior
15335 * and simply do nothing here. */
15336
15337 if (mData->mSession.mState == SessionState_Unlocking)
15338 return S_OK;
15339 }
15340
15341 /* ignore notifications sent after #OnSessionEnd() is called */
15342 if (!directControl)
15343 return S_OK;
15344
15345 return directControl->UpdateMachineState(mData->mMachineState);
15346}
15347
15348
15349/*static*/
15350HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15351{
15352 va_list args;
15353 va_start(args, pcszMsg);
15354 HRESULT rc = setErrorInternalV(aResultCode,
15355 getStaticClassIID(),
15356 getStaticComponentName(),
15357 pcszMsg, args,
15358 false /* aWarning */,
15359 true /* aLogIt */);
15360 va_end(args);
15361 return rc;
15362}
15363
15364
15365HRESULT Machine::updateState(MachineState_T aState)
15366{
15367 NOREF(aState);
15368 ReturnComNotImplemented();
15369}
15370
15371HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15372{
15373 NOREF(aProgress);
15374 ReturnComNotImplemented();
15375}
15376
15377HRESULT Machine::endPowerUp(LONG aResult)
15378{
15379 NOREF(aResult);
15380 ReturnComNotImplemented();
15381}
15382
15383HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15384{
15385 NOREF(aProgress);
15386 ReturnComNotImplemented();
15387}
15388
15389HRESULT Machine::endPoweringDown(LONG aResult,
15390 const com::Utf8Str &aErrMsg)
15391{
15392 NOREF(aResult);
15393 NOREF(aErrMsg);
15394 ReturnComNotImplemented();
15395}
15396
15397HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15398 BOOL *aMatched,
15399 ULONG *aMaskedInterfaces)
15400{
15401 NOREF(aDevice);
15402 NOREF(aMatched);
15403 NOREF(aMaskedInterfaces);
15404 ReturnComNotImplemented();
15405
15406}
15407
15408HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15409{
15410 NOREF(aId); NOREF(aCaptureFilename);
15411 ReturnComNotImplemented();
15412}
15413
15414HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15415 BOOL aDone)
15416{
15417 NOREF(aId);
15418 NOREF(aDone);
15419 ReturnComNotImplemented();
15420}
15421
15422HRESULT Machine::autoCaptureUSBDevices()
15423{
15424 ReturnComNotImplemented();
15425}
15426
15427HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15428{
15429 NOREF(aDone);
15430 ReturnComNotImplemented();
15431}
15432
15433HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15434 ComPtr<IProgress> &aProgress)
15435{
15436 NOREF(aSession);
15437 NOREF(aProgress);
15438 ReturnComNotImplemented();
15439}
15440
15441HRESULT Machine::finishOnlineMergeMedium()
15442{
15443 ReturnComNotImplemented();
15444}
15445
15446HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15447 std::vector<com::Utf8Str> &aValues,
15448 std::vector<LONG64> &aTimestamps,
15449 std::vector<com::Utf8Str> &aFlags)
15450{
15451 NOREF(aNames);
15452 NOREF(aValues);
15453 NOREF(aTimestamps);
15454 NOREF(aFlags);
15455 ReturnComNotImplemented();
15456}
15457
15458HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15459 const com::Utf8Str &aValue,
15460 LONG64 aTimestamp,
15461 const com::Utf8Str &aFlags,
15462 BOOL fWasDeleted)
15463{
15464 NOREF(aName);
15465 NOREF(aValue);
15466 NOREF(aTimestamp);
15467 NOREF(aFlags);
15468 NOREF(fWasDeleted);
15469 ReturnComNotImplemented();
15470}
15471
15472HRESULT Machine::lockMedia()
15473{
15474 ReturnComNotImplemented();
15475}
15476
15477HRESULT Machine::unlockMedia()
15478{
15479 ReturnComNotImplemented();
15480}
15481
15482HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15483 ComPtr<IMediumAttachment> &aNewAttachment)
15484{
15485 NOREF(aAttachment);
15486 NOREF(aNewAttachment);
15487 ReturnComNotImplemented();
15488}
15489
15490HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15491 ULONG aCpuUser,
15492 ULONG aCpuKernel,
15493 ULONG aCpuIdle,
15494 ULONG aMemTotal,
15495 ULONG aMemFree,
15496 ULONG aMemBalloon,
15497 ULONG aMemShared,
15498 ULONG aMemCache,
15499 ULONG aPagedTotal,
15500 ULONG aMemAllocTotal,
15501 ULONG aMemFreeTotal,
15502 ULONG aMemBalloonTotal,
15503 ULONG aMemSharedTotal,
15504 ULONG aVmNetRx,
15505 ULONG aVmNetTx)
15506{
15507 NOREF(aValidStats);
15508 NOREF(aCpuUser);
15509 NOREF(aCpuKernel);
15510 NOREF(aCpuIdle);
15511 NOREF(aMemTotal);
15512 NOREF(aMemFree);
15513 NOREF(aMemBalloon);
15514 NOREF(aMemShared);
15515 NOREF(aMemCache);
15516 NOREF(aPagedTotal);
15517 NOREF(aMemAllocTotal);
15518 NOREF(aMemFreeTotal);
15519 NOREF(aMemBalloonTotal);
15520 NOREF(aMemSharedTotal);
15521 NOREF(aVmNetRx);
15522 NOREF(aVmNetTx);
15523 ReturnComNotImplemented();
15524}
15525
15526HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15527 com::Utf8Str &aResult)
15528{
15529 NOREF(aAuthParams);
15530 NOREF(aResult);
15531 ReturnComNotImplemented();
15532}
15533
15534com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15535{
15536 com::Utf8Str strControllerName = "Unknown";
15537 switch (aBusType)
15538 {
15539 case StorageBus_IDE:
15540 {
15541 strControllerName = "IDE";
15542 break;
15543 }
15544 case StorageBus_SATA:
15545 {
15546 strControllerName = "SATA";
15547 break;
15548 }
15549 case StorageBus_SCSI:
15550 {
15551 strControllerName = "SCSI";
15552 break;
15553 }
15554 case StorageBus_Floppy:
15555 {
15556 strControllerName = "Floppy";
15557 break;
15558 }
15559 case StorageBus_SAS:
15560 {
15561 strControllerName = "SAS";
15562 break;
15563 }
15564 case StorageBus_USB:
15565 {
15566 strControllerName = "USB";
15567 break;
15568 }
15569 default:
15570 break;
15571 }
15572 return strControllerName;
15573}
15574
15575HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15576{
15577 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15578
15579 AutoCaller autoCaller(this);
15580 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15581
15582 HRESULT rc = S_OK;
15583
15584 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15585 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15586 rc = getUSBDeviceFilters(usbDeviceFilters);
15587 if (FAILED(rc)) return rc;
15588
15589 NOREF(aFlags);
15590 com::Utf8Str osTypeId;
15591 ComObjPtr<GuestOSType> osType = NULL;
15592
15593 /* Get the guest os type as a string from the VB. */
15594 rc = getOSTypeId(osTypeId);
15595 if (FAILED(rc)) return rc;
15596
15597 /* Get the os type obj that coresponds, can be used to get
15598 * the defaults for this guest OS. */
15599 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15600 if (FAILED(rc)) return rc;
15601
15602 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15603
15604 /* Let the OS type select 64-bit ness. */
15605 mHWData->mLongMode = osType->i_is64Bit()
15606 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15607
15608 /* Let the OS type enable the X2APIC */
15609 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15610
15611 /* This one covers IOAPICEnabled. */
15612 mBIOSSettings->i_applyDefaults(osType);
15613
15614 /* Initialize default record settings. */
15615 mRecordingSettings->i_applyDefaults();
15616
15617 /* Initialize default BIOS settings here */
15618 /* Hardware virtualization must be ON by default */
15619 mHWData->mAPIC = true;
15620 mHWData->mHWVirtExEnabled = true;
15621
15622 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15623 if (FAILED(rc)) return rc;
15624
15625 rc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15626 if (FAILED(rc)) return rc;
15627
15628 /* Graphics stuff. */
15629 GraphicsControllerType_T graphicsController;
15630 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15631 if (FAILED(rc)) return rc;
15632
15633 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15634 if (FAILED(rc)) return rc;
15635
15636 ULONG vramSize;
15637 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15638 if (FAILED(rc)) return rc;
15639
15640 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15641 if (FAILED(rc)) return rc;
15642
15643 BOOL fAccelerate2DVideoEnabled;
15644 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15645 if (FAILED(rc)) return rc;
15646
15647 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15648 if (FAILED(rc)) return rc;
15649
15650 BOOL fAccelerate3DEnabled;
15651 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15652 if (FAILED(rc)) return rc;
15653
15654 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15655 if (FAILED(rc)) return rc;
15656
15657 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15658 if (FAILED(rc)) return rc;
15659
15660 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15661 if (FAILED(rc)) return rc;
15662
15663 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15664 if (FAILED(rc)) return rc;
15665
15666 BOOL mRTCUseUTC;
15667 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15668 if (FAILED(rc)) return rc;
15669
15670 setRTCUseUTC(mRTCUseUTC);
15671 if (FAILED(rc)) return rc;
15672
15673 /* the setter does more than just the assignment, so use it */
15674 ChipsetType_T enmChipsetType;
15675 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15676 if (FAILED(rc)) return rc;
15677
15678 rc = COMSETTER(ChipsetType)(enmChipsetType);
15679 if (FAILED(rc)) return rc;
15680
15681 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15682 if (FAILED(rc)) return rc;
15683
15684 /* Apply IOMMU defaults. */
15685 IommuType_T enmIommuType;
15686 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15687 if (FAILED(rc)) return rc;
15688
15689 rc = COMSETTER(IommuType)(enmIommuType);
15690 if (FAILED(rc)) return rc;
15691
15692 /* Apply network adapters defaults */
15693 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15694 mNetworkAdapters[slot]->i_applyDefaults(osType);
15695
15696 /* Apply serial port defaults */
15697 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15698 mSerialPorts[slot]->i_applyDefaults(osType);
15699
15700 /* Apply parallel port defaults - not OS dependent*/
15701 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15702 mParallelPorts[slot]->i_applyDefaults();
15703
15704 /* This one covers the TPM type. */
15705 mTrustedPlatformModule->i_applyDefaults(osType);
15706
15707 /* This one covers secure boot. */
15708 rc = mNvramStore->i_applyDefaults(osType);
15709 if (FAILED(rc)) return rc;
15710
15711 /* Audio stuff. */
15712 AudioControllerType_T audioController;
15713 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15714 if (FAILED(rc)) return rc;
15715
15716 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15717 if (FAILED(rc)) return rc;
15718
15719 AudioCodecType_T audioCodec;
15720 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15721 if (FAILED(rc)) return rc;
15722
15723 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15724 if (FAILED(rc)) return rc;
15725
15726 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15727 if (FAILED(rc)) return rc;
15728
15729 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15730 if (FAILED(rc)) return rc;
15731
15732 /* Storage Controllers */
15733 StorageControllerType_T hdStorageControllerType;
15734 StorageBus_T hdStorageBusType;
15735 StorageControllerType_T dvdStorageControllerType;
15736 StorageBus_T dvdStorageBusType;
15737 BOOL recommendedFloppy;
15738 ComPtr<IStorageController> floppyController;
15739 ComPtr<IStorageController> hdController;
15740 ComPtr<IStorageController> dvdController;
15741 Utf8Str strFloppyName, strDVDName, strHDName;
15742
15743 /* GUI auto generates controller names using bus type. Do the same*/
15744 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15745
15746 /* Floppy recommended? add one. */
15747 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15748 if (FAILED(rc)) return rc;
15749 if (recommendedFloppy)
15750 {
15751 rc = addStorageController(strFloppyName,
15752 StorageBus_Floppy,
15753 floppyController);
15754 if (FAILED(rc)) return rc;
15755 }
15756
15757 /* Setup one DVD storage controller. */
15758 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15759 if (FAILED(rc)) return rc;
15760
15761 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15762 if (FAILED(rc)) return rc;
15763
15764 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15765
15766 rc = addStorageController(strDVDName,
15767 dvdStorageBusType,
15768 dvdController);
15769 if (FAILED(rc)) return rc;
15770
15771 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15772 if (FAILED(rc)) return rc;
15773
15774 /* Setup one HDD storage controller. */
15775 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15776 if (FAILED(rc)) return rc;
15777
15778 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15779 if (FAILED(rc)) return rc;
15780
15781 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15782
15783 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15784 {
15785 rc = addStorageController(strHDName,
15786 hdStorageBusType,
15787 hdController);
15788 if (FAILED(rc)) return rc;
15789
15790 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15791 if (FAILED(rc)) return rc;
15792 }
15793 else
15794 {
15795 /* The HD controller is the same as DVD: */
15796 hdController = dvdController;
15797 }
15798
15799 /* Limit the AHCI port count if it's used because windows has trouble with
15800 * too many ports and other guest (OS X in particular) may take extra long
15801 * boot: */
15802
15803 // pParent = static_cast<Medium*>(aP)
15804 IStorageController *temp = hdController;
15805 ComObjPtr<StorageController> storageController;
15806 storageController = static_cast<StorageController *>(temp);
15807
15808 // tempHDController = aHDController;
15809 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15810 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15811 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15812 storageController->COMSETTER(PortCount)(1);
15813
15814 /* USB stuff */
15815
15816 bool ohciEnabled = false;
15817
15818 ComPtr<IUSBController> usbController;
15819 BOOL recommendedUSB3;
15820 BOOL recommendedUSB;
15821 BOOL usbProxyAvailable;
15822
15823 getUSBProxyAvailable(&usbProxyAvailable);
15824 if (FAILED(rc)) return rc;
15825
15826 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15827 if (FAILED(rc)) return rc;
15828 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15829 if (FAILED(rc)) return rc;
15830
15831 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15832 {
15833#ifdef VBOX_WITH_EXTPACK
15834 /* USB 3.0 is only available if the proper ExtPack is installed. */
15835 ExtPackManager *aManager = mParent->i_getExtPackManager();
15836 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15837 {
15838 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15839 if (FAILED(rc)) return rc;
15840
15841 /* xHci includes OHCI */
15842 ohciEnabled = true;
15843 }
15844#endif
15845 }
15846 if ( !ohciEnabled
15847 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15848 {
15849 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15850 if (FAILED(rc)) return rc;
15851 ohciEnabled = true;
15852
15853#ifdef VBOX_WITH_EXTPACK
15854 /* USB 2.0 is only available if the proper ExtPack is installed.
15855 * Note. Configuring EHCI here and providing messages about
15856 * the missing extpack isn't exactly clean, but it is a
15857 * necessary evil to patch over legacy compatability issues
15858 * introduced by the new distribution model. */
15859 ExtPackManager *manager = mParent->i_getExtPackManager();
15860 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15861 {
15862 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15863 if (FAILED(rc)) return rc;
15864 }
15865#endif
15866 }
15867
15868 /* Set recommended human interface device types: */
15869 BOOL recommendedUSBHID;
15870 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15871 if (FAILED(rc)) return rc;
15872
15873 if (recommendedUSBHID)
15874 {
15875 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15876 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15877 if (!ohciEnabled && !usbDeviceFilters.isNull())
15878 {
15879 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15880 if (FAILED(rc)) return rc;
15881 }
15882 }
15883
15884 BOOL recommendedUSBTablet;
15885 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15886 if (FAILED(rc)) return rc;
15887
15888 if (recommendedUSBTablet)
15889 {
15890 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15891 if (!ohciEnabled && !usbDeviceFilters.isNull())
15892 {
15893 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15894 if (FAILED(rc)) return rc;
15895 }
15896 }
15897
15898 /* Enable the VMMDev testing feature for bootsector VMs: */
15899 if (osTypeId == "VBoxBS_64")
15900 {
15901 rc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
15902 if (FAILED(rc))
15903 return rc;
15904 }
15905
15906 return S_OK;
15907}
15908
15909HRESULT Machine::changeEncryption(const com::Utf8Str &aCurrentPassword,
15910 const com::Utf8Str &aCipher,
15911 const com::Utf8Str &aNewPassword,
15912 const com::Utf8Str &aNewPasswordId,
15913 BOOL aForce,
15914 ComPtr<IProgress> &aProgress)
15915{
15916 LogFlowFuncEnter();
15917
15918#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
15919 RT_NOREF(aCurrentPassword, aCipher, aNewPassword, aNewPasswordId, aForce, aProgress);
15920 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
15921#else
15922 RT_NOREF(aCurrentPassword, aCipher, aNewPassword, aNewPasswordId, aForce, aProgress);
15923 /** @todo */
15924 return E_NOTIMPL;
15925#endif
15926}
15927
15928HRESULT Machine::getEncryptionSettings(com::Utf8Str &aCipher,
15929 com::Utf8Str &aPasswordId)
15930{
15931#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
15932 RT_NOREF(aCipher, aPasswordId);
15933 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
15934#else
15935 AutoLimitedCaller autoCaller(this);
15936 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15937
15938 PCVBOXCRYPTOIF pCryptoIf = NULL;
15939 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
15940 if (FAILED(hrc)) return hrc; /* Error is set */
15941
15942 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15943
15944 if (mData->mstrKeyStore.isNotEmpty())
15945 {
15946 char *pszCipher = NULL;
15947 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), NULL /*pszPassword*/,
15948 NULL /*ppbKey*/, NULL /*pcbKey*/, &pszCipher);
15949 if (RT_SUCCESS(vrc))
15950 {
15951 aCipher = getCipherStringWithoutMode(pszCipher);
15952 RTStrFree(pszCipher);
15953 aPasswordId = mData->mstrKeyId;
15954 }
15955 else
15956 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
15957 tr("Failed to query the encryption settings with %Rrc"),
15958 vrc);
15959 }
15960 else
15961 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
15962
15963 mParent->i_releaseCryptoIf(pCryptoIf);
15964
15965 return hrc;
15966#endif
15967}
15968
15969HRESULT Machine::checkEncryptionPassword(const com::Utf8Str &aPassword)
15970{
15971#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
15972 RT_NOREF(aPassword);
15973 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
15974#else
15975 AutoLimitedCaller autoCaller(this);
15976 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15977
15978 PCVBOXCRYPTOIF pCryptoIf = NULL;
15979 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
15980 if (FAILED(hrc)) return hrc; /* Error is set */
15981
15982 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15983
15984 if (mData->mstrKeyStore.isNotEmpty())
15985 {
15986 char *pszCipher = NULL;
15987 uint8_t *pbDek = NULL;
15988 size_t cbDek = 0;
15989 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), aPassword.c_str(),
15990 &pbDek, &cbDek, &pszCipher);
15991 if (RT_SUCCESS(vrc))
15992 {
15993 RTStrFree(pszCipher);
15994 RTMemSaferFree(pbDek, cbDek);
15995 }
15996 else
15997 hrc = setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc,
15998 tr("The password supplied for the encrypted machine is incorrect"));
15999 }
16000 else
16001 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16002
16003 mParent->i_releaseCryptoIf(pCryptoIf);
16004
16005 return hrc;
16006#endif
16007}
16008
16009HRESULT Machine::addEncryptionPassword(const com::Utf8Str &aId,
16010 const com::Utf8Str &aPassword)
16011{
16012#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16013 RT_NOREF(aId, aPassword);
16014 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16015#else
16016 AutoLimitedCaller autoCaller(this);
16017 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16018
16019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16020
16021 size_t cbPassword = aPassword.length() + 1;
16022 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
16023
16024 mData->mpKeyStore->addSecretKey(aId, pbPassword, cbPassword);
16025
16026 if ( mData->mAccessible
16027 && mData->mSession.mState == SessionState_Locked
16028 && mData->mSession.mLockType == LockType_VM
16029 && mData->mSession.mDirectControl != NULL)
16030 {
16031 /* get the console from the direct session */
16032 ComPtr<IConsole> console;
16033 HRESULT rc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16034 ComAssertComRC(rc);
16035 /* send passsword to console */
16036 console->AddEncryptionPassword(Bstr(aId).raw(),
16037 Bstr(aPassword).raw(),
16038 TRUE);
16039 }
16040
16041 if (mData->mstrKeyId == aId)
16042 {
16043 HRESULT hrc = checkEncryptionPassword(aPassword);
16044 if (FAILED(hrc))
16045 return hrc;
16046
16047 if (SUCCEEDED(hrc))
16048 {
16049 /*
16050 * Encryption is used and password is correct,
16051 * Reinit the machine if required.
16052 */
16053 BOOL fAccessible;
16054 alock.release();
16055 getAccessible(&fAccessible);
16056 alock.acquire();
16057 }
16058 }
16059
16060 /*
16061 * Add the password into the NvramStore only after
16062 * the machine becomes accessible and the NvramStore
16063 * contains key id and key store.
16064 */
16065 if (mNvramStore.isNotNull())
16066 mNvramStore->i_addPassword(aId, aPassword);
16067
16068 return S_OK;
16069#endif
16070}
16071
16072HRESULT Machine::addEncryptionPasswords(const std::vector<com::Utf8Str> &aIds,
16073 const std::vector<com::Utf8Str> &aPasswords)
16074{
16075#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16076 RT_NOREF(aIds, aPasswords);
16077 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16078#else
16079 if (aIds.size() != aPasswords.size())
16080 return setError(E_INVALIDARG, tr("Id and passwords arrays must have the same size"));
16081
16082 HRESULT hrc = S_OK;
16083 for (size_t i = 0; i < aIds.size() && SUCCEEDED(hrc); ++i)
16084 hrc = addEncryptionPassword(aIds[i], aPasswords[i]);
16085
16086 return hrc;
16087#endif
16088}
16089
16090HRESULT Machine::removeEncryptionPassword(AutoCaller &autoCaller, const com::Utf8Str &aId)
16091{
16092#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16093 RT_NOREF(autoCaller, aId);
16094 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16095#else
16096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16097
16098 if ( mData->mAccessible
16099 && mData->mSession.mState == SessionState_Locked
16100 && mData->mSession.mLockType == LockType_VM
16101 && mData->mSession.mDirectControl != NULL)
16102 {
16103 /* get the console from the direct session */
16104 ComPtr<IConsole> console;
16105 HRESULT rc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16106 ComAssertComRC(rc);
16107 /* send passsword to console */
16108 console->RemoveEncryptionPassword(Bstr(aId).raw());
16109 }
16110
16111 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty() && mData->mstrKeyId == aId)
16112 {
16113 if (Global::IsOnlineOrTransient(mData->mMachineState))
16114 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
16115 alock.release();
16116 autoCaller.release();
16117 /* return because all passwords are purged when machine becomes inaccessible; */
16118 return i_setInaccessible();
16119 }
16120
16121 if (mNvramStore.isNotNull())
16122 mNvramStore->i_removePassword(aId);
16123 mData->mpKeyStore->deleteSecretKey(aId);
16124 return S_OK;
16125#endif
16126}
16127
16128HRESULT Machine::clearAllEncryptionPasswords(AutoCaller &autoCaller)
16129{
16130#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16131 RT_NOREF(autoCaller);
16132 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16133#else
16134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16135
16136 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty())
16137 {
16138 if (Global::IsOnlineOrTransient(mData->mMachineState))
16139 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
16140 alock.release();
16141 autoCaller.release();
16142 /* return because all passwords are purged when machine becomes inaccessible; */
16143 return i_setInaccessible();
16144 }
16145
16146 mNvramStore->i_removeAllPasswords();
16147 mData->mpKeyStore->deleteAllSecretKeys(false, true);
16148 return S_OK;
16149#endif
16150}
16151
16152#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
16153HRESULT Machine::i_setInaccessible()
16154{
16155 if (!mData->mAccessible)
16156 return S_OK;
16157
16158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16159 VirtualBox *pParent = mParent;
16160 com::Utf8Str strConfigFile = mData->m_strConfigFile;
16161 Guid id(i_getId());
16162
16163 alock.release();
16164
16165 uninit();
16166 HRESULT rc = initFromSettings(pParent, strConfigFile, &id, com::Utf8Str());
16167
16168 alock.acquire();
16169 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
16170 return rc;
16171}
16172#endif
16173
16174/* This isn't handled entirely by the wrapper generator yet. */
16175#ifdef VBOX_WITH_XPCOM
16176NS_DECL_CLASSINFO(SessionMachine)
16177NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
16178
16179NS_DECL_CLASSINFO(SnapshotMachine)
16180NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
16181#endif
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette