VirtualBox

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

Last change on this file since 95556 was 95556, checked in by vboxsync, 3 years ago

fixed VERR_PDM_MEDIA_LOCKED when inserting already mounted medium bugref:9687

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 599.2 KB
Line 
1/* $Id: MachineImpl.cpp 95556 2022-07-07 18:48:44Z 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.215389.xyz. 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); RT_NOREF(hrc2);
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#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
532 rc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
533#endif
534 if (SUCCEEDED(rc))
535 {
536 /* At this point the changing of the current state modification
537 * flag is allowed. */
538 i_allowStateModification();
539
540 /* commit all changes made during the initialization */
541 i_commit();
542 }
543 }
544
545 /* Confirm a successful initialization when it's the case */
546 if (SUCCEEDED(rc))
547 {
548#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
549 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
550 {
551 size_t cbPassword = aPassword.length() + 1;
552 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
553 mData->mpKeyStore->addSecretKey(aPasswordId, pbPassword, cbPassword);
554 }
555#endif
556
557 if (mData->mAccessible)
558 autoInitSpan.setSucceeded();
559 else
560 autoInitSpan.setLimited();
561 }
562
563 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
564 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
565 mData->mRegistered,
566 mData->mAccessible,
567 rc));
568
569 LogFlowThisFuncLeave();
570
571 return rc;
572}
573
574/**
575 * Initializes a new instance with data from machine XML (formerly Init_Registered).
576 * Gets called in two modes:
577 *
578 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
579 * UUID is specified and we mark the machine as "registered";
580 *
581 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
582 * and the machine remains unregistered until RegisterMachine() is called.
583 *
584 * @param aParent Associated parent object
585 * @param strConfigFile Local file system path to the VM settings file (can
586 * be relative to the VirtualBox config directory).
587 * @param aId UUID of the machine or NULL (see above).
588 * @param strPassword Password for decrypting the config
589 *
590 * @return Success indicator. if not S_OK, the machine object is invalid
591 */
592HRESULT Machine::initFromSettings(VirtualBox *aParent,
593 const Utf8Str &strConfigFile,
594 const Guid *aId,
595 const com::Utf8Str &strPassword)
596{
597 LogFlowThisFuncEnter();
598 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
599
600 PCVBOXCRYPTOIF pCryptoIf = NULL;
601#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
602 if (strPassword.isNotEmpty())
603 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
604#else
605 if (strPassword.isNotEmpty())
606 {
607 /* Get at the crpytographic interface. */
608 HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
609 if (FAILED(hrc))
610 return hrc; /* Error is set. */
611 }
612#endif
613
614 /* Enclose the state transition NotReady->InInit->Ready */
615 AutoInitSpan autoInitSpan(this);
616 AssertReturn(autoInitSpan.isOk(), E_FAIL);
617
618 HRESULT rc = initImpl(aParent, strConfigFile);
619 if (FAILED(rc)) return rc;
620
621 if (aId)
622 {
623 // loading a registered VM:
624 unconst(mData->mUuid) = *aId;
625 mData->mRegistered = TRUE;
626 // now load the settings from XML:
627 rc = i_registeredInit();
628 // this calls initDataAndChildObjects() and loadSettings()
629 }
630 else
631 {
632 // opening an unregistered VM (VirtualBox::OpenMachine()):
633 rc = initDataAndChildObjects();
634
635 if (SUCCEEDED(rc))
636 {
637 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
638 mData->mAccessible = TRUE;
639
640 try
641 {
642 // load and parse machine XML; this will throw on XML or logic errors
643 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
644 pCryptoIf,
645 strPassword.c_str());
646
647 // reject VM UUID duplicates, they can happen if someone
648 // tries to register an already known VM config again
649 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
650 true /* fPermitInaccessible */,
651 false /* aDoSetError */,
652 NULL) != VBOX_E_OBJECT_NOT_FOUND)
653 {
654 throw setError(E_FAIL,
655 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
656 mData->m_strConfigFile.c_str());
657 }
658
659 // use UUID from machine config
660 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
661
662#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
663 // No exception is thrown if config is encrypted, allowing us to get the uuid and the encryption fields.
664 // We fill in the encryptions fields, and the rest will be filled in if all data parsed.
665 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
666 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
667#endif
668
669 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
670 {
671 // We just set the inaccessible state and fill the error info allowing the caller
672 // to register the machine with encrypted config even if the password is incorrect
673 mData->mAccessible = FALSE;
674
675 /* fetch the current error info */
676 mData->mAccessError = com::ErrorInfo();
677
678 setError(VBOX_E_PASSWORD_INCORRECT,
679 tr("Decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
680 mData->pMachineConfigFile->uuid.raw());
681 }
682 else
683 {
684#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
685 if (strPassword.isNotEmpty())
686 {
687 size_t cbKey = strPassword.length() + 1; /* Include terminator */
688 const uint8_t *pbKey = (const uint8_t *)strPassword.c_str();
689 mData->mpKeyStore->addSecretKey(mData->mstrKeyId, pbKey, cbKey);
690 }
691#endif
692
693 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
694 NULL /* puuidRegistry */);
695 if (FAILED(rc)) throw rc;
696
697 /* At this point the changing of the current state modification
698 * flag is allowed. */
699 i_allowStateModification();
700
701 i_commit();
702 }
703 }
704 catch (HRESULT err)
705 {
706 /* we assume that error info is set by the thrower */
707 rc = err;
708 }
709 catch (...)
710 {
711 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
712 }
713 }
714 }
715
716 /* Confirm a successful initialization when it's the case */
717 if (SUCCEEDED(rc))
718 {
719 if (mData->mAccessible)
720 autoInitSpan.setSucceeded();
721 else
722 {
723 autoInitSpan.setLimited();
724
725 // uninit media from this machine's media registry, or else
726 // reloading the settings will fail
727 mParent->i_unregisterMachineMedia(i_getId());
728 }
729 }
730
731#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
732 if (pCryptoIf)
733 {
734 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
735 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
736 }
737#endif
738
739 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
740 "rc=%08X\n",
741 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
742 mData->mRegistered, mData->mAccessible, rc));
743
744 LogFlowThisFuncLeave();
745
746 return rc;
747}
748
749/**
750 * Initializes a new instance from a machine config that is already in memory
751 * (import OVF case). Since we are importing, the UUID in the machine
752 * config is ignored and we always generate a fresh one.
753 *
754 * @param aParent Associated parent object.
755 * @param strName Name for the new machine; this overrides what is specified in config.
756 * @param strSettingsFilename File name of .vbox file.
757 * @param config Machine configuration loaded and parsed from XML.
758 *
759 * @return Success indicator. if not S_OK, the machine object is invalid
760 */
761HRESULT Machine::init(VirtualBox *aParent,
762 const Utf8Str &strName,
763 const Utf8Str &strSettingsFilename,
764 const settings::MachineConfigFile &config)
765{
766 LogFlowThisFuncEnter();
767
768 /* Enclose the state transition NotReady->InInit->Ready */
769 AutoInitSpan autoInitSpan(this);
770 AssertReturn(autoInitSpan.isOk(), E_FAIL);
771
772 HRESULT rc = initImpl(aParent, strSettingsFilename);
773 if (FAILED(rc)) return rc;
774
775 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
776 if (FAILED(rc)) return rc;
777
778 rc = initDataAndChildObjects();
779
780 if (SUCCEEDED(rc))
781 {
782 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
783 mData->mAccessible = TRUE;
784
785 // create empty machine config for instance data
786 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
787
788 // generate fresh UUID, ignore machine config
789 unconst(mData->mUuid).create();
790
791 rc = i_loadMachineDataFromSettings(config,
792 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
793
794 // override VM name as well, it may be different
795 mUserData->s.strName = strName;
796
797 if (SUCCEEDED(rc))
798 {
799 /* At this point the changing of the current state modification
800 * flag is allowed. */
801 i_allowStateModification();
802
803 /* commit all changes made during the initialization */
804 i_commit();
805 }
806 }
807
808 /* Confirm a successful initialization when it's the case */
809 if (SUCCEEDED(rc))
810 {
811 if (mData->mAccessible)
812 autoInitSpan.setSucceeded();
813 else
814 {
815 /* Ignore all errors from unregistering, they would destroy
816- * the more interesting error information we already have,
817- * pinpointing the issue with the VM config. */
818 ErrorInfoKeeper eik;
819
820 autoInitSpan.setLimited();
821
822 // uninit media from this machine's media registry, or else
823 // reloading the settings will fail
824 mParent->i_unregisterMachineMedia(i_getId());
825 }
826 }
827
828 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
829 "rc=%08X\n",
830 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
831 mData->mRegistered, mData->mAccessible, rc));
832
833 LogFlowThisFuncLeave();
834
835 return rc;
836}
837
838/**
839 * Shared code between the various init() implementations.
840 * @param aParent The VirtualBox object.
841 * @param strConfigFile Settings file.
842 * @return
843 */
844HRESULT Machine::initImpl(VirtualBox *aParent,
845 const Utf8Str &strConfigFile)
846{
847 LogFlowThisFuncEnter();
848
849 AssertReturn(aParent, E_INVALIDARG);
850 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
851
852 HRESULT rc = S_OK;
853
854 /* share the parent weakly */
855 unconst(mParent) = aParent;
856
857 /* allocate the essential machine data structure (the rest will be
858 * allocated later by initDataAndChildObjects() */
859 mData.allocate();
860
861 /* memorize the config file name (as provided) */
862 mData->m_strConfigFile = strConfigFile;
863
864 /* get the full file name */
865 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
866 if (RT_FAILURE(vrc1))
867 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
868 tr("Invalid machine settings file name '%s' (%Rrc)"),
869 strConfigFile.c_str(),
870 vrc1);
871
872#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
873 /** @todo Only create when the machine is going to be encrypted. */
874 /* Non-pageable memory is not accessible for non-VM process */
875 mData->mpKeyStore = new SecretKeyStore(false /* fKeyBufNonPageable */);
876 AssertReturn(mData->mpKeyStore, E_OUTOFMEMORY);
877#endif
878
879 LogFlowThisFuncLeave();
880
881 return rc;
882}
883
884/**
885 * Tries to create a machine settings file in the path stored in the machine
886 * instance data. Used when a new machine is created to fail gracefully if
887 * the settings file could not be written (e.g. because machine dir is read-only).
888 * @return
889 */
890HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
891{
892 HRESULT rc = S_OK;
893
894 // when we create a new machine, we must be able to create the settings file
895 RTFILE f = NIL_RTFILE;
896 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
897 if ( RT_SUCCESS(vrc)
898 || vrc == VERR_SHARING_VIOLATION
899 )
900 {
901 if (RT_SUCCESS(vrc))
902 RTFileClose(f);
903 if (!fForceOverwrite)
904 rc = setError(VBOX_E_FILE_ERROR,
905 tr("Machine settings file '%s' already exists"),
906 mData->m_strConfigFileFull.c_str());
907 else
908 {
909 /* try to delete the config file, as otherwise the creation
910 * of a new settings file will fail. */
911 i_deleteFile(mData->m_strConfigFileFull.c_str(), false /* fIgnoreFailures */, tr("existing settings file"));
912 }
913 }
914 else if ( vrc != VERR_FILE_NOT_FOUND
915 && vrc != VERR_PATH_NOT_FOUND
916 )
917 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
918 tr("Invalid machine settings file name '%s' (%Rrc)"),
919 mData->m_strConfigFileFull.c_str(),
920 vrc);
921 return rc;
922}
923
924/**
925 * Initializes the registered machine by loading the settings file.
926 * This method is separated from #init() in order to make it possible to
927 * retry the operation after VirtualBox startup instead of refusing to
928 * startup the whole VirtualBox server in case if the settings file of some
929 * registered VM is invalid or inaccessible.
930 *
931 * @note Must be always called from this object's write lock
932 * (unless called from #init() that doesn't need any locking).
933 * @note Locks the mUSBController method for writing.
934 * @note Subclasses must not call this method.
935 */
936HRESULT Machine::i_registeredInit()
937{
938 AssertReturn(!i_isSessionMachine(), E_FAIL);
939 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
940 AssertReturn(mData->mUuid.isValid(), E_FAIL);
941 AssertReturn(!mData->mAccessible, E_FAIL);
942
943 HRESULT rc = initDataAndChildObjects();
944
945 if (SUCCEEDED(rc))
946 {
947 /* Temporarily reset the registered flag in order to let setters
948 * potentially called from loadSettings() succeed (isMutable() used in
949 * all setters will return FALSE for a Machine instance if mRegistered
950 * is TRUE). */
951 mData->mRegistered = FALSE;
952
953 PCVBOXCRYPTOIF pCryptoIf = NULL;
954 SecretKey *pKey = NULL;
955 const char *pszPassword = NULL;
956#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
957 /* Resolve password and cryptographic support interface if machine is encrypted. */
958 if (mData->mstrKeyId.isNotEmpty())
959 {
960 /* Get at the crpytographic interface. */
961 rc = mParent->i_retainCryptoIf(&pCryptoIf);
962 if (SUCCEEDED(rc))
963 {
964 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
965 if (RT_SUCCESS(vrc))
966 pszPassword = (const char *)pKey->getKeyBuffer();
967 else
968 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to retain key for key ID '%s' with %Rrc"),
969 mData->mstrKeyId.c_str(), vrc);
970 }
971 }
972#else
973 RT_NOREF(pKey);
974#endif
975
976 if (SUCCEEDED(rc))
977 {
978 try
979 {
980 // load and parse machine XML; this will throw on XML or logic errors
981 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
982 pCryptoIf, pszPassword);
983
984 if (mData->mUuid != mData->pMachineConfigFile->uuid)
985 throw setError(E_FAIL,
986 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
987 mData->pMachineConfigFile->uuid.raw(),
988 mData->m_strConfigFileFull.c_str(),
989 mData->mUuid.toString().c_str(),
990 mParent->i_settingsFilePath().c_str());
991
992#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
993 // If config is encrypted, no exception is thrown allowing us to get the uuid and the encryption fields.
994 // We fill in the encryptions fields, and the rest will be filled in if all data parsed
995 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
996 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
997
998 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
999 rc = setError(VBOX_E_PASSWORD_INCORRECT,
1000 tr("Config decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
1001 mData->pMachineConfigFile->uuid.raw());
1002 else
1003#endif
1004 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
1005 NULL /* const Guid *puuidRegistry */);
1006 if (FAILED(rc)) throw rc;
1007 }
1008 catch (HRESULT err)
1009 {
1010 /* we assume that error info is set by the thrower */
1011 rc = err;
1012 }
1013 catch (...)
1014 {
1015 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
1016 }
1017
1018 /* Restore the registered flag (even on failure) */
1019 mData->mRegistered = TRUE;
1020 }
1021
1022#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1023 if (pCryptoIf)
1024 mParent->i_releaseCryptoIf(pCryptoIf);
1025 if (pKey)
1026 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
1027#endif
1028 }
1029
1030 if (SUCCEEDED(rc))
1031 {
1032 /* Set mAccessible to TRUE only if we successfully locked and loaded
1033 * the settings file */
1034 mData->mAccessible = TRUE;
1035
1036 /* commit all changes made during loading the settings file */
1037 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
1038 /// @todo r=klaus for some reason the settings loading logic backs up
1039 // the settings, and therefore a commit is needed. Should probably be changed.
1040 }
1041 else
1042 {
1043 /* If the machine is registered, then, instead of returning a
1044 * failure, we mark it as inaccessible and set the result to
1045 * success to give it a try later */
1046
1047 /* fetch the current error info */
1048 mData->mAccessError = com::ErrorInfo();
1049 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
1050
1051 /* rollback all changes */
1052 i_rollback(false /* aNotify */);
1053
1054 // uninit media from this machine's media registry, or else
1055 // reloading the settings will fail
1056 mParent->i_unregisterMachineMedia(i_getId());
1057
1058 /* uninitialize the common part to make sure all data is reset to
1059 * default (null) values */
1060 uninitDataAndChildObjects();
1061
1062 rc = S_OK;
1063 }
1064
1065 return rc;
1066}
1067
1068/**
1069 * Uninitializes the instance.
1070 * Called either from FinalRelease() or by the parent when it gets destroyed.
1071 *
1072 * @note The caller of this method must make sure that this object
1073 * a) doesn't have active callers on the current thread and b) is not locked
1074 * by the current thread; otherwise uninit() will hang either a) due to
1075 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
1076 * a dead-lock caused by this thread waiting for all callers on the other
1077 * threads are done but preventing them from doing so by holding a lock.
1078 */
1079void Machine::uninit()
1080{
1081 LogFlowThisFuncEnter();
1082
1083 Assert(!isWriteLockOnCurrentThread());
1084
1085 Assert(!uRegistryNeedsSaving);
1086 if (uRegistryNeedsSaving)
1087 {
1088 AutoCaller autoCaller(this);
1089 if (SUCCEEDED(autoCaller.rc()))
1090 {
1091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1092 i_saveSettings(NULL, alock, Machine::SaveS_Force);
1093 }
1094 }
1095
1096 /* Enclose the state transition Ready->InUninit->NotReady */
1097 AutoUninitSpan autoUninitSpan(this);
1098 if (autoUninitSpan.uninitDone())
1099 return;
1100
1101 Assert(!i_isSnapshotMachine());
1102 Assert(!i_isSessionMachine());
1103 Assert(!!mData);
1104
1105 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
1106 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
1107
1108 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1109
1110 if (!mData->mSession.mMachine.isNull())
1111 {
1112 /* Theoretically, this can only happen if the VirtualBox server has been
1113 * terminated while there were clients running that owned open direct
1114 * sessions. Since in this case we are definitely called by
1115 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
1116 * won't happen on the client watcher thread (because it has a
1117 * VirtualBox caller for the duration of the
1118 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
1119 * cannot happen until the VirtualBox caller is released). This is
1120 * important, because SessionMachine::uninit() cannot correctly operate
1121 * after we return from this method (it expects the Machine instance is
1122 * still valid). We'll call it ourselves below.
1123 */
1124 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
1125 (SessionMachine*)mData->mSession.mMachine));
1126
1127 if (Global::IsOnlineOrTransient(mData->mMachineState))
1128 {
1129 Log1WarningThisFunc(("Setting state to Aborted!\n"));
1130 /* set machine state using SessionMachine reimplementation */
1131 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
1132 }
1133
1134 /*
1135 * Uninitialize SessionMachine using public uninit() to indicate
1136 * an unexpected uninitialization.
1137 */
1138 mData->mSession.mMachine->uninit();
1139 /* SessionMachine::uninit() must set mSession.mMachine to null */
1140 Assert(mData->mSession.mMachine.isNull());
1141 }
1142
1143 // uninit media from this machine's media registry, if they're still there
1144 Guid uuidMachine(i_getId());
1145
1146 /* the lock is no more necessary (SessionMachine is uninitialized) */
1147 alock.release();
1148
1149 /* XXX This will fail with
1150 * "cannot be closed because it is still attached to 1 virtual machines"
1151 * because at this point we did not call uninitDataAndChildObjects() yet
1152 * and therefore also removeBackReference() for all these mediums was not called! */
1153
1154 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
1155 mParent->i_unregisterMachineMedia(uuidMachine);
1156
1157 // has machine been modified?
1158 if (mData->flModifications)
1159 {
1160 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
1161 i_rollback(false /* aNotify */);
1162 }
1163
1164 if (mData->mAccessible)
1165 uninitDataAndChildObjects();
1166
1167#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1168 if (mData->mpKeyStore != NULL)
1169 delete mData->mpKeyStore;
1170#endif
1171
1172 /* free the essential data structure last */
1173 mData.free();
1174
1175 LogFlowThisFuncLeave();
1176}
1177
1178// Wrapped IMachine properties
1179/////////////////////////////////////////////////////////////////////////////
1180HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
1181{
1182 /* mParent is constant during life time, no need to lock */
1183 ComObjPtr<VirtualBox> pVirtualBox(mParent);
1184 aParent = pVirtualBox;
1185
1186 return S_OK;
1187}
1188
1189
1190HRESULT Machine::getAccessible(BOOL *aAccessible)
1191{
1192 /* In some cases (medium registry related), it is necessary to be able to
1193 * go through the list of all machines. Happens when an inaccessible VM
1194 * has a sensible medium registry. */
1195 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
1196 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1197
1198 HRESULT rc = S_OK;
1199
1200 if (!mData->mAccessible)
1201 {
1202 /* try to initialize the VM once more if not accessible */
1203
1204 AutoReinitSpan autoReinitSpan(this);
1205 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
1206
1207#ifdef DEBUG
1208 LogFlowThisFunc(("Dumping media backreferences\n"));
1209 mParent->i_dumpAllBackRefs();
1210#endif
1211
1212 if (mData->pMachineConfigFile)
1213 {
1214 // reset the XML file to force loadSettings() (called from i_registeredInit())
1215 // to parse it again; the file might have changed
1216 delete mData->pMachineConfigFile;
1217 mData->pMachineConfigFile = NULL;
1218 }
1219
1220 rc = i_registeredInit();
1221
1222 if (SUCCEEDED(rc) && mData->mAccessible)
1223 {
1224 autoReinitSpan.setSucceeded();
1225
1226 /* make sure interesting parties will notice the accessibility
1227 * state change */
1228 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
1229 mParent->i_onMachineDataChanged(mData->mUuid);
1230 }
1231 }
1232
1233 if (SUCCEEDED(rc))
1234 *aAccessible = mData->mAccessible;
1235
1236 LogFlowThisFuncLeave();
1237
1238 return rc;
1239}
1240
1241HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
1242{
1243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1244
1245 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
1246 {
1247 /* return shortly */
1248 aAccessError = NULL;
1249 return S_OK;
1250 }
1251
1252 HRESULT rc = S_OK;
1253
1254 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1255 rc = errorInfo.createObject();
1256 if (SUCCEEDED(rc))
1257 {
1258 errorInfo->init(mData->mAccessError.getResultCode(),
1259 mData->mAccessError.getInterfaceID().ref(),
1260 Utf8Str(mData->mAccessError.getComponent()).c_str(),
1261 Utf8Str(mData->mAccessError.getText()));
1262 aAccessError = errorInfo;
1263 }
1264
1265 return rc;
1266}
1267
1268HRESULT Machine::getName(com::Utf8Str &aName)
1269{
1270 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1271
1272 aName = mUserData->s.strName;
1273
1274 return S_OK;
1275}
1276
1277HRESULT Machine::setName(const com::Utf8Str &aName)
1278{
1279 // prohibit setting a UUID only as the machine name, or else it can
1280 // never be found by findMachine()
1281 Guid test(aName);
1282
1283 if (test.isValid())
1284 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1285
1286 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1287
1288 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1289 if (FAILED(rc)) return rc;
1290
1291 i_setModified(IsModified_MachineData);
1292 mUserData.backup();
1293 mUserData->s.strName = aName;
1294
1295 return S_OK;
1296}
1297
1298HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1299{
1300 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1301
1302 aDescription = mUserData->s.strDescription;
1303
1304 return S_OK;
1305}
1306
1307HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1308{
1309 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1310
1311 // this can be done in principle in any state as it doesn't affect the VM
1312 // significantly, but play safe by not messing around while complex
1313 // activities are going on
1314 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1315 if (FAILED(rc)) return rc;
1316
1317 i_setModified(IsModified_MachineData);
1318 mUserData.backup();
1319 mUserData->s.strDescription = aDescription;
1320
1321 return S_OK;
1322}
1323
1324HRESULT Machine::getId(com::Guid &aId)
1325{
1326 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1327
1328 aId = mData->mUuid;
1329
1330 return S_OK;
1331}
1332
1333HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1334{
1335 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1336 aGroups.resize(mUserData->s.llGroups.size());
1337 size_t i = 0;
1338 for (StringsList::const_iterator
1339 it = mUserData->s.llGroups.begin();
1340 it != mUserData->s.llGroups.end();
1341 ++it, ++i)
1342 aGroups[i] = (*it);
1343
1344 return S_OK;
1345}
1346
1347HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1348{
1349 StringsList llGroups;
1350 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1351 if (FAILED(rc))
1352 return rc;
1353
1354 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1355
1356 rc = i_checkStateDependency(MutableOrSavedStateDep);
1357 if (FAILED(rc)) return rc;
1358
1359 i_setModified(IsModified_MachineData);
1360 mUserData.backup();
1361 mUserData->s.llGroups = llGroups;
1362
1363 return S_OK;
1364}
1365
1366HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1367{
1368 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1369
1370 aOSTypeId = mUserData->s.strOsType;
1371
1372 return S_OK;
1373}
1374
1375HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1376{
1377 /* look up the object by Id to check it is valid */
1378 ComObjPtr<GuestOSType> pGuestOSType;
1379 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1380
1381 /* when setting, always use the "etalon" value for consistency -- lookup
1382 * by ID is case-insensitive and the input value may have different case */
1383 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1384
1385 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1386
1387 HRESULT rc = i_checkStateDependency(MutableStateDep);
1388 if (FAILED(rc)) return rc;
1389
1390 i_setModified(IsModified_MachineData);
1391 mUserData.backup();
1392 mUserData->s.strOsType = osTypeId;
1393
1394 return S_OK;
1395}
1396
1397HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1398{
1399 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1400
1401 *aFirmwareType = mHWData->mFirmwareType;
1402
1403 return S_OK;
1404}
1405
1406HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1407{
1408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1409
1410 HRESULT rc = i_checkStateDependency(MutableStateDep);
1411 if (FAILED(rc)) return rc;
1412
1413 i_setModified(IsModified_MachineData);
1414 mHWData.backup();
1415 mHWData->mFirmwareType = aFirmwareType;
1416 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1417 alock.release();
1418
1419 mNvramStore->i_updateNonVolatileStorageFile(strNVRAM);
1420
1421 return S_OK;
1422}
1423
1424HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1425{
1426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1427
1428 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1429
1430 return S_OK;
1431}
1432
1433HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1434{
1435 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1436
1437 HRESULT rc = i_checkStateDependency(MutableStateDep);
1438 if (FAILED(rc)) return rc;
1439
1440 i_setModified(IsModified_MachineData);
1441 mHWData.backup();
1442 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1443
1444 return S_OK;
1445}
1446
1447HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1448{
1449 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1450
1451 *aPointingHIDType = mHWData->mPointingHIDType;
1452
1453 return S_OK;
1454}
1455
1456HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1457{
1458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1459
1460 HRESULT rc = i_checkStateDependency(MutableStateDep);
1461 if (FAILED(rc)) return rc;
1462
1463 i_setModified(IsModified_MachineData);
1464 mHWData.backup();
1465 mHWData->mPointingHIDType = aPointingHIDType;
1466
1467 return S_OK;
1468}
1469
1470HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1471{
1472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1473
1474 *aChipsetType = mHWData->mChipsetType;
1475
1476 return S_OK;
1477}
1478
1479HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1480{
1481 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1482
1483 HRESULT rc = i_checkStateDependency(MutableStateDep);
1484 if (FAILED(rc)) return rc;
1485
1486 if (aChipsetType != mHWData->mChipsetType)
1487 {
1488 i_setModified(IsModified_MachineData);
1489 mHWData.backup();
1490 mHWData->mChipsetType = aChipsetType;
1491
1492 // Resize network adapter array, to be finalized on commit/rollback.
1493 // We must not throw away entries yet, otherwise settings are lost
1494 // without a way to roll back.
1495 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1496 size_t oldCount = mNetworkAdapters.size();
1497 if (newCount > oldCount)
1498 {
1499 mNetworkAdapters.resize(newCount);
1500 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1501 {
1502 unconst(mNetworkAdapters[slot]).createObject();
1503 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1504 }
1505 }
1506 }
1507
1508 return S_OK;
1509}
1510
1511HRESULT Machine::getIommuType(IommuType_T *aIommuType)
1512{
1513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1514
1515 *aIommuType = mHWData->mIommuType;
1516
1517 return S_OK;
1518}
1519
1520HRESULT Machine::setIommuType(IommuType_T aIommuType)
1521{
1522 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1523
1524 HRESULT rc = i_checkStateDependency(MutableStateDep);
1525 if (FAILED(rc)) return rc;
1526
1527 if (aIommuType != mHWData->mIommuType)
1528 {
1529 if (aIommuType == IommuType_Intel)
1530 {
1531#ifndef VBOX_WITH_IOMMU_INTEL
1532 LogRelFunc(("Setting Intel IOMMU when Intel IOMMU support not available!\n"));
1533 return E_UNEXPECTED;
1534#endif
1535 }
1536
1537 i_setModified(IsModified_MachineData);
1538 mHWData.backup();
1539 mHWData->mIommuType = aIommuType;
1540 }
1541
1542 return S_OK;
1543}
1544
1545HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1546{
1547 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1548
1549 aParavirtDebug = mHWData->mParavirtDebug;
1550 return S_OK;
1551}
1552
1553HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1554{
1555 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1556
1557 HRESULT rc = i_checkStateDependency(MutableStateDep);
1558 if (FAILED(rc)) return rc;
1559
1560 /** @todo Parse/validate options? */
1561 if (aParavirtDebug != mHWData->mParavirtDebug)
1562 {
1563 i_setModified(IsModified_MachineData);
1564 mHWData.backup();
1565 mHWData->mParavirtDebug = aParavirtDebug;
1566 }
1567
1568 return S_OK;
1569}
1570
1571HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1572{
1573 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1574
1575 *aParavirtProvider = mHWData->mParavirtProvider;
1576
1577 return S_OK;
1578}
1579
1580HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1581{
1582 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1583
1584 HRESULT rc = i_checkStateDependency(MutableStateDep);
1585 if (FAILED(rc)) return rc;
1586
1587 if (aParavirtProvider != mHWData->mParavirtProvider)
1588 {
1589 i_setModified(IsModified_MachineData);
1590 mHWData.backup();
1591 mHWData->mParavirtProvider = aParavirtProvider;
1592 }
1593
1594 return S_OK;
1595}
1596
1597HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1598{
1599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1600
1601 *aParavirtProvider = mHWData->mParavirtProvider;
1602 switch (mHWData->mParavirtProvider)
1603 {
1604 case ParavirtProvider_None:
1605 case ParavirtProvider_HyperV:
1606 case ParavirtProvider_KVM:
1607 case ParavirtProvider_Minimal:
1608 break;
1609
1610 /* Resolve dynamic provider types to the effective types. */
1611 default:
1612 {
1613 ComObjPtr<GuestOSType> pGuestOSType;
1614 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1615 pGuestOSType);
1616 if (FAILED(hrc2) || pGuestOSType.isNull())
1617 {
1618 *aParavirtProvider = ParavirtProvider_None;
1619 break;
1620 }
1621
1622 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1623 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1624
1625 switch (mHWData->mParavirtProvider)
1626 {
1627 case ParavirtProvider_Legacy:
1628 {
1629 if (fOsXGuest)
1630 *aParavirtProvider = ParavirtProvider_Minimal;
1631 else
1632 *aParavirtProvider = ParavirtProvider_None;
1633 break;
1634 }
1635
1636 case ParavirtProvider_Default:
1637 {
1638 if (fOsXGuest)
1639 *aParavirtProvider = ParavirtProvider_Minimal;
1640 else if ( mUserData->s.strOsType == "Windows11_64"
1641 || mUserData->s.strOsType == "Windows10"
1642 || mUserData->s.strOsType == "Windows10_64"
1643 || mUserData->s.strOsType == "Windows81"
1644 || mUserData->s.strOsType == "Windows81_64"
1645 || mUserData->s.strOsType == "Windows8"
1646 || mUserData->s.strOsType == "Windows8_64"
1647 || mUserData->s.strOsType == "Windows7"
1648 || mUserData->s.strOsType == "Windows7_64"
1649 || mUserData->s.strOsType == "WindowsVista"
1650 || mUserData->s.strOsType == "WindowsVista_64"
1651 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1652 || mUserData->s.strOsType.startsWith("Windows201"))
1653 && mUserData->s.strOsType.endsWith("_64"))
1654 || mUserData->s.strOsType == "Windows2012"
1655 || mUserData->s.strOsType == "Windows2012_64"
1656 || mUserData->s.strOsType == "Windows2008"
1657 || mUserData->s.strOsType == "Windows2008_64")
1658 {
1659 *aParavirtProvider = ParavirtProvider_HyperV;
1660 }
1661 else if (guestTypeFamilyId == "Linux" &&
1662 mUserData->s.strOsType != "Linux22" && // Linux22 and Linux24{_64} excluded as they're too old
1663 mUserData->s.strOsType != "Linux24" && // to have any KVM paravirtualization support.
1664 mUserData->s.strOsType != "Linux24_64")
1665 {
1666 *aParavirtProvider = ParavirtProvider_KVM;
1667 }
1668 else
1669 *aParavirtProvider = ParavirtProvider_None;
1670 break;
1671 }
1672
1673 default: AssertFailedBreak(); /* Shut up MSC. */
1674 }
1675 break;
1676 }
1677 }
1678
1679 Assert( *aParavirtProvider == ParavirtProvider_None
1680 || *aParavirtProvider == ParavirtProvider_Minimal
1681 || *aParavirtProvider == ParavirtProvider_HyperV
1682 || *aParavirtProvider == ParavirtProvider_KVM);
1683 return S_OK;
1684}
1685
1686HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1687{
1688 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1689
1690 aHardwareVersion = mHWData->mHWVersion;
1691
1692 return S_OK;
1693}
1694
1695HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1696{
1697 /* check known version */
1698 Utf8Str hwVersion = aHardwareVersion;
1699 if ( hwVersion.compare("1") != 0
1700 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1701 return setError(E_INVALIDARG,
1702 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1703
1704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1705
1706 HRESULT rc = i_checkStateDependency(MutableStateDep);
1707 if (FAILED(rc)) return rc;
1708
1709 i_setModified(IsModified_MachineData);
1710 mHWData.backup();
1711 mHWData->mHWVersion = aHardwareVersion;
1712
1713 return S_OK;
1714}
1715
1716HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1717{
1718 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1719
1720 if (!mHWData->mHardwareUUID.isZero())
1721 aHardwareUUID = mHWData->mHardwareUUID;
1722 else
1723 aHardwareUUID = mData->mUuid;
1724
1725 return S_OK;
1726}
1727
1728HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1729{
1730 if (!aHardwareUUID.isValid())
1731 return E_INVALIDARG;
1732
1733 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1734
1735 HRESULT rc = i_checkStateDependency(MutableStateDep);
1736 if (FAILED(rc)) return rc;
1737
1738 i_setModified(IsModified_MachineData);
1739 mHWData.backup();
1740 if (aHardwareUUID == mData->mUuid)
1741 mHWData->mHardwareUUID.clear();
1742 else
1743 mHWData->mHardwareUUID = aHardwareUUID;
1744
1745 return S_OK;
1746}
1747
1748HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1749{
1750 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1751
1752 *aMemorySize = mHWData->mMemorySize;
1753
1754 return S_OK;
1755}
1756
1757HRESULT Machine::setMemorySize(ULONG aMemorySize)
1758{
1759 /* check RAM limits */
1760 if ( aMemorySize < MM_RAM_MIN_IN_MB
1761 || aMemorySize > MM_RAM_MAX_IN_MB
1762 )
1763 return setError(E_INVALIDARG,
1764 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1765 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1766
1767 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1768
1769 HRESULT rc = i_checkStateDependency(MutableStateDep);
1770 if (FAILED(rc)) return rc;
1771
1772 i_setModified(IsModified_MachineData);
1773 mHWData.backup();
1774 mHWData->mMemorySize = aMemorySize;
1775
1776 return S_OK;
1777}
1778
1779HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1780{
1781 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1782
1783 *aCPUCount = mHWData->mCPUCount;
1784
1785 return S_OK;
1786}
1787
1788HRESULT Machine::setCPUCount(ULONG aCPUCount)
1789{
1790 /* check CPU limits */
1791 if ( aCPUCount < SchemaDefs::MinCPUCount
1792 || aCPUCount > SchemaDefs::MaxCPUCount
1793 )
1794 return setError(E_INVALIDARG,
1795 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1796 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1797
1798 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1799
1800 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1801 if (mHWData->mCPUHotPlugEnabled)
1802 {
1803 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1804 {
1805 if (mHWData->mCPUAttached[idx])
1806 return setError(E_INVALIDARG,
1807 tr("There is still a CPU attached to socket %lu."
1808 "Detach the CPU before removing the socket"),
1809 aCPUCount, idx+1);
1810 }
1811 }
1812
1813 HRESULT rc = i_checkStateDependency(MutableStateDep);
1814 if (FAILED(rc)) return rc;
1815
1816 i_setModified(IsModified_MachineData);
1817 mHWData.backup();
1818 mHWData->mCPUCount = aCPUCount;
1819
1820 return S_OK;
1821}
1822
1823HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1824{
1825 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1826
1827 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1828
1829 return S_OK;
1830}
1831
1832HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1833{
1834 HRESULT rc = S_OK;
1835
1836 /* check throttle limits */
1837 if ( aCPUExecutionCap < 1
1838 || aCPUExecutionCap > 100
1839 )
1840 return setError(E_INVALIDARG,
1841 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1842 aCPUExecutionCap, 1, 100);
1843
1844 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1845
1846 rc = i_checkStateDependency(MutableOrRunningStateDep);
1847 if (FAILED(rc)) return rc;
1848
1849 alock.release();
1850 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1851 alock.acquire();
1852 if (FAILED(rc)) return rc;
1853
1854 i_setModified(IsModified_MachineData);
1855 mHWData.backup();
1856 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1857
1858 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1859 if (Global::IsOnline(mData->mMachineState))
1860 i_saveSettings(NULL, alock);
1861
1862 return S_OK;
1863}
1864
1865HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1866{
1867 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1868
1869 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1870
1871 return S_OK;
1872}
1873
1874HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1875{
1876 HRESULT rc = S_OK;
1877
1878 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1879
1880 rc = i_checkStateDependency(MutableStateDep);
1881 if (FAILED(rc)) return rc;
1882
1883 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1884 {
1885 if (aCPUHotPlugEnabled)
1886 {
1887 i_setModified(IsModified_MachineData);
1888 mHWData.backup();
1889
1890 /* Add the amount of CPUs currently attached */
1891 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1892 mHWData->mCPUAttached[i] = true;
1893 }
1894 else
1895 {
1896 /*
1897 * We can disable hotplug only if the amount of maximum CPUs is equal
1898 * to the amount of attached CPUs
1899 */
1900 unsigned cCpusAttached = 0;
1901 unsigned iHighestId = 0;
1902
1903 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1904 {
1905 if (mHWData->mCPUAttached[i])
1906 {
1907 cCpusAttached++;
1908 iHighestId = i;
1909 }
1910 }
1911
1912 if ( (cCpusAttached != mHWData->mCPUCount)
1913 || (iHighestId >= mHWData->mCPUCount))
1914 return setError(E_INVALIDARG,
1915 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1916
1917 i_setModified(IsModified_MachineData);
1918 mHWData.backup();
1919 }
1920 }
1921
1922 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1923
1924 return rc;
1925}
1926
1927HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1928{
1929 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1930
1931 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1932
1933 return S_OK;
1934}
1935
1936HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1937{
1938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1939
1940 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1941 if (SUCCEEDED(hrc))
1942 {
1943 i_setModified(IsModified_MachineData);
1944 mHWData.backup();
1945 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1946 }
1947 return hrc;
1948}
1949
1950HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1951{
1952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1953 aCPUProfile = mHWData->mCpuProfile;
1954 return S_OK;
1955}
1956
1957HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1958{
1959 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1960 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1961 if (SUCCEEDED(hrc))
1962 {
1963 i_setModified(IsModified_MachineData);
1964 mHWData.backup();
1965 /* Empty equals 'host'. */
1966 if (aCPUProfile.isNotEmpty())
1967 mHWData->mCpuProfile = aCPUProfile;
1968 else
1969 mHWData->mCpuProfile = "host";
1970 }
1971 return hrc;
1972}
1973
1974HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1975{
1976#ifdef VBOX_WITH_USB_CARDREADER
1977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1978
1979 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1980
1981 return S_OK;
1982#else
1983 NOREF(aEmulatedUSBCardReaderEnabled);
1984 return E_NOTIMPL;
1985#endif
1986}
1987
1988HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1989{
1990#ifdef VBOX_WITH_USB_CARDREADER
1991 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1992
1993 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1994 if (FAILED(rc)) return rc;
1995
1996 i_setModified(IsModified_MachineData);
1997 mHWData.backup();
1998 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1999
2000 return S_OK;
2001#else
2002 NOREF(aEmulatedUSBCardReaderEnabled);
2003 return E_NOTIMPL;
2004#endif
2005}
2006
2007HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
2008{
2009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2010
2011 *aHPETEnabled = mHWData->mHPETEnabled;
2012
2013 return S_OK;
2014}
2015
2016HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
2017{
2018 HRESULT rc = S_OK;
2019
2020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2021
2022 rc = i_checkStateDependency(MutableStateDep);
2023 if (FAILED(rc)) return rc;
2024
2025 i_setModified(IsModified_MachineData);
2026 mHWData.backup();
2027
2028 mHWData->mHPETEnabled = aHPETEnabled;
2029
2030 return rc;
2031}
2032
2033/** @todo this method should not be public */
2034HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2035{
2036 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2037
2038 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2039
2040 return S_OK;
2041}
2042
2043/**
2044 * Set the memory balloon size.
2045 *
2046 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2047 * we have to make sure that we never call IGuest from here.
2048 */
2049HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2050{
2051 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2052#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2053 /* check limits */
2054 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2055 return setError(E_INVALIDARG,
2056 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2057 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2058
2059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2060
2061 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
2062 if (FAILED(rc)) return rc;
2063
2064 i_setModified(IsModified_MachineData);
2065 mHWData.backup();
2066 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2067
2068 return S_OK;
2069#else
2070 NOREF(aMemoryBalloonSize);
2071 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2072#endif
2073}
2074
2075HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2076{
2077 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2078
2079 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2080 return S_OK;
2081}
2082
2083HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2084{
2085#ifdef VBOX_WITH_PAGE_SHARING
2086 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2087
2088 HRESULT rc = i_checkStateDependency(MutableStateDep);
2089 if (FAILED(rc)) return rc;
2090
2091 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2092 i_setModified(IsModified_MachineData);
2093 mHWData.backup();
2094 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2095 return S_OK;
2096#else
2097 NOREF(aPageFusionEnabled);
2098 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2099#endif
2100}
2101
2102HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2103{
2104 /* mBIOSSettings is constant during life time, no need to lock */
2105 aBIOSSettings = mBIOSSettings;
2106
2107 return S_OK;
2108}
2109
2110HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
2111{
2112 /* mTrustedPlatformModule is constant during life time, no need to lock */
2113 aTrustedPlatformModule = mTrustedPlatformModule;
2114
2115 return S_OK;
2116}
2117
2118HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
2119{
2120 /* mNvramStore is constant during life time, no need to lock */
2121 aNvramStore = mNvramStore;
2122
2123 return S_OK;
2124}
2125
2126HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
2127{
2128 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2129
2130 aRecordingSettings = mRecordingSettings;
2131
2132 return S_OK;
2133}
2134
2135HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
2136{
2137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2138
2139 aGraphicsAdapter = mGraphicsAdapter;
2140
2141 return S_OK;
2142}
2143
2144HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2145{
2146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2147
2148 switch (aProperty)
2149 {
2150 case CPUPropertyType_PAE:
2151 *aValue = mHWData->mPAEEnabled;
2152 break;
2153
2154 case CPUPropertyType_LongMode:
2155 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2156 *aValue = TRUE;
2157 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2158 *aValue = FALSE;
2159#if HC_ARCH_BITS == 64
2160 else
2161 *aValue = TRUE;
2162#else
2163 else
2164 {
2165 *aValue = FALSE;
2166
2167 ComObjPtr<GuestOSType> pGuestOSType;
2168 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2169 pGuestOSType);
2170 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
2171 {
2172 if (pGuestOSType->i_is64Bit())
2173 {
2174 ComObjPtr<Host> pHost = mParent->i_host();
2175 alock.release();
2176
2177 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2178 if (FAILED(hrc2))
2179 *aValue = FALSE;
2180 }
2181 }
2182 }
2183#endif
2184 break;
2185
2186 case CPUPropertyType_TripleFaultReset:
2187 *aValue = mHWData->mTripleFaultReset;
2188 break;
2189
2190 case CPUPropertyType_APIC:
2191 *aValue = mHWData->mAPIC;
2192 break;
2193
2194 case CPUPropertyType_X2APIC:
2195 *aValue = mHWData->mX2APIC;
2196 break;
2197
2198 case CPUPropertyType_IBPBOnVMExit:
2199 *aValue = mHWData->mIBPBOnVMExit;
2200 break;
2201
2202 case CPUPropertyType_IBPBOnVMEntry:
2203 *aValue = mHWData->mIBPBOnVMEntry;
2204 break;
2205
2206 case CPUPropertyType_SpecCtrl:
2207 *aValue = mHWData->mSpecCtrl;
2208 break;
2209
2210 case CPUPropertyType_SpecCtrlByHost:
2211 *aValue = mHWData->mSpecCtrlByHost;
2212 break;
2213
2214 case CPUPropertyType_HWVirt:
2215 *aValue = mHWData->mNestedHWVirt;
2216 break;
2217
2218 case CPUPropertyType_L1DFlushOnEMTScheduling:
2219 *aValue = mHWData->mL1DFlushOnSched;
2220 break;
2221
2222 case CPUPropertyType_L1DFlushOnVMEntry:
2223 *aValue = mHWData->mL1DFlushOnVMEntry;
2224 break;
2225
2226 case CPUPropertyType_MDSClearOnEMTScheduling:
2227 *aValue = mHWData->mMDSClearOnSched;
2228 break;
2229
2230 case CPUPropertyType_MDSClearOnVMEntry:
2231 *aValue = mHWData->mMDSClearOnVMEntry;
2232 break;
2233
2234 default:
2235 return E_INVALIDARG;
2236 }
2237 return S_OK;
2238}
2239
2240HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2241{
2242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2243
2244 HRESULT rc = i_checkStateDependency(MutableStateDep);
2245 if (FAILED(rc)) return rc;
2246
2247 switch (aProperty)
2248 {
2249 case CPUPropertyType_PAE:
2250 i_setModified(IsModified_MachineData);
2251 mHWData.backup();
2252 mHWData->mPAEEnabled = !!aValue;
2253 break;
2254
2255 case CPUPropertyType_LongMode:
2256 i_setModified(IsModified_MachineData);
2257 mHWData.backup();
2258 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2259 break;
2260
2261 case CPUPropertyType_TripleFaultReset:
2262 i_setModified(IsModified_MachineData);
2263 mHWData.backup();
2264 mHWData->mTripleFaultReset = !!aValue;
2265 break;
2266
2267 case CPUPropertyType_APIC:
2268 if (mHWData->mX2APIC)
2269 aValue = TRUE;
2270 i_setModified(IsModified_MachineData);
2271 mHWData.backup();
2272 mHWData->mAPIC = !!aValue;
2273 break;
2274
2275 case CPUPropertyType_X2APIC:
2276 i_setModified(IsModified_MachineData);
2277 mHWData.backup();
2278 mHWData->mX2APIC = !!aValue;
2279 if (aValue)
2280 mHWData->mAPIC = !!aValue;
2281 break;
2282
2283 case CPUPropertyType_IBPBOnVMExit:
2284 i_setModified(IsModified_MachineData);
2285 mHWData.backup();
2286 mHWData->mIBPBOnVMExit = !!aValue;
2287 break;
2288
2289 case CPUPropertyType_IBPBOnVMEntry:
2290 i_setModified(IsModified_MachineData);
2291 mHWData.backup();
2292 mHWData->mIBPBOnVMEntry = !!aValue;
2293 break;
2294
2295 case CPUPropertyType_SpecCtrl:
2296 i_setModified(IsModified_MachineData);
2297 mHWData.backup();
2298 mHWData->mSpecCtrl = !!aValue;
2299 break;
2300
2301 case CPUPropertyType_SpecCtrlByHost:
2302 i_setModified(IsModified_MachineData);
2303 mHWData.backup();
2304 mHWData->mSpecCtrlByHost = !!aValue;
2305 break;
2306
2307 case CPUPropertyType_HWVirt:
2308 i_setModified(IsModified_MachineData);
2309 mHWData.backup();
2310 mHWData->mNestedHWVirt = !!aValue;
2311 break;
2312
2313 case CPUPropertyType_L1DFlushOnEMTScheduling:
2314 i_setModified(IsModified_MachineData);
2315 mHWData.backup();
2316 mHWData->mL1DFlushOnSched = !!aValue;
2317 break;
2318
2319 case CPUPropertyType_L1DFlushOnVMEntry:
2320 i_setModified(IsModified_MachineData);
2321 mHWData.backup();
2322 mHWData->mL1DFlushOnVMEntry = !!aValue;
2323 break;
2324
2325 case CPUPropertyType_MDSClearOnEMTScheduling:
2326 i_setModified(IsModified_MachineData);
2327 mHWData.backup();
2328 mHWData->mMDSClearOnSched = !!aValue;
2329 break;
2330
2331 case CPUPropertyType_MDSClearOnVMEntry:
2332 i_setModified(IsModified_MachineData);
2333 mHWData.backup();
2334 mHWData->mMDSClearOnVMEntry = !!aValue;
2335 break;
2336
2337 default:
2338 return E_INVALIDARG;
2339 }
2340 return S_OK;
2341}
2342
2343HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2344 ULONG *aValEcx, ULONG *aValEdx)
2345{
2346 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2347 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2348 {
2349 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2350 it != mHWData->mCpuIdLeafList.end();
2351 ++it)
2352 {
2353 if (aOrdinal == 0)
2354 {
2355 const settings::CpuIdLeaf &rLeaf= *it;
2356 *aIdx = rLeaf.idx;
2357 *aSubIdx = rLeaf.idxSub;
2358 *aValEax = rLeaf.uEax;
2359 *aValEbx = rLeaf.uEbx;
2360 *aValEcx = rLeaf.uEcx;
2361 *aValEdx = rLeaf.uEdx;
2362 return S_OK;
2363 }
2364 aOrdinal--;
2365 }
2366 }
2367 return E_INVALIDARG;
2368}
2369
2370HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2371{
2372 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2373
2374 /*
2375 * Search the list.
2376 */
2377 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2378 {
2379 const settings::CpuIdLeaf &rLeaf= *it;
2380 if ( rLeaf.idx == aIdx
2381 && ( aSubIdx == UINT32_MAX
2382 || rLeaf.idxSub == aSubIdx) )
2383 {
2384 *aValEax = rLeaf.uEax;
2385 *aValEbx = rLeaf.uEbx;
2386 *aValEcx = rLeaf.uEcx;
2387 *aValEdx = rLeaf.uEdx;
2388 return S_OK;
2389 }
2390 }
2391
2392 return E_INVALIDARG;
2393}
2394
2395
2396HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2397{
2398 /*
2399 * Validate input before taking locks and checking state.
2400 */
2401 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2402 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2403 if ( aIdx >= UINT32_C(0x20)
2404 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2405 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2406 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2407
2408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2409 HRESULT rc = i_checkStateDependency(MutableStateDep);
2410 if (FAILED(rc)) return rc;
2411
2412 /*
2413 * Impose a maximum number of leaves.
2414 */
2415 if (mHWData->mCpuIdLeafList.size() > 256)
2416 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2417
2418 /*
2419 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2420 */
2421 i_setModified(IsModified_MachineData);
2422 mHWData.backup();
2423
2424 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2425 {
2426 settings::CpuIdLeaf &rLeaf= *it;
2427 if ( rLeaf.idx == aIdx
2428 && ( aSubIdx == UINT32_MAX
2429 || rLeaf.idxSub == aSubIdx) )
2430 it = mHWData->mCpuIdLeafList.erase(it);
2431 else
2432 ++it;
2433 }
2434
2435 settings::CpuIdLeaf NewLeaf;
2436 NewLeaf.idx = aIdx;
2437 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2438 NewLeaf.uEax = aValEax;
2439 NewLeaf.uEbx = aValEbx;
2440 NewLeaf.uEcx = aValEcx;
2441 NewLeaf.uEdx = aValEdx;
2442 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2443 return S_OK;
2444}
2445
2446HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2447{
2448 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2449
2450 HRESULT rc = i_checkStateDependency(MutableStateDep);
2451 if (FAILED(rc)) return rc;
2452
2453 /*
2454 * Do the removal.
2455 */
2456 bool fModified = mHWData.isBackedUp();
2457 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2458 {
2459 settings::CpuIdLeaf &rLeaf= *it;
2460 if ( rLeaf.idx == aIdx
2461 && ( aSubIdx == UINT32_MAX
2462 || rLeaf.idxSub == aSubIdx) )
2463 {
2464 if (!fModified)
2465 {
2466 fModified = true;
2467 i_setModified(IsModified_MachineData);
2468 mHWData.backup();
2469 // Start from the beginning, since mHWData.backup() creates
2470 // a new list, causing iterator mixup. This makes sure that
2471 // the settings are not unnecessarily marked as modified,
2472 // at the price of extra list walking.
2473 it = mHWData->mCpuIdLeafList.begin();
2474 }
2475 else
2476 it = mHWData->mCpuIdLeafList.erase(it);
2477 }
2478 else
2479 ++it;
2480 }
2481
2482 return S_OK;
2483}
2484
2485HRESULT Machine::removeAllCPUIDLeaves()
2486{
2487 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2488
2489 HRESULT rc = i_checkStateDependency(MutableStateDep);
2490 if (FAILED(rc)) return rc;
2491
2492 if (mHWData->mCpuIdLeafList.size() > 0)
2493 {
2494 i_setModified(IsModified_MachineData);
2495 mHWData.backup();
2496
2497 mHWData->mCpuIdLeafList.clear();
2498 }
2499
2500 return S_OK;
2501}
2502HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2503{
2504 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2505
2506 switch(aProperty)
2507 {
2508 case HWVirtExPropertyType_Enabled:
2509 *aValue = mHWData->mHWVirtExEnabled;
2510 break;
2511
2512 case HWVirtExPropertyType_VPID:
2513 *aValue = mHWData->mHWVirtExVPIDEnabled;
2514 break;
2515
2516 case HWVirtExPropertyType_NestedPaging:
2517 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2518 break;
2519
2520 case HWVirtExPropertyType_UnrestrictedExecution:
2521 *aValue = mHWData->mHWVirtExUXEnabled;
2522 break;
2523
2524 case HWVirtExPropertyType_LargePages:
2525 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2526 break;
2527
2528 case HWVirtExPropertyType_Force:
2529 *aValue = mHWData->mHWVirtExForceEnabled;
2530 break;
2531
2532 case HWVirtExPropertyType_UseNativeApi:
2533 *aValue = mHWData->mHWVirtExUseNativeApi;
2534 break;
2535
2536 case HWVirtExPropertyType_VirtVmsaveVmload:
2537 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2538 break;
2539
2540 default:
2541 return E_INVALIDARG;
2542 }
2543 return S_OK;
2544}
2545
2546HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2547{
2548 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2549
2550 HRESULT rc = i_checkStateDependency(MutableStateDep);
2551 if (FAILED(rc)) return rc;
2552
2553 switch (aProperty)
2554 {
2555 case HWVirtExPropertyType_Enabled:
2556 i_setModified(IsModified_MachineData);
2557 mHWData.backup();
2558 mHWData->mHWVirtExEnabled = !!aValue;
2559 break;
2560
2561 case HWVirtExPropertyType_VPID:
2562 i_setModified(IsModified_MachineData);
2563 mHWData.backup();
2564 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2565 break;
2566
2567 case HWVirtExPropertyType_NestedPaging:
2568 i_setModified(IsModified_MachineData);
2569 mHWData.backup();
2570 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2571 break;
2572
2573 case HWVirtExPropertyType_UnrestrictedExecution:
2574 i_setModified(IsModified_MachineData);
2575 mHWData.backup();
2576 mHWData->mHWVirtExUXEnabled = !!aValue;
2577 break;
2578
2579 case HWVirtExPropertyType_LargePages:
2580 i_setModified(IsModified_MachineData);
2581 mHWData.backup();
2582 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2583 break;
2584
2585 case HWVirtExPropertyType_Force:
2586 i_setModified(IsModified_MachineData);
2587 mHWData.backup();
2588 mHWData->mHWVirtExForceEnabled = !!aValue;
2589 break;
2590
2591 case HWVirtExPropertyType_UseNativeApi:
2592 i_setModified(IsModified_MachineData);
2593 mHWData.backup();
2594 mHWData->mHWVirtExUseNativeApi = !!aValue;
2595 break;
2596
2597 case HWVirtExPropertyType_VirtVmsaveVmload:
2598 i_setModified(IsModified_MachineData);
2599 mHWData.backup();
2600 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2601 break;
2602
2603 default:
2604 return E_INVALIDARG;
2605 }
2606
2607 return S_OK;
2608}
2609
2610HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2611{
2612 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2613
2614 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2615
2616 return S_OK;
2617}
2618
2619HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2620{
2621 /** @todo (r=dmik):
2622 * 1. Allow to change the name of the snapshot folder containing snapshots
2623 * 2. Rename the folder on disk instead of just changing the property
2624 * value (to be smart and not to leave garbage). Note that it cannot be
2625 * done here because the change may be rolled back. Thus, the right
2626 * place is #saveSettings().
2627 */
2628
2629 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2630
2631 HRESULT rc = i_checkStateDependency(MutableStateDep);
2632 if (FAILED(rc)) return rc;
2633
2634 if (!mData->mCurrentSnapshot.isNull())
2635 return setError(E_FAIL,
2636 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2637
2638 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2639
2640 if (strSnapshotFolder.isEmpty())
2641 strSnapshotFolder = "Snapshots";
2642 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2643 if (RT_FAILURE(vrc))
2644 return setErrorBoth(E_FAIL, vrc,
2645 tr("Invalid snapshot folder '%s' (%Rrc)"),
2646 strSnapshotFolder.c_str(), vrc);
2647
2648 i_setModified(IsModified_MachineData);
2649 mUserData.backup();
2650
2651 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2652
2653 return S_OK;
2654}
2655
2656HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2657{
2658 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2659
2660 aMediumAttachments.resize(mMediumAttachments->size());
2661 size_t i = 0;
2662 for (MediumAttachmentList::const_iterator
2663 it = mMediumAttachments->begin();
2664 it != mMediumAttachments->end();
2665 ++it, ++i)
2666 aMediumAttachments[i] = *it;
2667
2668 return S_OK;
2669}
2670
2671HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2672{
2673 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2674
2675 Assert(!!mVRDEServer);
2676
2677 aVRDEServer = mVRDEServer;
2678
2679 return S_OK;
2680}
2681
2682HRESULT Machine::getAudioSettings(ComPtr<IAudioSettings> &aAudioSettings)
2683{
2684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2685
2686 aAudioSettings = mAudioSettings;
2687
2688 return S_OK;
2689}
2690
2691HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2692{
2693#ifdef VBOX_WITH_VUSB
2694 clearError();
2695 MultiResult rc(S_OK);
2696
2697# ifdef VBOX_WITH_USB
2698 rc = mParent->i_host()->i_checkUSBProxyService();
2699 if (FAILED(rc)) return rc;
2700# endif
2701
2702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2703
2704 aUSBControllers.resize(mUSBControllers->size());
2705 size_t i = 0;
2706 for (USBControllerList::const_iterator
2707 it = mUSBControllers->begin();
2708 it != mUSBControllers->end();
2709 ++it, ++i)
2710 aUSBControllers[i] = *it;
2711
2712 return S_OK;
2713#else
2714 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2715 * extended error info to indicate that USB is simply not available
2716 * (w/o treating it as a failure), for example, as in OSE */
2717 NOREF(aUSBControllers);
2718 ReturnComNotImplemented();
2719#endif /* VBOX_WITH_VUSB */
2720}
2721
2722HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2723{
2724#ifdef VBOX_WITH_VUSB
2725 clearError();
2726 MultiResult rc(S_OK);
2727
2728# ifdef VBOX_WITH_USB
2729 rc = mParent->i_host()->i_checkUSBProxyService();
2730 if (FAILED(rc)) return rc;
2731# endif
2732
2733 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2734
2735 aUSBDeviceFilters = mUSBDeviceFilters;
2736 return rc;
2737#else
2738 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2739 * extended error info to indicate that USB is simply not available
2740 * (w/o treating it as a failure), for example, as in OSE */
2741 NOREF(aUSBDeviceFilters);
2742 ReturnComNotImplemented();
2743#endif /* VBOX_WITH_VUSB */
2744}
2745
2746HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2747{
2748 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2749
2750 aSettingsFilePath = mData->m_strConfigFileFull;
2751
2752 return S_OK;
2753}
2754
2755HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2756{
2757 RT_NOREF(aSettingsFilePath);
2758 ReturnComNotImplemented();
2759}
2760
2761HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2762{
2763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2764
2765 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2766 if (FAILED(rc)) return rc;
2767
2768 if (!mData->pMachineConfigFile->fileExists())
2769 // this is a new machine, and no config file exists yet:
2770 *aSettingsModified = TRUE;
2771 else
2772 *aSettingsModified = (mData->flModifications != 0);
2773
2774 return S_OK;
2775}
2776
2777HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2778{
2779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2780
2781 *aSessionState = mData->mSession.mState;
2782
2783 return S_OK;
2784}
2785
2786HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2787{
2788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2789
2790 aSessionName = mData->mSession.mName;
2791
2792 return S_OK;
2793}
2794
2795HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2796{
2797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2798
2799 *aSessionPID = mData->mSession.mPID;
2800
2801 return S_OK;
2802}
2803
2804HRESULT Machine::getState(MachineState_T *aState)
2805{
2806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2807
2808 *aState = mData->mMachineState;
2809 Assert(mData->mMachineState != MachineState_Null);
2810
2811 return S_OK;
2812}
2813
2814HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2815{
2816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2817
2818 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2819
2820 return S_OK;
2821}
2822
2823HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2824{
2825 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2826
2827 aStateFilePath = mSSData->strStateFilePath;
2828
2829 return S_OK;
2830}
2831
2832HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2833{
2834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2835
2836 i_getLogFolder(aLogFolder);
2837
2838 return S_OK;
2839}
2840
2841HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2842{
2843 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2844
2845 aCurrentSnapshot = mData->mCurrentSnapshot;
2846
2847 return S_OK;
2848}
2849
2850HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2851{
2852 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2853
2854 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2855 ? 0
2856 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2857
2858 return S_OK;
2859}
2860
2861HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2862{
2863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2864
2865 /* Note: for machines with no snapshots, we always return FALSE
2866 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2867 * reasons :) */
2868
2869 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2870 ? FALSE
2871 : mData->mCurrentStateModified;
2872
2873 return S_OK;
2874}
2875
2876HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2877{
2878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2879
2880 aSharedFolders.resize(mHWData->mSharedFolders.size());
2881 size_t i = 0;
2882 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2883 it = mHWData->mSharedFolders.begin();
2884 it != mHWData->mSharedFolders.end();
2885 ++it, ++i)
2886 aSharedFolders[i] = *it;
2887
2888 return S_OK;
2889}
2890
2891HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2892{
2893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2894
2895 *aClipboardMode = mHWData->mClipboardMode;
2896
2897 return S_OK;
2898}
2899
2900HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2901{
2902 HRESULT rc = S_OK;
2903
2904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2905
2906 rc = i_checkStateDependency(MutableOrRunningStateDep);
2907 if (FAILED(rc)) return rc;
2908
2909 alock.release();
2910 rc = i_onClipboardModeChange(aClipboardMode);
2911 alock.acquire();
2912 if (FAILED(rc)) return rc;
2913
2914 i_setModified(IsModified_MachineData);
2915 mHWData.backup();
2916 mHWData->mClipboardMode = aClipboardMode;
2917
2918 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2919 if (Global::IsOnline(mData->mMachineState))
2920 i_saveSettings(NULL, alock);
2921
2922 return S_OK;
2923}
2924
2925HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2926{
2927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2928
2929 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2930
2931 return S_OK;
2932}
2933
2934HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2935{
2936 HRESULT rc = S_OK;
2937
2938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2939
2940 rc = i_checkStateDependency(MutableOrRunningStateDep);
2941 if (FAILED(rc)) return rc;
2942
2943 alock.release();
2944 rc = i_onClipboardFileTransferModeChange(aEnabled);
2945 alock.acquire();
2946 if (FAILED(rc)) return rc;
2947
2948 i_setModified(IsModified_MachineData);
2949 mHWData.backup();
2950 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2951
2952 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2953 if (Global::IsOnline(mData->mMachineState))
2954 i_saveSettings(NULL, alock);
2955
2956 return S_OK;
2957}
2958
2959HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2960{
2961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2962
2963 *aDnDMode = mHWData->mDnDMode;
2964
2965 return S_OK;
2966}
2967
2968HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2969{
2970 HRESULT rc = S_OK;
2971
2972 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2973
2974 rc = i_checkStateDependency(MutableOrRunningStateDep);
2975 if (FAILED(rc)) return rc;
2976
2977 alock.release();
2978 rc = i_onDnDModeChange(aDnDMode);
2979
2980 alock.acquire();
2981 if (FAILED(rc)) return rc;
2982
2983 i_setModified(IsModified_MachineData);
2984 mHWData.backup();
2985 mHWData->mDnDMode = aDnDMode;
2986
2987 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2988 if (Global::IsOnline(mData->mMachineState))
2989 i_saveSettings(NULL, alock);
2990
2991 return S_OK;
2992}
2993
2994HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2995{
2996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2997
2998 aStorageControllers.resize(mStorageControllers->size());
2999 size_t i = 0;
3000 for (StorageControllerList::const_iterator
3001 it = mStorageControllers->begin();
3002 it != mStorageControllers->end();
3003 ++it, ++i)
3004 aStorageControllers[i] = *it;
3005
3006 return S_OK;
3007}
3008
3009HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
3010{
3011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3012
3013 *aEnabled = mUserData->s.fTeleporterEnabled;
3014
3015 return S_OK;
3016}
3017
3018HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
3019{
3020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3021
3022 /* Only allow it to be set to true when PoweredOff or Aborted.
3023 (Clearing it is always permitted.) */
3024 if ( aTeleporterEnabled
3025 && mData->mRegistered
3026 && ( !i_isSessionMachine()
3027 || ( mData->mMachineState != MachineState_PoweredOff
3028 && mData->mMachineState != MachineState_Teleported
3029 && mData->mMachineState != MachineState_Aborted
3030 )
3031 )
3032 )
3033 return setError(VBOX_E_INVALID_VM_STATE,
3034 tr("The machine is not powered off (state is %s)"),
3035 Global::stringifyMachineState(mData->mMachineState));
3036
3037 i_setModified(IsModified_MachineData);
3038 mUserData.backup();
3039 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
3040
3041 return S_OK;
3042}
3043
3044HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
3045{
3046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3047
3048 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
3049
3050 return S_OK;
3051}
3052
3053HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
3054{
3055 if (aTeleporterPort >= _64K)
3056 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
3057
3058 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3059
3060 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3061 if (FAILED(rc)) return rc;
3062
3063 i_setModified(IsModified_MachineData);
3064 mUserData.backup();
3065 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3066
3067 return S_OK;
3068}
3069
3070HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3071{
3072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3073
3074 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3075
3076 return S_OK;
3077}
3078
3079HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3080{
3081 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3082
3083 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3084 if (FAILED(rc)) return rc;
3085
3086 i_setModified(IsModified_MachineData);
3087 mUserData.backup();
3088 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3089
3090 return S_OK;
3091}
3092
3093HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3094{
3095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3096 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3097
3098 return S_OK;
3099}
3100
3101HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3102{
3103 /*
3104 * Hash the password first.
3105 */
3106 com::Utf8Str aT = aTeleporterPassword;
3107
3108 if (!aT.isEmpty())
3109 {
3110 if (VBoxIsPasswordHashed(&aT))
3111 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3112 VBoxHashPassword(&aT);
3113 }
3114
3115 /*
3116 * Do the update.
3117 */
3118 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3119 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3120 if (SUCCEEDED(hrc))
3121 {
3122 i_setModified(IsModified_MachineData);
3123 mUserData.backup();
3124 mUserData->s.strTeleporterPassword = aT;
3125 }
3126
3127 return hrc;
3128}
3129
3130HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3131{
3132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3133
3134 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3135
3136 return S_OK;
3137}
3138
3139HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3140{
3141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3142
3143 /* Only allow it to be set to true when PoweredOff or Aborted.
3144 (Clearing it is always permitted.) */
3145 if ( aRTCUseUTC
3146 && mData->mRegistered
3147 && ( !i_isSessionMachine()
3148 || ( mData->mMachineState != MachineState_PoweredOff
3149 && mData->mMachineState != MachineState_Teleported
3150 && mData->mMachineState != MachineState_Aborted
3151 )
3152 )
3153 )
3154 return setError(VBOX_E_INVALID_VM_STATE,
3155 tr("The machine is not powered off (state is %s)"),
3156 Global::stringifyMachineState(mData->mMachineState));
3157
3158 i_setModified(IsModified_MachineData);
3159 mUserData.backup();
3160 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3161
3162 return S_OK;
3163}
3164
3165HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3166{
3167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3168
3169 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3170
3171 return S_OK;
3172}
3173
3174HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3175{
3176 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3177
3178 HRESULT rc = i_checkStateDependency(MutableStateDep);
3179 if (FAILED(rc)) return rc;
3180
3181 i_setModified(IsModified_MachineData);
3182 mHWData.backup();
3183 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3184
3185 return S_OK;
3186}
3187
3188HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3189{
3190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3191
3192 *aIOCacheSize = mHWData->mIOCacheSize;
3193
3194 return S_OK;
3195}
3196
3197HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3198{
3199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3200
3201 HRESULT rc = i_checkStateDependency(MutableStateDep);
3202 if (FAILED(rc)) return rc;
3203
3204 i_setModified(IsModified_MachineData);
3205 mHWData.backup();
3206 mHWData->mIOCacheSize = aIOCacheSize;
3207
3208 return S_OK;
3209}
3210
3211HRESULT Machine::getStateKeyId(com::Utf8Str &aKeyId)
3212{
3213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3214
3215#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3216 aKeyId = mSSData->strStateKeyId;
3217#else
3218 aKeyId = com::Utf8Str::Empty;
3219#endif
3220
3221 return S_OK;
3222}
3223
3224HRESULT Machine::getStateKeyStore(com::Utf8Str &aKeyStore)
3225{
3226 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3227
3228#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3229 aKeyStore = mSSData->strStateKeyStore;
3230#else
3231 aKeyStore = com::Utf8Str::Empty;
3232#endif
3233
3234 return S_OK;
3235}
3236
3237HRESULT Machine::getLogKeyId(com::Utf8Str &aKeyId)
3238{
3239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3240
3241#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3242 aKeyId = mData->mstrLogKeyId;
3243#else
3244 aKeyId = com::Utf8Str::Empty;
3245#endif
3246
3247 return S_OK;
3248}
3249
3250HRESULT Machine::getLogKeyStore(com::Utf8Str &aKeyStore)
3251{
3252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3253
3254#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3255 aKeyStore = mData->mstrLogKeyStore;
3256#else
3257 aKeyStore = com::Utf8Str::Empty;
3258#endif
3259
3260 return S_OK;
3261}
3262
3263
3264/**
3265 * @note Locks objects!
3266 */
3267HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3268 LockType_T aLockType)
3269{
3270 /* check the session state */
3271 SessionState_T state;
3272 HRESULT rc = aSession->COMGETTER(State)(&state);
3273 if (FAILED(rc)) return rc;
3274
3275 if (state != SessionState_Unlocked)
3276 return setError(VBOX_E_INVALID_OBJECT_STATE,
3277 tr("The given session is busy"));
3278
3279 // get the client's IInternalSessionControl interface
3280 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3281 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
3282 E_INVALIDARG);
3283
3284 // session name (only used in some code paths)
3285 Utf8Str strSessionName;
3286
3287 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3288
3289 if (!mData->mRegistered)
3290 return setError(E_UNEXPECTED,
3291 tr("The machine '%s' is not registered"),
3292 mUserData->s.strName.c_str());
3293
3294 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3295
3296 SessionState_T oldState = mData->mSession.mState;
3297 /* Hack: in case the session is closing and there is a progress object
3298 * which allows waiting for the session to be closed, take the opportunity
3299 * and do a limited wait (max. 1 second). This helps a lot when the system
3300 * is busy and thus session closing can take a little while. */
3301 if ( mData->mSession.mState == SessionState_Unlocking
3302 && mData->mSession.mProgress)
3303 {
3304 alock.release();
3305 mData->mSession.mProgress->WaitForCompletion(1000);
3306 alock.acquire();
3307 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3308 }
3309
3310 // try again now
3311 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3312 // (i.e. session machine exists)
3313 && (aLockType == LockType_Shared) // caller wants a shared link to the
3314 // existing session that holds the write lock:
3315 )
3316 {
3317 // OK, share the session... we are now dealing with three processes:
3318 // 1) VBoxSVC (where this code runs);
3319 // 2) process C: the caller's client process (who wants a shared session);
3320 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3321
3322 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3323 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3324 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3325 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3326 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3327
3328 /*
3329 * Release the lock before calling the client process. It's safe here
3330 * since the only thing to do after we get the lock again is to add
3331 * the remote control to the list (which doesn't directly influence
3332 * anything).
3333 */
3334 alock.release();
3335
3336 // get the console of the session holding the write lock (this is a remote call)
3337 ComPtr<IConsole> pConsoleW;
3338 if (mData->mSession.mLockType == LockType_VM)
3339 {
3340 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3341 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3342 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3343 if (FAILED(rc))
3344 // the failure may occur w/o any error info (from RPC), so provide one
3345 return setError(VBOX_E_VM_ERROR,
3346 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3347 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3348 }
3349
3350 // share the session machine and W's console with the caller's session
3351 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3352 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3353 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3354
3355 if (FAILED(rc))
3356 // the failure may occur w/o any error info (from RPC), so provide one
3357 return setError(VBOX_E_VM_ERROR,
3358 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3359 alock.acquire();
3360
3361 // need to revalidate the state after acquiring the lock again
3362 if (mData->mSession.mState != SessionState_Locked)
3363 {
3364 pSessionControl->Uninitialize();
3365 return setError(VBOX_E_INVALID_SESSION_STATE,
3366 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3367 mUserData->s.strName.c_str());
3368 }
3369
3370 // add the caller's session to the list
3371 mData->mSession.mRemoteControls.push_back(pSessionControl);
3372 }
3373 else if ( mData->mSession.mState == SessionState_Locked
3374 || mData->mSession.mState == SessionState_Unlocking
3375 )
3376 {
3377 // sharing not permitted, or machine still unlocking:
3378 return setError(VBOX_E_INVALID_OBJECT_STATE,
3379 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3380 mUserData->s.strName.c_str());
3381 }
3382 else
3383 {
3384 // machine is not locked: then write-lock the machine (create the session machine)
3385
3386 // must not be busy
3387 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3388
3389 // get the caller's session PID
3390 RTPROCESS pid = NIL_RTPROCESS;
3391 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3392 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3393 Assert(pid != NIL_RTPROCESS);
3394
3395 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3396
3397 if (fLaunchingVMProcess)
3398 {
3399 if (mData->mSession.mPID == NIL_RTPROCESS)
3400 {
3401 // two or more clients racing for a lock, the one which set the
3402 // session state to Spawning will win, the others will get an
3403 // error as we can't decide here if waiting a little would help
3404 // (only for shared locks this would avoid an error)
3405 return setError(VBOX_E_INVALID_OBJECT_STATE,
3406 tr("The machine '%s' already has a lock request pending"),
3407 mUserData->s.strName.c_str());
3408 }
3409
3410 // this machine is awaiting for a spawning session to be opened:
3411 // then the calling process must be the one that got started by
3412 // LaunchVMProcess()
3413
3414 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3415 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3416
3417#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3418 /* Hardened windows builds spawns three processes when a VM is
3419 launched, the 3rd one is the one that will end up here. */
3420 RTPROCESS pidParent;
3421 int vrc = RTProcQueryParent(pid, &pidParent);
3422 if (RT_SUCCESS(vrc))
3423 vrc = RTProcQueryParent(pidParent, &pidParent);
3424 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3425 || vrc == VERR_ACCESS_DENIED)
3426 {
3427 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3428 mData->mSession.mPID = pid;
3429 }
3430#endif
3431
3432 if (mData->mSession.mPID != pid)
3433 return setError(E_ACCESSDENIED,
3434 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3435 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3436 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3437 }
3438
3439 // create the mutable SessionMachine from the current machine
3440 ComObjPtr<SessionMachine> sessionMachine;
3441 sessionMachine.createObject();
3442 rc = sessionMachine->init(this);
3443 AssertComRC(rc);
3444
3445 /* NOTE: doing return from this function after this point but
3446 * before the end is forbidden since it may call SessionMachine::uninit()
3447 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3448 * lock while still holding the Machine lock in alock so that a deadlock
3449 * is possible due to the wrong lock order. */
3450
3451 if (SUCCEEDED(rc))
3452 {
3453 /*
3454 * Set the session state to Spawning to protect against subsequent
3455 * attempts to open a session and to unregister the machine after
3456 * we release the lock.
3457 */
3458 SessionState_T origState = mData->mSession.mState;
3459 mData->mSession.mState = SessionState_Spawning;
3460
3461#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3462 /* Get the client token ID to be passed to the client process */
3463 Utf8Str strTokenId;
3464 sessionMachine->i_getTokenId(strTokenId);
3465 Assert(!strTokenId.isEmpty());
3466#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3467 /* Get the client token to be passed to the client process */
3468 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3469 /* The token is now "owned" by pToken, fix refcount */
3470 if (!pToken.isNull())
3471 pToken->Release();
3472#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3473
3474 /*
3475 * Release the lock before calling the client process -- it will call
3476 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3477 * because the state is Spawning, so that LaunchVMProcess() and
3478 * LockMachine() calls will fail. This method, called before we
3479 * acquire the lock again, will fail because of the wrong PID.
3480 *
3481 * Note that mData->mSession.mRemoteControls accessed outside
3482 * the lock may not be modified when state is Spawning, so it's safe.
3483 */
3484 alock.release();
3485
3486 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3487#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3488 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3489#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3490 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3491 /* Now the token is owned by the client process. */
3492 pToken.setNull();
3493#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3494 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3495
3496 /* The failure may occur w/o any error info (from RPC), so provide one */
3497 if (FAILED(rc))
3498 setError(VBOX_E_VM_ERROR,
3499 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3500
3501 // get session name, either to remember or to compare against
3502 // the already known session name.
3503 {
3504 Bstr bstrSessionName;
3505 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3506 if (SUCCEEDED(rc2))
3507 strSessionName = bstrSessionName;
3508 }
3509
3510 if ( SUCCEEDED(rc)
3511 && fLaunchingVMProcess
3512 )
3513 {
3514 /* complete the remote session initialization */
3515
3516 /* get the console from the direct session */
3517 ComPtr<IConsole> console;
3518 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3519 ComAssertComRC(rc);
3520
3521 if (SUCCEEDED(rc) && !console)
3522 {
3523 ComAssert(!!console);
3524 rc = E_FAIL;
3525 }
3526
3527 /* assign machine & console to the remote session */
3528 if (SUCCEEDED(rc))
3529 {
3530 /*
3531 * after LaunchVMProcess(), the first and the only
3532 * entry in remoteControls is that remote session
3533 */
3534 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3535 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3536 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3537
3538 /* The failure may occur w/o any error info (from RPC), so provide one */
3539 if (FAILED(rc))
3540 setError(VBOX_E_VM_ERROR,
3541 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3542 }
3543
3544 if (FAILED(rc))
3545 pSessionControl->Uninitialize();
3546 }
3547
3548 /* acquire the lock again */
3549 alock.acquire();
3550
3551 /* Restore the session state */
3552 mData->mSession.mState = origState;
3553 }
3554
3555 // finalize spawning anyway (this is why we don't return on errors above)
3556 if (fLaunchingVMProcess)
3557 {
3558 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3559 /* Note that the progress object is finalized later */
3560 /** @todo Consider checking mData->mSession.mProgress for cancellation
3561 * around here. */
3562
3563 /* We don't reset mSession.mPID here because it is necessary for
3564 * SessionMachine::uninit() to reap the child process later. */
3565
3566 if (FAILED(rc))
3567 {
3568 /* Close the remote session, remove the remote control from the list
3569 * and reset session state to Closed (@note keep the code in sync
3570 * with the relevant part in checkForSpawnFailure()). */
3571
3572 Assert(mData->mSession.mRemoteControls.size() == 1);
3573 if (mData->mSession.mRemoteControls.size() == 1)
3574 {
3575 ErrorInfoKeeper eik;
3576 mData->mSession.mRemoteControls.front()->Uninitialize();
3577 }
3578
3579 mData->mSession.mRemoteControls.clear();
3580 mData->mSession.mState = SessionState_Unlocked;
3581 }
3582 }
3583 else
3584 {
3585 /* memorize PID of the directly opened session */
3586 if (SUCCEEDED(rc))
3587 mData->mSession.mPID = pid;
3588 }
3589
3590 if (SUCCEEDED(rc))
3591 {
3592 mData->mSession.mLockType = aLockType;
3593 /* memorize the direct session control and cache IUnknown for it */
3594 mData->mSession.mDirectControl = pSessionControl;
3595 mData->mSession.mState = SessionState_Locked;
3596 if (!fLaunchingVMProcess)
3597 mData->mSession.mName = strSessionName;
3598 /* associate the SessionMachine with this Machine */
3599 mData->mSession.mMachine = sessionMachine;
3600
3601 /* request an IUnknown pointer early from the remote party for later
3602 * identity checks (it will be internally cached within mDirectControl
3603 * at least on XPCOM) */
3604 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3605 NOREF(unk);
3606
3607#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3608 if (aLockType == LockType_VM)
3609 {
3610 /* get the console from the direct session */
3611 ComPtr<IConsole> console;
3612 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3613 ComAssertComRC(rc);
3614 /* send passswords to console */
3615 for (SecretKeyStore::SecretKeyMap::iterator it = mData->mpKeyStore->begin();
3616 it != mData->mpKeyStore->end();
3617 ++it)
3618 {
3619 SecretKey *pKey = it->second;
3620 pKey->retain();
3621 console->AddEncryptionPassword(Bstr(it->first).raw(),
3622 Bstr((const char*)pKey->getKeyBuffer()).raw(),
3623 TRUE);
3624 pKey->release();
3625 }
3626
3627 }
3628#endif
3629 }
3630
3631 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3632 * would break the lock order */
3633 alock.release();
3634
3635 /* uninitialize the created session machine on failure */
3636 if (FAILED(rc))
3637 sessionMachine->uninit();
3638 }
3639
3640 if (SUCCEEDED(rc))
3641 {
3642 /*
3643 * tell the client watcher thread to update the set of
3644 * machines that have open sessions
3645 */
3646 mParent->i_updateClientWatcher();
3647
3648 if (oldState != SessionState_Locked)
3649 /* fire an event */
3650 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3651 }
3652
3653 return rc;
3654}
3655
3656/**
3657 * @note Locks objects!
3658 */
3659HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3660 const com::Utf8Str &aName,
3661 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3662 ComPtr<IProgress> &aProgress)
3663{
3664 Utf8Str strFrontend(aName);
3665 /* "emergencystop" doesn't need the session, so skip the checks/interface
3666 * retrieval. This code doesn't quite fit in here, but introducing a
3667 * special API method would be even more effort, and would require explicit
3668 * support by every API client. It's better to hide the feature a bit. */
3669 if (strFrontend != "emergencystop")
3670 CheckComArgNotNull(aSession);
3671
3672 HRESULT rc = S_OK;
3673 if (strFrontend.isEmpty())
3674 {
3675 Bstr bstrFrontend;
3676 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3677 if (FAILED(rc))
3678 return rc;
3679 strFrontend = bstrFrontend;
3680 if (strFrontend.isEmpty())
3681 {
3682 ComPtr<ISystemProperties> systemProperties;
3683 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3684 if (FAILED(rc))
3685 return rc;
3686 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3687 if (FAILED(rc))
3688 return rc;
3689 strFrontend = bstrFrontend;
3690 }
3691 /* paranoia - emergencystop is not a valid default */
3692 if (strFrontend == "emergencystop")
3693 strFrontend = Utf8Str::Empty;
3694 }
3695 /* default frontend: Qt GUI */
3696 if (strFrontend.isEmpty())
3697 strFrontend = "GUI/Qt";
3698
3699 if (strFrontend != "emergencystop")
3700 {
3701 /* check the session state */
3702 SessionState_T state;
3703 rc = aSession->COMGETTER(State)(&state);
3704 if (FAILED(rc))
3705 return rc;
3706
3707 if (state != SessionState_Unlocked)
3708 return setError(VBOX_E_INVALID_OBJECT_STATE,
3709 tr("The given session is busy"));
3710
3711 /* get the IInternalSessionControl interface */
3712 ComPtr<IInternalSessionControl> control(aSession);
3713 ComAssertMsgRet(!control.isNull(),
3714 ("No IInternalSessionControl interface"),
3715 E_INVALIDARG);
3716
3717 /* get the teleporter enable state for the progress object init. */
3718 BOOL fTeleporterEnabled;
3719 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3720 if (FAILED(rc))
3721 return rc;
3722
3723 /* create a progress object */
3724 ComObjPtr<ProgressProxy> progress;
3725 progress.createObject();
3726 rc = progress->init(mParent,
3727 static_cast<IMachine*>(this),
3728 Bstr(tr("Starting VM")).raw(),
3729 TRUE /* aCancelable */,
3730 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3731 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3732 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3733 2 /* uFirstOperationWeight */,
3734 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3735
3736 if (SUCCEEDED(rc))
3737 {
3738 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3739 if (SUCCEEDED(rc))
3740 {
3741 aProgress = progress;
3742
3743 /* signal the client watcher thread */
3744 mParent->i_updateClientWatcher();
3745
3746 /* fire an event */
3747 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3748 }
3749 }
3750 }
3751 else
3752 {
3753 /* no progress object - either instant success or failure */
3754 aProgress = NULL;
3755
3756 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3757
3758 if (mData->mSession.mState != SessionState_Locked)
3759 return setError(VBOX_E_INVALID_OBJECT_STATE,
3760 tr("The machine '%s' is not locked by a session"),
3761 mUserData->s.strName.c_str());
3762
3763 /* must have a VM process associated - do not kill normal API clients
3764 * with an open session */
3765 if (!Global::IsOnline(mData->mMachineState))
3766 return setError(VBOX_E_INVALID_OBJECT_STATE,
3767 tr("The machine '%s' does not have a VM process"),
3768 mUserData->s.strName.c_str());
3769
3770 /* forcibly terminate the VM process */
3771 if (mData->mSession.mPID != NIL_RTPROCESS)
3772 RTProcTerminate(mData->mSession.mPID);
3773
3774 /* signal the client watcher thread, as most likely the client has
3775 * been terminated */
3776 mParent->i_updateClientWatcher();
3777 }
3778
3779 return rc;
3780}
3781
3782HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3783{
3784 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3785 return setError(E_INVALIDARG,
3786 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3787 aPosition, SchemaDefs::MaxBootPosition);
3788
3789 if (aDevice == DeviceType_USB)
3790 return setError(E_NOTIMPL,
3791 tr("Booting from USB device is currently not supported"));
3792
3793 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3794
3795 HRESULT rc = i_checkStateDependency(MutableStateDep);
3796 if (FAILED(rc)) return rc;
3797
3798 i_setModified(IsModified_MachineData);
3799 mHWData.backup();
3800 mHWData->mBootOrder[aPosition - 1] = aDevice;
3801
3802 return S_OK;
3803}
3804
3805HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3806{
3807 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3808 return setError(E_INVALIDARG,
3809 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3810 aPosition, SchemaDefs::MaxBootPosition);
3811
3812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3813
3814 *aDevice = mHWData->mBootOrder[aPosition - 1];
3815
3816 return S_OK;
3817}
3818
3819HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3820 LONG aControllerPort,
3821 LONG aDevice,
3822 DeviceType_T aType,
3823 const ComPtr<IMedium> &aMedium)
3824{
3825 IMedium *aM = aMedium;
3826 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3827 aName.c_str(), aControllerPort, aDevice, aType, aM));
3828
3829 // request the host lock first, since might be calling Host methods for getting host drives;
3830 // next, protect the media tree all the while we're in here, as well as our member variables
3831 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3832 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3833
3834 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3835 if (FAILED(rc)) return rc;
3836
3837 /// @todo NEWMEDIA implicit machine registration
3838 if (!mData->mRegistered)
3839 return setError(VBOX_E_INVALID_OBJECT_STATE,
3840 tr("Cannot attach storage devices to an unregistered machine"));
3841
3842 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3843
3844 /* Check for an existing controller. */
3845 ComObjPtr<StorageController> ctl;
3846 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3847 if (FAILED(rc)) return rc;
3848
3849 StorageControllerType_T ctrlType;
3850 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3851 if (FAILED(rc))
3852 return setError(E_FAIL,
3853 tr("Could not get type of controller '%s'"),
3854 aName.c_str());
3855
3856 bool fSilent = false;
3857 Utf8Str strReconfig;
3858
3859 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3860 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3861 if ( mData->mMachineState == MachineState_Paused
3862 && strReconfig == "1")
3863 fSilent = true;
3864
3865 /* Check that the controller can do hot-plugging if we attach the device while the VM is running. */
3866 bool fHotplug = false;
3867 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3868 fHotplug = true;
3869
3870 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3871 return setError(VBOX_E_INVALID_VM_STATE,
3872 tr("Controller '%s' does not support hot-plugging"),
3873 aName.c_str());
3874
3875 /* Attaching a USB device when a VM is powered off should default to being marked as hot-pluggable */
3876 if (!fHotplug && !Global::IsOnlineOrTransient(mData->mMachineState) && ctrlType == StorageControllerType_USB)
3877 fHotplug = true;
3878
3879 // check that the port and device are not out of range
3880 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3881 if (FAILED(rc)) return rc;
3882
3883 /* check if the device slot is already busy */
3884 MediumAttachment *pAttachTemp;
3885 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3886 aName,
3887 aControllerPort,
3888 aDevice)))
3889 {
3890 Medium *pMedium = pAttachTemp->i_getMedium();
3891 if (pMedium)
3892 {
3893 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3894 return setError(VBOX_E_OBJECT_IN_USE,
3895 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3896 pMedium->i_getLocationFull().c_str(),
3897 aControllerPort,
3898 aDevice,
3899 aName.c_str());
3900 }
3901 else
3902 return setError(VBOX_E_OBJECT_IN_USE,
3903 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3904 aControllerPort, aDevice, aName.c_str());
3905 }
3906
3907 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3908 if (aMedium && medium.isNull())
3909 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3910
3911 AutoCaller mediumCaller(medium);
3912 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3913
3914 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3915
3916 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3917 && !medium.isNull()
3918 && ( medium->i_getType() != MediumType_Readonly
3919 || medium->i_getDeviceType() != DeviceType_DVD)
3920 )
3921 return setError(VBOX_E_OBJECT_IN_USE,
3922 tr("Medium '%s' is already attached to this virtual machine"),
3923 medium->i_getLocationFull().c_str());
3924
3925 if (!medium.isNull())
3926 {
3927 MediumType_T mtype = medium->i_getType();
3928 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3929 // For DVDs it's not written to the config file, so needs no global config
3930 // version bump. For floppies it's a new attribute "type", which is ignored
3931 // by older VirtualBox version, so needs no global config version bump either.
3932 // For hard disks this type is not accepted.
3933 if (mtype == MediumType_MultiAttach)
3934 {
3935 // This type is new with VirtualBox 4.0 and therefore requires settings
3936 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3937 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3938 // two reasons: The medium type is a property of the media registry tree, which
3939 // can reside in the global config file (for pre-4.0 media); we would therefore
3940 // possibly need to bump the global config version. We don't want to do that though
3941 // because that might make downgrading to pre-4.0 impossible.
3942 // As a result, we can only use these two new types if the medium is NOT in the
3943 // global registry:
3944 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3945 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3946 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3947 )
3948 return setError(VBOX_E_INVALID_OBJECT_STATE,
3949 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3950 "to machines that were created with VirtualBox 4.0 or later"),
3951 medium->i_getLocationFull().c_str());
3952 }
3953 }
3954
3955 bool fIndirect = false;
3956 if (!medium.isNull())
3957 fIndirect = medium->i_isReadOnly();
3958 bool associate = true;
3959
3960 do
3961 {
3962 if ( aType == DeviceType_HardDisk
3963 && mMediumAttachments.isBackedUp())
3964 {
3965 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3966
3967 /* check if the medium was attached to the VM before we started
3968 * changing attachments in which case the attachment just needs to
3969 * be restored */
3970 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3971 {
3972 AssertReturn(!fIndirect, E_FAIL);
3973
3974 /* see if it's the same bus/channel/device */
3975 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3976 {
3977 /* the simplest case: restore the whole attachment
3978 * and return, nothing else to do */
3979 mMediumAttachments->push_back(pAttachTemp);
3980
3981 /* Reattach the medium to the VM. */
3982 if (fHotplug || fSilent)
3983 {
3984 mediumLock.release();
3985 treeLock.release();
3986 alock.release();
3987
3988 MediumLockList *pMediumLockList(new MediumLockList());
3989
3990 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3991 medium /* pToLockWrite */,
3992 false /* fMediumLockWriteAll */,
3993 NULL,
3994 *pMediumLockList);
3995 alock.acquire();
3996 if (FAILED(rc))
3997 delete pMediumLockList;
3998 else
3999 {
4000 mData->mSession.mLockedMedia.Unlock();
4001 alock.release();
4002 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4003 mData->mSession.mLockedMedia.Lock();
4004 alock.acquire();
4005 }
4006 alock.release();
4007
4008 if (SUCCEEDED(rc))
4009 {
4010 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4011 /* Remove lock list in case of error. */
4012 if (FAILED(rc))
4013 {
4014 mData->mSession.mLockedMedia.Unlock();
4015 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4016 mData->mSession.mLockedMedia.Lock();
4017 }
4018 }
4019 }
4020
4021 return S_OK;
4022 }
4023
4024 /* bus/channel/device differ; we need a new attachment object,
4025 * but don't try to associate it again */
4026 associate = false;
4027 break;
4028 }
4029 }
4030
4031 /* go further only if the attachment is to be indirect */
4032 if (!fIndirect)
4033 break;
4034
4035 /* perform the so called smart attachment logic for indirect
4036 * attachments. Note that smart attachment is only applicable to base
4037 * hard disks. */
4038
4039 if (medium->i_getParent().isNull())
4040 {
4041 /* first, investigate the backup copy of the current hard disk
4042 * attachments to make it possible to re-attach existing diffs to
4043 * another device slot w/o losing their contents */
4044 if (mMediumAttachments.isBackedUp())
4045 {
4046 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4047
4048 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4049 uint32_t foundLevel = 0;
4050
4051 for (MediumAttachmentList::const_iterator
4052 it = oldAtts.begin();
4053 it != oldAtts.end();
4054 ++it)
4055 {
4056 uint32_t level = 0;
4057 MediumAttachment *pAttach = *it;
4058 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4059 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4060 if (pMedium.isNull())
4061 continue;
4062
4063 if (pMedium->i_getBase(&level) == medium)
4064 {
4065 /* skip the hard disk if its currently attached (we
4066 * cannot attach the same hard disk twice) */
4067 if (i_findAttachment(*mMediumAttachments.data(),
4068 pMedium))
4069 continue;
4070
4071 /* matched device, channel and bus (i.e. attached to the
4072 * same place) will win and immediately stop the search;
4073 * otherwise the attachment that has the youngest
4074 * descendant of medium will be used
4075 */
4076 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4077 {
4078 /* the simplest case: restore the whole attachment
4079 * and return, nothing else to do */
4080 mMediumAttachments->push_back(*it);
4081
4082 /* Reattach the medium to the VM. */
4083 if (fHotplug || fSilent)
4084 {
4085 mediumLock.release();
4086 treeLock.release();
4087 alock.release();
4088
4089 MediumLockList *pMediumLockList(new MediumLockList());
4090
4091 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4092 medium /* pToLockWrite */,
4093 false /* fMediumLockWriteAll */,
4094 NULL,
4095 *pMediumLockList);
4096 alock.acquire();
4097 if (FAILED(rc))
4098 delete pMediumLockList;
4099 else
4100 {
4101 mData->mSession.mLockedMedia.Unlock();
4102 alock.release();
4103 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4104 mData->mSession.mLockedMedia.Lock();
4105 alock.acquire();
4106 }
4107 alock.release();
4108
4109 if (SUCCEEDED(rc))
4110 {
4111 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4112 /* Remove lock list in case of error. */
4113 if (FAILED(rc))
4114 {
4115 mData->mSession.mLockedMedia.Unlock();
4116 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4117 mData->mSession.mLockedMedia.Lock();
4118 }
4119 }
4120 }
4121
4122 return S_OK;
4123 }
4124 else if ( foundIt == oldAtts.end()
4125 || level > foundLevel /* prefer younger */
4126 )
4127 {
4128 foundIt = it;
4129 foundLevel = level;
4130 }
4131 }
4132 }
4133
4134 if (foundIt != oldAtts.end())
4135 {
4136 /* use the previously attached hard disk */
4137 medium = (*foundIt)->i_getMedium();
4138 mediumCaller.attach(medium);
4139 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4140 mediumLock.attach(medium);
4141 /* not implicit, doesn't require association with this VM */
4142 fIndirect = false;
4143 associate = false;
4144 /* go right to the MediumAttachment creation */
4145 break;
4146 }
4147 }
4148
4149 /* must give up the medium lock and medium tree lock as below we
4150 * go over snapshots, which needs a lock with higher lock order. */
4151 mediumLock.release();
4152 treeLock.release();
4153
4154 /* then, search through snapshots for the best diff in the given
4155 * hard disk's chain to base the new diff on */
4156
4157 ComObjPtr<Medium> base;
4158 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4159 while (snap)
4160 {
4161 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4162
4163 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4164
4165 MediumAttachment *pAttachFound = NULL;
4166 uint32_t foundLevel = 0;
4167
4168 for (MediumAttachmentList::const_iterator
4169 it = snapAtts.begin();
4170 it != snapAtts.end();
4171 ++it)
4172 {
4173 MediumAttachment *pAttach = *it;
4174 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4175 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4176 if (pMedium.isNull())
4177 continue;
4178
4179 uint32_t level = 0;
4180 if (pMedium->i_getBase(&level) == medium)
4181 {
4182 /* matched device, channel and bus (i.e. attached to the
4183 * same place) will win and immediately stop the search;
4184 * otherwise the attachment that has the youngest
4185 * descendant of medium will be used
4186 */
4187 if ( pAttach->i_getDevice() == aDevice
4188 && pAttach->i_getPort() == aControllerPort
4189 && pAttach->i_getControllerName() == aName
4190 )
4191 {
4192 pAttachFound = pAttach;
4193 break;
4194 }
4195 else if ( !pAttachFound
4196 || level > foundLevel /* prefer younger */
4197 )
4198 {
4199 pAttachFound = pAttach;
4200 foundLevel = level;
4201 }
4202 }
4203 }
4204
4205 if (pAttachFound)
4206 {
4207 base = pAttachFound->i_getMedium();
4208 break;
4209 }
4210
4211 snap = snap->i_getParent();
4212 }
4213
4214 /* re-lock medium tree and the medium, as we need it below */
4215 treeLock.acquire();
4216 mediumLock.acquire();
4217
4218 /* found a suitable diff, use it as a base */
4219 if (!base.isNull())
4220 {
4221 medium = base;
4222 mediumCaller.attach(medium);
4223 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4224 mediumLock.attach(medium);
4225 }
4226 }
4227
4228 Utf8Str strFullSnapshotFolder;
4229 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4230
4231 ComObjPtr<Medium> diff;
4232 diff.createObject();
4233 // store this diff in the same registry as the parent
4234 Guid uuidRegistryParent;
4235 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4236 {
4237 // parent image has no registry: this can happen if we're attaching a new immutable
4238 // image that has not yet been attached (medium then points to the base and we're
4239 // creating the diff image for the immutable, and the parent is not yet registered);
4240 // put the parent in the machine registry then
4241 mediumLock.release();
4242 treeLock.release();
4243 alock.release();
4244 i_addMediumToRegistry(medium);
4245 alock.acquire();
4246 treeLock.acquire();
4247 mediumLock.acquire();
4248 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4249 }
4250 rc = diff->init(mParent,
4251 medium->i_getPreferredDiffFormat(),
4252 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4253 uuidRegistryParent,
4254 DeviceType_HardDisk);
4255 if (FAILED(rc)) return rc;
4256
4257 /* Apply the normal locking logic to the entire chain. */
4258 MediumLockList *pMediumLockList(new MediumLockList());
4259 mediumLock.release();
4260 treeLock.release();
4261 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4262 diff /* pToLockWrite */,
4263 false /* fMediumLockWriteAll */,
4264 medium,
4265 *pMediumLockList);
4266 treeLock.acquire();
4267 mediumLock.acquire();
4268 if (SUCCEEDED(rc))
4269 {
4270 mediumLock.release();
4271 treeLock.release();
4272 rc = pMediumLockList->Lock();
4273 treeLock.acquire();
4274 mediumLock.acquire();
4275 if (FAILED(rc))
4276 setError(rc,
4277 tr("Could not lock medium when creating diff '%s'"),
4278 diff->i_getLocationFull().c_str());
4279 else
4280 {
4281 /* will release the lock before the potentially lengthy
4282 * operation, so protect with the special state */
4283 MachineState_T oldState = mData->mMachineState;
4284 i_setMachineState(MachineState_SettingUp);
4285
4286 mediumLock.release();
4287 treeLock.release();
4288 alock.release();
4289
4290 rc = medium->i_createDiffStorage(diff,
4291 medium->i_getPreferredDiffVariant(),
4292 pMediumLockList,
4293 NULL /* aProgress */,
4294 true /* aWait */,
4295 false /* aNotify */);
4296
4297 alock.acquire();
4298 treeLock.acquire();
4299 mediumLock.acquire();
4300
4301 i_setMachineState(oldState);
4302 }
4303 }
4304
4305 /* Unlock the media and free the associated memory. */
4306 delete pMediumLockList;
4307
4308 if (FAILED(rc)) return rc;
4309
4310 /* use the created diff for the actual attachment */
4311 medium = diff;
4312 mediumCaller.attach(medium);
4313 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4314 mediumLock.attach(medium);
4315 }
4316 while (0);
4317
4318 ComObjPtr<MediumAttachment> attachment;
4319 attachment.createObject();
4320 rc = attachment->init(this,
4321 medium,
4322 aName,
4323 aControllerPort,
4324 aDevice,
4325 aType,
4326 fIndirect,
4327 false /* fPassthrough */,
4328 false /* fTempEject */,
4329 false /* fNonRotational */,
4330 false /* fDiscard */,
4331 fHotplug /* fHotPluggable */,
4332 Utf8Str::Empty);
4333 if (FAILED(rc)) return rc;
4334
4335 if (associate && !medium.isNull())
4336 {
4337 // as the last step, associate the medium to the VM
4338 rc = medium->i_addBackReference(mData->mUuid);
4339 // here we can fail because of Deleting, or being in process of creating a Diff
4340 if (FAILED(rc)) return rc;
4341
4342 mediumLock.release();
4343 treeLock.release();
4344 alock.release();
4345 i_addMediumToRegistry(medium);
4346 alock.acquire();
4347 treeLock.acquire();
4348 mediumLock.acquire();
4349 }
4350
4351 /* success: finally remember the attachment */
4352 i_setModified(IsModified_Storage);
4353 mMediumAttachments.backup();
4354 mMediumAttachments->push_back(attachment);
4355
4356 mediumLock.release();
4357 treeLock.release();
4358 alock.release();
4359
4360 if (fHotplug || fSilent)
4361 {
4362 if (!medium.isNull())
4363 {
4364 MediumLockList *pMediumLockList(new MediumLockList());
4365
4366 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4367 medium /* pToLockWrite */,
4368 false /* fMediumLockWriteAll */,
4369 NULL,
4370 *pMediumLockList);
4371 alock.acquire();
4372 if (FAILED(rc))
4373 delete pMediumLockList;
4374 else
4375 {
4376 mData->mSession.mLockedMedia.Unlock();
4377 alock.release();
4378 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4379 mData->mSession.mLockedMedia.Lock();
4380 alock.acquire();
4381 }
4382 alock.release();
4383 }
4384
4385 if (SUCCEEDED(rc))
4386 {
4387 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4388 /* Remove lock list in case of error. */
4389 if (FAILED(rc))
4390 {
4391 mData->mSession.mLockedMedia.Unlock();
4392 mData->mSession.mLockedMedia.Remove(attachment);
4393 mData->mSession.mLockedMedia.Lock();
4394 }
4395 }
4396 }
4397
4398 /* Save modified registries, but skip this machine as it's the caller's
4399 * job to save its settings like all other settings changes. */
4400 mParent->i_unmarkRegistryModified(i_getId());
4401 mParent->i_saveModifiedRegistries();
4402
4403 if (SUCCEEDED(rc))
4404 {
4405 if (fIndirect && medium != aM)
4406 mParent->i_onMediumConfigChanged(medium);
4407 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4408 }
4409
4410 return rc;
4411}
4412
4413HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4414 LONG aDevice)
4415{
4416 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4417 aName.c_str(), aControllerPort, aDevice));
4418
4419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4420
4421 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4422 if (FAILED(rc)) return rc;
4423
4424 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4425
4426 /* Check for an existing controller. */
4427 ComObjPtr<StorageController> ctl;
4428 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4429 if (FAILED(rc)) return rc;
4430
4431 StorageControllerType_T ctrlType;
4432 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4433 if (FAILED(rc))
4434 return setError(E_FAIL,
4435 tr("Could not get type of controller '%s'"),
4436 aName.c_str());
4437
4438 bool fSilent = false;
4439 Utf8Str strReconfig;
4440
4441 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4442 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4443 if ( mData->mMachineState == MachineState_Paused
4444 && strReconfig == "1")
4445 fSilent = true;
4446
4447 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4448 bool fHotplug = false;
4449 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4450 fHotplug = true;
4451
4452 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4453 return setError(VBOX_E_INVALID_VM_STATE,
4454 tr("Controller '%s' does not support hot-plugging"),
4455 aName.c_str());
4456
4457 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4458 aName,
4459 aControllerPort,
4460 aDevice);
4461 if (!pAttach)
4462 return setError(VBOX_E_OBJECT_NOT_FOUND,
4463 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4464 aDevice, aControllerPort, aName.c_str());
4465
4466 if (fHotplug && !pAttach->i_getHotPluggable())
4467 return setError(VBOX_E_NOT_SUPPORTED,
4468 tr("The device slot %d on port %d of controller '%s' does not support hot-plugging"),
4469 aDevice, aControllerPort, aName.c_str());
4470
4471 /*
4472 * The VM has to detach the device before we delete any implicit diffs.
4473 * If this fails we can roll back without loosing data.
4474 */
4475 if (fHotplug || fSilent)
4476 {
4477 alock.release();
4478 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4479 alock.acquire();
4480 }
4481 if (FAILED(rc)) return rc;
4482
4483 /* If we are here everything went well and we can delete the implicit now. */
4484 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4485
4486 alock.release();
4487
4488 /* Save modified registries, but skip this machine as it's the caller's
4489 * job to save its settings like all other settings changes. */
4490 mParent->i_unmarkRegistryModified(i_getId());
4491 mParent->i_saveModifiedRegistries();
4492
4493 if (SUCCEEDED(rc))
4494 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4495
4496 return rc;
4497}
4498
4499HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4500 LONG aDevice, BOOL aPassthrough)
4501{
4502 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4503 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4504
4505 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4506
4507 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4508 if (FAILED(rc)) return rc;
4509
4510 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4511
4512 /* Check for an existing controller. */
4513 ComObjPtr<StorageController> ctl;
4514 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4515 if (FAILED(rc)) return rc;
4516
4517 StorageControllerType_T ctrlType;
4518 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4519 if (FAILED(rc))
4520 return setError(E_FAIL,
4521 tr("Could not get type of controller '%s'"),
4522 aName.c_str());
4523
4524 bool fSilent = false;
4525 Utf8Str strReconfig;
4526
4527 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4528 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4529 if ( mData->mMachineState == MachineState_Paused
4530 && strReconfig == "1")
4531 fSilent = true;
4532
4533 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4534 bool fHotplug = false;
4535 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4536 fHotplug = true;
4537
4538 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4539 return setError(VBOX_E_INVALID_VM_STATE,
4540 tr("Controller '%s' does not support hot-plugging which is required to change the passthrough setting while the VM is running"),
4541 aName.c_str());
4542
4543 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4544 aName,
4545 aControllerPort,
4546 aDevice);
4547 if (!pAttach)
4548 return setError(VBOX_E_OBJECT_NOT_FOUND,
4549 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4550 aDevice, aControllerPort, aName.c_str());
4551
4552
4553 i_setModified(IsModified_Storage);
4554 mMediumAttachments.backup();
4555
4556 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4557
4558 if (pAttach->i_getType() != DeviceType_DVD)
4559 return setError(E_INVALIDARG,
4560 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4561 aDevice, aControllerPort, aName.c_str());
4562
4563 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4564
4565 pAttach->i_updatePassthrough(!!aPassthrough);
4566
4567 attLock.release();
4568 alock.release();
4569 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4570 if (SUCCEEDED(rc) && fValueChanged)
4571 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4572
4573 return rc;
4574}
4575
4576HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4577 LONG aDevice, BOOL aTemporaryEject)
4578{
4579
4580 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4581 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4582
4583 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4584
4585 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4586 if (FAILED(rc)) return rc;
4587
4588 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4589 aName,
4590 aControllerPort,
4591 aDevice);
4592 if (!pAttach)
4593 return setError(VBOX_E_OBJECT_NOT_FOUND,
4594 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4595 aDevice, aControllerPort, aName.c_str());
4596
4597
4598 i_setModified(IsModified_Storage);
4599 mMediumAttachments.backup();
4600
4601 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4602
4603 if (pAttach->i_getType() != DeviceType_DVD)
4604 return setError(E_INVALIDARG,
4605 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4606 aDevice, aControllerPort, aName.c_str());
4607 pAttach->i_updateTempEject(!!aTemporaryEject);
4608
4609 return S_OK;
4610}
4611
4612HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4613 LONG aDevice, BOOL aNonRotational)
4614{
4615
4616 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4617 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4618
4619 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4620
4621 HRESULT rc = i_checkStateDependency(MutableStateDep);
4622 if (FAILED(rc)) return rc;
4623
4624 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4625
4626 if (Global::IsOnlineOrTransient(mData->mMachineState))
4627 return setError(VBOX_E_INVALID_VM_STATE,
4628 tr("Invalid machine state: %s"),
4629 Global::stringifyMachineState(mData->mMachineState));
4630
4631 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4632 aName,
4633 aControllerPort,
4634 aDevice);
4635 if (!pAttach)
4636 return setError(VBOX_E_OBJECT_NOT_FOUND,
4637 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4638 aDevice, aControllerPort, aName.c_str());
4639
4640
4641 i_setModified(IsModified_Storage);
4642 mMediumAttachments.backup();
4643
4644 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4645
4646 if (pAttach->i_getType() != DeviceType_HardDisk)
4647 return setError(E_INVALIDARG,
4648 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"),
4649 aDevice, aControllerPort, aName.c_str());
4650 pAttach->i_updateNonRotational(!!aNonRotational);
4651
4652 return S_OK;
4653}
4654
4655HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4656 LONG aDevice, BOOL aDiscard)
4657{
4658
4659 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4660 aName.c_str(), aControllerPort, aDevice, aDiscard));
4661
4662 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4663
4664 HRESULT rc = i_checkStateDependency(MutableStateDep);
4665 if (FAILED(rc)) return rc;
4666
4667 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4668
4669 if (Global::IsOnlineOrTransient(mData->mMachineState))
4670 return setError(VBOX_E_INVALID_VM_STATE,
4671 tr("Invalid machine state: %s"),
4672 Global::stringifyMachineState(mData->mMachineState));
4673
4674 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4675 aName,
4676 aControllerPort,
4677 aDevice);
4678 if (!pAttach)
4679 return setError(VBOX_E_OBJECT_NOT_FOUND,
4680 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4681 aDevice, aControllerPort, aName.c_str());
4682
4683
4684 i_setModified(IsModified_Storage);
4685 mMediumAttachments.backup();
4686
4687 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4688
4689 if (pAttach->i_getType() != DeviceType_HardDisk)
4690 return setError(E_INVALIDARG,
4691 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"),
4692 aDevice, aControllerPort, aName.c_str());
4693 pAttach->i_updateDiscard(!!aDiscard);
4694
4695 return S_OK;
4696}
4697
4698HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4699 LONG aDevice, BOOL aHotPluggable)
4700{
4701 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4702 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4703
4704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4705
4706 HRESULT rc = i_checkStateDependency(MutableStateDep);
4707 if (FAILED(rc)) return rc;
4708
4709 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4710
4711 if (Global::IsOnlineOrTransient(mData->mMachineState))
4712 return setError(VBOX_E_INVALID_VM_STATE,
4713 tr("Invalid machine state: %s"),
4714 Global::stringifyMachineState(mData->mMachineState));
4715
4716 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4717 aName,
4718 aControllerPort,
4719 aDevice);
4720 if (!pAttach)
4721 return setError(VBOX_E_OBJECT_NOT_FOUND,
4722 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4723 aDevice, aControllerPort, aName.c_str());
4724
4725 /* Check for an existing controller. */
4726 ComObjPtr<StorageController> ctl;
4727 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4728 if (FAILED(rc)) return rc;
4729
4730 StorageControllerType_T ctrlType;
4731 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4732 if (FAILED(rc))
4733 return setError(E_FAIL,
4734 tr("Could not get type of controller '%s'"),
4735 aName.c_str());
4736
4737 if (!i_isControllerHotplugCapable(ctrlType))
4738 return setError(VBOX_E_NOT_SUPPORTED,
4739 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4740 aName.c_str());
4741
4742 /* silently ignore attempts to modify the hot-plug status of USB devices */
4743 if (ctrlType == StorageControllerType_USB)
4744 return S_OK;
4745
4746 i_setModified(IsModified_Storage);
4747 mMediumAttachments.backup();
4748
4749 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4750
4751 if (pAttach->i_getType() == DeviceType_Floppy)
4752 return setError(E_INVALIDARG,
4753 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"),
4754 aDevice, aControllerPort, aName.c_str());
4755 pAttach->i_updateHotPluggable(!!aHotPluggable);
4756
4757 return S_OK;
4758}
4759
4760HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4761 LONG aDevice)
4762{
4763 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4764 aName.c_str(), aControllerPort, aDevice));
4765
4766 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4767}
4768
4769HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4770 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4771{
4772 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4773 aName.c_str(), aControllerPort, aDevice));
4774
4775 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4776
4777 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4778 if (FAILED(rc)) return rc;
4779
4780 if (Global::IsOnlineOrTransient(mData->mMachineState))
4781 return setError(VBOX_E_INVALID_VM_STATE,
4782 tr("Invalid machine state: %s"),
4783 Global::stringifyMachineState(mData->mMachineState));
4784
4785 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4786 aName,
4787 aControllerPort,
4788 aDevice);
4789 if (!pAttach)
4790 return setError(VBOX_E_OBJECT_NOT_FOUND,
4791 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4792 aDevice, aControllerPort, aName.c_str());
4793
4794
4795 i_setModified(IsModified_Storage);
4796 mMediumAttachments.backup();
4797
4798 IBandwidthGroup *iB = aBandwidthGroup;
4799 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4800 if (aBandwidthGroup && group.isNull())
4801 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4802
4803 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4804
4805 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4806 if (strBandwidthGroupOld.isNotEmpty())
4807 {
4808 /* Get the bandwidth group object and release it - this must not fail. */
4809 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4810 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4811 Assert(SUCCEEDED(rc));
4812
4813 pBandwidthGroupOld->i_release();
4814 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4815 }
4816
4817 if (!group.isNull())
4818 {
4819 group->i_reference();
4820 pAttach->i_updateBandwidthGroup(group->i_getName());
4821 }
4822
4823 return S_OK;
4824}
4825
4826HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4827 LONG aControllerPort,
4828 LONG aDevice,
4829 DeviceType_T aType)
4830{
4831 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4832 aName.c_str(), aControllerPort, aDevice, aType));
4833
4834 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4835}
4836
4837
4838HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4839 LONG aControllerPort,
4840 LONG aDevice,
4841 BOOL aForce)
4842{
4843 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4844 aName.c_str(), aControllerPort, aForce));
4845
4846 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4847}
4848
4849HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4850 LONG aControllerPort,
4851 LONG aDevice,
4852 const ComPtr<IMedium> &aMedium,
4853 BOOL aForce)
4854{
4855 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4856 aName.c_str(), aControllerPort, aDevice, aForce));
4857
4858 // request the host lock first, since might be calling Host methods for getting host drives;
4859 // next, protect the media tree all the while we're in here, as well as our member variables
4860 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4861 this->lockHandle(),
4862 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4863
4864 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4865 if (FAILED(hrc)) return hrc;
4866
4867 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4868 aName,
4869 aControllerPort,
4870 aDevice);
4871 if (pAttach.isNull())
4872 return setError(VBOX_E_OBJECT_NOT_FOUND,
4873 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4874 aDevice, aControllerPort, aName.c_str());
4875
4876 /* Remember previously mounted medium. The medium before taking the
4877 * backup is not necessarily the same thing. */
4878 ComObjPtr<Medium> oldmedium;
4879 oldmedium = pAttach->i_getMedium();
4880
4881 IMedium *iM = aMedium;
4882 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4883 if (aMedium && pMedium.isNull())
4884 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4885
4886 /* Check if potential medium is already mounted */
4887 if (pMedium == oldmedium)
4888 return S_OK;
4889
4890 AutoCaller mediumCaller(pMedium);
4891 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4892
4893 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4894 if (pMedium)
4895 {
4896 DeviceType_T mediumType = pAttach->i_getType();
4897 switch (mediumType)
4898 {
4899 case DeviceType_DVD:
4900 case DeviceType_Floppy:
4901 break;
4902
4903 default:
4904 return setError(VBOX_E_INVALID_OBJECT_STATE,
4905 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4906 aControllerPort,
4907 aDevice,
4908 aName.c_str());
4909 }
4910 }
4911
4912 i_setModified(IsModified_Storage);
4913 mMediumAttachments.backup();
4914
4915 {
4916 // The backup operation makes the pAttach reference point to the
4917 // old settings. Re-get the correct reference.
4918 pAttach = i_findAttachment(*mMediumAttachments.data(),
4919 aName,
4920 aControllerPort,
4921 aDevice);
4922 if (!oldmedium.isNull())
4923 oldmedium->i_removeBackReference(mData->mUuid);
4924 if (!pMedium.isNull())
4925 {
4926 pMedium->i_addBackReference(mData->mUuid);
4927
4928 mediumLock.release();
4929 multiLock.release();
4930 i_addMediumToRegistry(pMedium);
4931 multiLock.acquire();
4932 mediumLock.acquire();
4933 }
4934
4935 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4936 pAttach->i_updateMedium(pMedium);
4937 }
4938
4939 i_setModified(IsModified_Storage);
4940
4941 mediumLock.release();
4942 multiLock.release();
4943 HRESULT rc = i_onMediumChange(pAttach, aForce);
4944 multiLock.acquire();
4945 mediumLock.acquire();
4946
4947 /* On error roll back this change only. */
4948 if (FAILED(rc))
4949 {
4950 if (!pMedium.isNull())
4951 pMedium->i_removeBackReference(mData->mUuid);
4952 pAttach = i_findAttachment(*mMediumAttachments.data(),
4953 aName,
4954 aControllerPort,
4955 aDevice);
4956 /* If the attachment is gone in the meantime, bail out. */
4957 if (pAttach.isNull())
4958 return rc;
4959 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4960 if (!oldmedium.isNull())
4961 oldmedium->i_addBackReference(mData->mUuid);
4962 pAttach->i_updateMedium(oldmedium);
4963 }
4964
4965 mediumLock.release();
4966 multiLock.release();
4967
4968 /* Save modified registries, but skip this machine as it's the caller's
4969 * job to save its settings like all other settings changes. */
4970 mParent->i_unmarkRegistryModified(i_getId());
4971 mParent->i_saveModifiedRegistries();
4972
4973 return rc;
4974}
4975HRESULT Machine::getMedium(const com::Utf8Str &aName,
4976 LONG aControllerPort,
4977 LONG aDevice,
4978 ComPtr<IMedium> &aMedium)
4979{
4980 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4981 aName.c_str(), aControllerPort, aDevice));
4982
4983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4984
4985 aMedium = NULL;
4986
4987 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4988 aName,
4989 aControllerPort,
4990 aDevice);
4991 if (pAttach.isNull())
4992 return setError(VBOX_E_OBJECT_NOT_FOUND,
4993 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4994 aDevice, aControllerPort, aName.c_str());
4995
4996 aMedium = pAttach->i_getMedium();
4997
4998 return S_OK;
4999}
5000
5001HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
5002{
5003 if (aSlot < RT_ELEMENTS(mSerialPorts))
5004 {
5005 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5006 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5007 return S_OK;
5008 }
5009 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
5010}
5011
5012HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
5013{
5014 if (aSlot < RT_ELEMENTS(mParallelPorts))
5015 {
5016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5017 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5018 return S_OK;
5019 }
5020 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
5021}
5022
5023
5024HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
5025{
5026 /* Do not assert if slot is out of range, just return the advertised
5027 status. testdriver/vbox.py triggers this in logVmInfo. */
5028 if (aSlot >= mNetworkAdapters.size())
5029 return setError(E_INVALIDARG,
5030 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
5031 aSlot, mNetworkAdapters.size());
5032
5033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5034
5035 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
5036
5037 return S_OK;
5038}
5039
5040HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
5041{
5042 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5043
5044 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
5045 size_t i = 0;
5046 for (settings::StringsMap::const_iterator
5047 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5048 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5049 ++it, ++i)
5050 aKeys[i] = it->first;
5051
5052 return S_OK;
5053}
5054
5055 /**
5056 * @note Locks this object for reading.
5057 */
5058HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
5059 com::Utf8Str &aValue)
5060{
5061 /* start with nothing found */
5062 aValue = "";
5063
5064 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5065
5066 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5067 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5068 // found:
5069 aValue = it->second; // source is a Utf8Str
5070
5071 /* return the result to caller (may be empty) */
5072 return S_OK;
5073}
5074
5075 /**
5076 * @note Locks mParent for writing + this object for writing.
5077 */
5078HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5079{
5080 /* Because control characters in aKey have caused problems in the settings
5081 * they are rejected unless the key should be deleted. */
5082 if (!aValue.isEmpty())
5083 {
5084 for (size_t i = 0; i < aKey.length(); ++i)
5085 {
5086 char ch = aKey[i];
5087 if (RTLocCIsCntrl(ch))
5088 return E_INVALIDARG;
5089 }
5090 }
5091
5092 Utf8Str strOldValue; // empty
5093
5094 // locking note: we only hold the read lock briefly to look up the old value,
5095 // then release it and call the onExtraCanChange callbacks. There is a small
5096 // chance of a race insofar as the callback might be called twice if two callers
5097 // change the same key at the same time, but that's a much better solution
5098 // than the deadlock we had here before. The actual changing of the extradata
5099 // is then performed under the write lock and race-free.
5100
5101 // look up the old value first; if nothing has changed then we need not do anything
5102 {
5103 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5104
5105 // For snapshots don't even think about allowing changes, extradata
5106 // is global for a machine, so there is nothing snapshot specific.
5107 if (i_isSnapshotMachine())
5108 return setError(VBOX_E_INVALID_VM_STATE,
5109 tr("Cannot set extradata for a snapshot"));
5110
5111 // check if the right IMachine instance is used
5112 if (mData->mRegistered && !i_isSessionMachine())
5113 return setError(VBOX_E_INVALID_VM_STATE,
5114 tr("Cannot set extradata for an immutable machine"));
5115
5116 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5117 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5118 strOldValue = it->second;
5119 }
5120
5121 bool fChanged;
5122 if ((fChanged = (strOldValue != aValue)))
5123 {
5124 // ask for permission from all listeners outside the locks;
5125 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5126 // lock to copy the list of callbacks to invoke
5127 Bstr bstrError;
5128 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
5129 {
5130 const char *sep = bstrError.isEmpty() ? "" : ": ";
5131 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
5132 return setError(E_ACCESSDENIED,
5133 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5134 aKey.c_str(),
5135 aValue.c_str(),
5136 sep,
5137 bstrError.raw());
5138 }
5139
5140 // data is changing and change not vetoed: then write it out under the lock
5141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5142
5143 if (aValue.isEmpty())
5144 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5145 else
5146 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5147 // creates a new key if needed
5148
5149 bool fNeedsGlobalSaveSettings = false;
5150 // This saving of settings is tricky: there is no "old state" for the
5151 // extradata items at all (unlike all other settings), so the old/new
5152 // settings comparison would give a wrong result!
5153 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
5154
5155 if (fNeedsGlobalSaveSettings)
5156 {
5157 // save the global settings; for that we should hold only the VirtualBox lock
5158 alock.release();
5159 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5160 mParent->i_saveSettings();
5161 }
5162 }
5163
5164 // fire notification outside the lock
5165 if (fChanged)
5166 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
5167
5168 return S_OK;
5169}
5170
5171HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5172{
5173 aProgress = NULL;
5174 NOREF(aSettingsFilePath);
5175 ReturnComNotImplemented();
5176}
5177
5178HRESULT Machine::saveSettings()
5179{
5180 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5181
5182 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5183 if (FAILED(rc)) return rc;
5184
5185 /* the settings file path may never be null */
5186 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5187
5188 /* save all VM data excluding snapshots */
5189 bool fNeedsGlobalSaveSettings = false;
5190 rc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
5191 mlock.release();
5192
5193 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5194 {
5195 // save the global settings; for that we should hold only the VirtualBox lock
5196 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5197 rc = mParent->i_saveSettings();
5198 }
5199
5200 return rc;
5201}
5202
5203
5204HRESULT Machine::discardSettings()
5205{
5206 /*
5207 * We need to take the machine list lock here as well as the machine one
5208 * or we'll get into trouble should any media stuff require rolling back.
5209 *
5210 * Details:
5211 *
5212 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5213 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5214 * 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]
5215 * 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
5216 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5217 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5218 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5219 * 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
5220 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5221 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5222 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5223 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5224 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5225 * 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]
5226 * 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] (*)
5227 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5228 * 0:005> k
5229 * # Child-SP RetAddr Call Site
5230 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5231 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5232 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5233 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5234 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5235 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5236 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5237 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5238 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5239 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5240 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5241 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5242 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5243 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5244 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5245 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5246 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5247 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5248 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5249 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5250 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5251 *
5252 */
5253 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5254 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5255
5256 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5257 if (FAILED(rc)) return rc;
5258
5259 /*
5260 * during this rollback, the session will be notified if data has
5261 * been actually changed
5262 */
5263 i_rollback(true /* aNotify */);
5264
5265 return S_OK;
5266}
5267
5268/** @note Locks objects! */
5269HRESULT Machine::unregister(AutoCaller &autoCaller,
5270 CleanupMode_T aCleanupMode,
5271 std::vector<ComPtr<IMedium> > &aMedia)
5272{
5273 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5274
5275 Guid id(i_getId());
5276
5277 if (mData->mSession.mState != SessionState_Unlocked)
5278 return setError(VBOX_E_INVALID_OBJECT_STATE,
5279 tr("Cannot unregister the machine '%s' while it is locked"),
5280 mUserData->s.strName.c_str());
5281
5282 // wait for state dependents to drop to zero
5283 i_ensureNoStateDependencies(alock);
5284
5285 if (!mData->mAccessible)
5286 {
5287 // inaccessible machines can only be unregistered; uninitialize ourselves
5288 // here because currently there may be no unregistered that are inaccessible
5289 // (this state combination is not supported). Note releasing the caller and
5290 // leaving the lock before calling uninit()
5291 alock.release();
5292 autoCaller.release();
5293
5294 uninit();
5295
5296 mParent->i_unregisterMachine(this, CleanupMode_UnregisterOnly, id);
5297 // calls VirtualBox::i_saveSettings()
5298
5299 return S_OK;
5300 }
5301
5302 HRESULT rc = S_OK;
5303 mData->llFilesToDelete.clear();
5304
5305 if (!mSSData->strStateFilePath.isEmpty())
5306 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5307
5308 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
5309 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
5310 mData->llFilesToDelete.push_back(strNVRAMFile);
5311
5312 // This list collects the medium objects from all medium attachments
5313 // which we will detach from the machine and its snapshots, in a specific
5314 // order which allows for closing all media without getting "media in use"
5315 // errors, simply by going through the list from the front to the back:
5316 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5317 // and must be closed before the parent media from the snapshots, or closing the parents
5318 // will fail because they still have children);
5319 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5320 // the root ("first") snapshot of the machine.
5321 MediaList llMedia;
5322
5323 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5324 && mMediumAttachments->size()
5325 )
5326 {
5327 // we have media attachments: detach them all and add the Medium objects to our list
5328 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5329 }
5330
5331 if (mData->mFirstSnapshot)
5332 {
5333 // add the media from the medium attachments of the snapshots to
5334 // llMedia as well, after the "main" machine media;
5335 // Snapshot::uninitAll() calls Machine::detachAllMedia() for each
5336 // snapshot machine, depth first.
5337
5338 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5339 MachineState_T oldState = mData->mMachineState;
5340 mData->mMachineState = MachineState_DeletingSnapshot;
5341
5342 // make a copy of the first snapshot reference so the refcount does not
5343 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
5344 // (would hang due to the AutoCaller voodoo)
5345 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5346
5347 // GO!
5348 pFirstSnapshot->i_uninitAll(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5349
5350 mData->mMachineState = oldState;
5351 }
5352
5353 if (FAILED(rc))
5354 {
5355 i_rollbackMedia();
5356 return rc;
5357 }
5358
5359 // commit all the media changes made above
5360 i_commitMedia();
5361
5362 mData->mRegistered = false;
5363
5364 // machine lock no longer needed
5365 alock.release();
5366
5367 /* Make sure that the settings of the current VM are not saved, because
5368 * they are rather crippled at this point to meet the cleanup expectations
5369 * and there's no point destroying the VM config on disk just because. */
5370 mParent->i_unmarkRegistryModified(id);
5371
5372 // return media to caller
5373 aMedia.resize(llMedia.size());
5374 size_t i = 0;
5375 for (MediaList::const_iterator
5376 it = llMedia.begin();
5377 it != llMedia.end();
5378 ++it, ++i)
5379 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5380
5381 mParent->i_unregisterMachine(this, aCleanupMode, id);
5382 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5383
5384 return S_OK;
5385}
5386
5387/**
5388 * Task record for deleting a machine config.
5389 */
5390class Machine::DeleteConfigTask
5391 : public Machine::Task
5392{
5393public:
5394 DeleteConfigTask(Machine *m,
5395 Progress *p,
5396 const Utf8Str &t,
5397 const RTCList<ComPtr<IMedium> > &llMediums,
5398 const StringsList &llFilesToDelete)
5399 : Task(m, p, t),
5400 m_llMediums(llMediums),
5401 m_llFilesToDelete(llFilesToDelete)
5402 {}
5403
5404private:
5405 void handler()
5406 {
5407 try
5408 {
5409 m_pMachine->i_deleteConfigHandler(*this);
5410 }
5411 catch (...)
5412 {
5413 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5414 }
5415 }
5416
5417 RTCList<ComPtr<IMedium> > m_llMediums;
5418 StringsList m_llFilesToDelete;
5419
5420 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5421};
5422
5423/**
5424 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5425 * SessionMachine::taskHandler().
5426 *
5427 * @note Locks this object for writing.
5428 *
5429 * @param task
5430 * @return
5431 */
5432void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5433{
5434 LogFlowThisFuncEnter();
5435
5436 AutoCaller autoCaller(this);
5437 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5438 if (FAILED(autoCaller.rc()))
5439 {
5440 /* we might have been uninitialized because the session was accidentally
5441 * closed by the client, so don't assert */
5442 HRESULT rc = setError(E_FAIL,
5443 tr("The session has been accidentally closed"));
5444 task.m_pProgress->i_notifyComplete(rc);
5445 LogFlowThisFuncLeave();
5446 return;
5447 }
5448
5449 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5450
5451 HRESULT rc = S_OK;
5452
5453 try
5454 {
5455 ULONG uLogHistoryCount = 3;
5456 ComPtr<ISystemProperties> systemProperties;
5457 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5458 if (FAILED(rc)) throw rc;
5459
5460 if (!systemProperties.isNull())
5461 {
5462 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5463 if (FAILED(rc)) throw rc;
5464 }
5465
5466 MachineState_T oldState = mData->mMachineState;
5467 i_setMachineState(MachineState_SettingUp);
5468 alock.release();
5469 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5470 {
5471 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5472 {
5473 AutoCaller mac(pMedium);
5474 if (FAILED(mac.rc())) throw mac.rc();
5475 Utf8Str strLocation = pMedium->i_getLocationFull();
5476 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5477 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5478 if (FAILED(rc)) throw rc;
5479 }
5480 if (pMedium->i_isMediumFormatFile())
5481 {
5482 ComPtr<IProgress> pProgress2;
5483 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5484 if (FAILED(rc)) throw rc;
5485 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5486 if (FAILED(rc)) throw rc;
5487 }
5488
5489 /* Close the medium, deliberately without checking the return
5490 * code, and without leaving any trace in the error info, as
5491 * a failure here is a very minor issue, which shouldn't happen
5492 * as above we even managed to delete the medium. */
5493 {
5494 ErrorInfoKeeper eik;
5495 pMedium->Close();
5496 }
5497 }
5498 i_setMachineState(oldState);
5499 alock.acquire();
5500
5501 // delete the files pushed on the task list by Machine::Delete()
5502 // (this includes saved states of the machine and snapshots and
5503 // medium storage files from the IMedium list passed in, and the
5504 // machine XML file)
5505 for (StringsList::const_iterator
5506 it = task.m_llFilesToDelete.begin();
5507 it != task.m_llFilesToDelete.end();
5508 ++it)
5509 {
5510 const Utf8Str &strFile = *it;
5511 LogFunc(("Deleting file %s\n", strFile.c_str()));
5512 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5513 if (FAILED(rc)) throw rc;
5514 i_deleteFile(strFile);
5515 }
5516
5517 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5518 if (FAILED(rc)) throw rc;
5519
5520 /* delete the settings only when the file actually exists */
5521 if (mData->pMachineConfigFile->fileExists())
5522 {
5523 /* Delete any backup or uncommitted XML files. Ignore failures.
5524 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5525 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5526 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5527 i_deleteFile(otherXml, true /* fIgnoreFailures */);
5528 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5529 i_deleteFile(otherXml, true /* fIgnoreFailures */);
5530
5531 /* delete the Logs folder, nothing important should be left
5532 * there (we don't check for errors because the user might have
5533 * some private files there that we don't want to delete) */
5534 Utf8Str logFolder;
5535 getLogFolder(logFolder);
5536 Assert(logFolder.length());
5537 if (RTDirExists(logFolder.c_str()))
5538 {
5539 /* Delete all VBox.log[.N] files from the Logs folder
5540 * (this must be in sync with the rotation logic in
5541 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5542 * files that may have been created by the GUI. */
5543 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5544 i_deleteFile(log, true /* fIgnoreFailures */);
5545 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5546 i_deleteFile(log, true /* fIgnoreFailures */);
5547 for (ULONG i = uLogHistoryCount; i > 0; i--)
5548 {
5549 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5550 i_deleteFile(log, true /* fIgnoreFailures */);
5551 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5552 i_deleteFile(log, true /* fIgnoreFailures */);
5553 }
5554 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5555 i_deleteFile(log, true /* fIgnoreFailures */);
5556#if defined(RT_OS_WINDOWS)
5557 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5558 i_deleteFile(log, true /* fIgnoreFailures */);
5559 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5560 i_deleteFile(log, true /* fIgnoreFailures */);
5561#endif
5562
5563 RTDirRemove(logFolder.c_str());
5564 }
5565
5566 /* delete the Snapshots folder, nothing important should be left
5567 * there (we don't check for errors because the user might have
5568 * some private files there that we don't want to delete) */
5569 Utf8Str strFullSnapshotFolder;
5570 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5571 Assert(!strFullSnapshotFolder.isEmpty());
5572 if (RTDirExists(strFullSnapshotFolder.c_str()))
5573 RTDirRemove(strFullSnapshotFolder.c_str());
5574
5575 // delete the directory that contains the settings file, but only
5576 // if it matches the VM name
5577 Utf8Str settingsDir;
5578 if (i_isInOwnDir(&settingsDir))
5579 RTDirRemove(settingsDir.c_str());
5580 }
5581
5582 alock.release();
5583
5584 mParent->i_saveModifiedRegistries();
5585 }
5586 catch (HRESULT aRC) { rc = aRC; }
5587
5588 task.m_pProgress->i_notifyComplete(rc);
5589
5590 LogFlowThisFuncLeave();
5591}
5592
5593HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5594{
5595 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5596
5597 HRESULT rc = i_checkStateDependency(MutableStateDep);
5598 if (FAILED(rc)) return rc;
5599
5600 if (mData->mRegistered)
5601 return setError(VBOX_E_INVALID_VM_STATE,
5602 tr("Cannot delete settings of a registered machine"));
5603
5604 // collect files to delete
5605 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5606 // machine config file
5607 if (mData->pMachineConfigFile->fileExists())
5608 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5609 // backup of machine config file
5610 Utf8Str strTmp(mData->m_strConfigFileFull);
5611 strTmp.append("-prev");
5612 if (RTFileExists(strTmp.c_str()))
5613 llFilesToDelete.push_back(strTmp);
5614
5615 RTCList<ComPtr<IMedium> > llMediums;
5616 for (size_t i = 0; i < aMedia.size(); ++i)
5617 {
5618 IMedium *pIMedium(aMedia[i]);
5619 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5620 if (pMedium.isNull())
5621 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
5622 SafeArray<BSTR> ids;
5623 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5624 if (FAILED(rc)) return rc;
5625 /* At this point the medium should not have any back references
5626 * anymore. If it has it is attached to another VM and *must* not
5627 * deleted. */
5628 if (ids.size() < 1)
5629 llMediums.append(pMedium);
5630 }
5631
5632 ComObjPtr<Progress> pProgress;
5633 pProgress.createObject();
5634 rc = pProgress->init(i_getVirtualBox(),
5635 static_cast<IMachine*>(this) /* aInitiator */,
5636 tr("Deleting files"),
5637 true /* fCancellable */,
5638 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5639 tr("Collecting file inventory"));
5640 if (FAILED(rc))
5641 return rc;
5642
5643 /* create and start the task on a separate thread (note that it will not
5644 * start working until we release alock) */
5645 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5646 rc = pTask->createThread();
5647 pTask = NULL;
5648 if (FAILED(rc))
5649 return rc;
5650
5651 pProgress.queryInterfaceTo(aProgress.asOutParam());
5652
5653 LogFlowFuncLeave();
5654
5655 return S_OK;
5656}
5657
5658HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5659{
5660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5661
5662 ComObjPtr<Snapshot> pSnapshot;
5663 HRESULT rc;
5664
5665 if (aNameOrId.isEmpty())
5666 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5667 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5668 else
5669 {
5670 Guid uuid(aNameOrId);
5671 if (uuid.isValid())
5672 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5673 else
5674 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5675 }
5676 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5677
5678 return rc;
5679}
5680
5681HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5682 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5683{
5684 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5685
5686 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5687 if (FAILED(rc)) return rc;
5688
5689 ComObjPtr<SharedFolder> sharedFolder;
5690 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5691 if (SUCCEEDED(rc))
5692 return setError(VBOX_E_OBJECT_IN_USE,
5693 tr("Shared folder named '%s' already exists"),
5694 aName.c_str());
5695
5696 sharedFolder.createObject();
5697 rc = sharedFolder->init(i_getMachine(),
5698 aName,
5699 aHostPath,
5700 !!aWritable,
5701 !!aAutomount,
5702 aAutoMountPoint,
5703 true /* fFailOnError */);
5704 if (FAILED(rc)) return rc;
5705
5706 i_setModified(IsModified_SharedFolders);
5707 mHWData.backup();
5708 mHWData->mSharedFolders.push_back(sharedFolder);
5709
5710 /* inform the direct session if any */
5711 alock.release();
5712 i_onSharedFolderChange();
5713
5714 return S_OK;
5715}
5716
5717HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5718{
5719 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5720
5721 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5722 if (FAILED(rc)) return rc;
5723
5724 ComObjPtr<SharedFolder> sharedFolder;
5725 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5726 if (FAILED(rc)) return rc;
5727
5728 i_setModified(IsModified_SharedFolders);
5729 mHWData.backup();
5730 mHWData->mSharedFolders.remove(sharedFolder);
5731
5732 /* inform the direct session if any */
5733 alock.release();
5734 i_onSharedFolderChange();
5735
5736 return S_OK;
5737}
5738
5739HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5740{
5741 /* start with No */
5742 *aCanShow = FALSE;
5743
5744 ComPtr<IInternalSessionControl> directControl;
5745 {
5746 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5747
5748 if (mData->mSession.mState != SessionState_Locked)
5749 return setError(VBOX_E_INVALID_VM_STATE,
5750 tr("Machine is not locked for session (session state: %s)"),
5751 Global::stringifySessionState(mData->mSession.mState));
5752
5753 if (mData->mSession.mLockType == LockType_VM)
5754 directControl = mData->mSession.mDirectControl;
5755 }
5756
5757 /* ignore calls made after #OnSessionEnd() is called */
5758 if (!directControl)
5759 return S_OK;
5760
5761 LONG64 dummy;
5762 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5763}
5764
5765HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5766{
5767 ComPtr<IInternalSessionControl> directControl;
5768 {
5769 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5770
5771 if (mData->mSession.mState != SessionState_Locked)
5772 return setError(E_FAIL,
5773 tr("Machine is not locked for session (session state: %s)"),
5774 Global::stringifySessionState(mData->mSession.mState));
5775
5776 if (mData->mSession.mLockType == LockType_VM)
5777 directControl = mData->mSession.mDirectControl;
5778 }
5779
5780 /* ignore calls made after #OnSessionEnd() is called */
5781 if (!directControl)
5782 return S_OK;
5783
5784 BOOL dummy;
5785 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5786}
5787
5788#ifdef VBOX_WITH_GUEST_PROPS
5789/**
5790 * Look up a guest property in VBoxSVC's internal structures.
5791 */
5792HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5793 com::Utf8Str &aValue,
5794 LONG64 *aTimestamp,
5795 com::Utf8Str &aFlags) const
5796{
5797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5798
5799 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5800 if (it != mHWData->mGuestProperties.end())
5801 {
5802 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5803 aValue = it->second.strValue;
5804 *aTimestamp = it->second.mTimestamp;
5805 GuestPropWriteFlags(it->second.mFlags, szFlags);
5806 aFlags = Utf8Str(szFlags);
5807 }
5808
5809 return S_OK;
5810}
5811
5812/**
5813 * Query the VM that a guest property belongs to for the property.
5814 * @returns E_ACCESSDENIED if the VM process is not available or not
5815 * currently handling queries and the lookup should then be done in
5816 * VBoxSVC.
5817 */
5818HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5819 com::Utf8Str &aValue,
5820 LONG64 *aTimestamp,
5821 com::Utf8Str &aFlags) const
5822{
5823 HRESULT rc = S_OK;
5824 Bstr bstrValue;
5825 Bstr bstrFlags;
5826
5827 ComPtr<IInternalSessionControl> directControl;
5828 {
5829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5830 if (mData->mSession.mLockType == LockType_VM)
5831 directControl = mData->mSession.mDirectControl;
5832 }
5833
5834 /* ignore calls made after #OnSessionEnd() is called */
5835 if (!directControl)
5836 rc = E_ACCESSDENIED;
5837 else
5838 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5839 0 /* accessMode */,
5840 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5841
5842 aValue = bstrValue;
5843 aFlags = bstrFlags;
5844
5845 return rc;
5846}
5847#endif // VBOX_WITH_GUEST_PROPS
5848
5849HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5850 com::Utf8Str &aValue,
5851 LONG64 *aTimestamp,
5852 com::Utf8Str &aFlags)
5853{
5854#ifndef VBOX_WITH_GUEST_PROPS
5855 ReturnComNotImplemented();
5856#else // VBOX_WITH_GUEST_PROPS
5857
5858 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5859
5860 if (rc == E_ACCESSDENIED)
5861 /* The VM is not running or the service is not (yet) accessible */
5862 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5863 return rc;
5864#endif // VBOX_WITH_GUEST_PROPS
5865}
5866
5867HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5868{
5869 LONG64 dummyTimestamp;
5870 com::Utf8Str dummyFlags;
5871 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5872 return rc;
5873
5874}
5875HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5876{
5877 com::Utf8Str dummyFlags;
5878 com::Utf8Str dummyValue;
5879 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5880 return rc;
5881}
5882
5883#ifdef VBOX_WITH_GUEST_PROPS
5884/**
5885 * Set a guest property in VBoxSVC's internal structures.
5886 */
5887HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5888 const com::Utf8Str &aFlags, bool fDelete)
5889{
5890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5891 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5892 if (FAILED(rc)) return rc;
5893
5894 try
5895 {
5896 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5897 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5898 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5899
5900 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
5901 return setError(E_INVALIDARG, tr("Properties with TRANSIENT or TRANSRESET flag cannot be set or modified if VM is not running"));
5902
5903 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5904 if (it == mHWData->mGuestProperties.end())
5905 {
5906 if (!fDelete)
5907 {
5908 i_setModified(IsModified_MachineData);
5909 mHWData.backupEx();
5910
5911 RTTIMESPEC time;
5912 HWData::GuestProperty prop;
5913 prop.strValue = aValue;
5914 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5915 prop.mFlags = fFlags;
5916 mHWData->mGuestProperties[aName] = prop;
5917 }
5918 }
5919 else
5920 {
5921 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5922 {
5923 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5924 }
5925 else
5926 {
5927 i_setModified(IsModified_MachineData);
5928 mHWData.backupEx();
5929
5930 /* The backupEx() operation invalidates our iterator,
5931 * so get a new one. */
5932 it = mHWData->mGuestProperties.find(aName);
5933 Assert(it != mHWData->mGuestProperties.end());
5934
5935 if (!fDelete)
5936 {
5937 RTTIMESPEC time;
5938 it->second.strValue = aValue;
5939 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5940 it->second.mFlags = fFlags;
5941 }
5942 else
5943 mHWData->mGuestProperties.erase(it);
5944 }
5945 }
5946
5947 if (SUCCEEDED(rc))
5948 {
5949 alock.release();
5950
5951 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fDelete);
5952 }
5953 }
5954 catch (std::bad_alloc &)
5955 {
5956 rc = E_OUTOFMEMORY;
5957 }
5958
5959 return rc;
5960}
5961
5962/**
5963 * Set a property on the VM that that property belongs to.
5964 * @returns E_ACCESSDENIED if the VM process is not available or not
5965 * currently handling queries and the setting should then be done in
5966 * VBoxSVC.
5967 */
5968HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5969 const com::Utf8Str &aFlags, bool fDelete)
5970{
5971 HRESULT rc;
5972
5973 try
5974 {
5975 ComPtr<IInternalSessionControl> directControl;
5976 {
5977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5978 if (mData->mSession.mLockType == LockType_VM)
5979 directControl = mData->mSession.mDirectControl;
5980 }
5981
5982 Bstr dummy1; /* will not be changed (setter) */
5983 Bstr dummy2; /* will not be changed (setter) */
5984 LONG64 dummy64;
5985 if (!directControl)
5986 rc = E_ACCESSDENIED;
5987 else
5988 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5989 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5990 fDelete ? 2 : 1 /* accessMode */,
5991 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5992 }
5993 catch (std::bad_alloc &)
5994 {
5995 rc = E_OUTOFMEMORY;
5996 }
5997
5998 return rc;
5999}
6000#endif // VBOX_WITH_GUEST_PROPS
6001
6002HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
6003 const com::Utf8Str &aFlags)
6004{
6005#ifndef VBOX_WITH_GUEST_PROPS
6006 ReturnComNotImplemented();
6007#else // VBOX_WITH_GUEST_PROPS
6008
6009 int vrc = GuestPropValidateName(aProperty.c_str(), aProperty.length() + 1 /* '\0' */);
6010 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
6011
6012 vrc = GuestPropValidateValue(aValue.c_str(), aValue.length() + 1 /* '\0' */);
6013 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
6014
6015 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
6016 if (rc == E_ACCESSDENIED)
6017 /* The VM is not running or the service is not (yet) accessible */
6018 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
6019 return rc;
6020#endif // VBOX_WITH_GUEST_PROPS
6021}
6022
6023HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
6024{
6025 return setGuestProperty(aProperty, aValue, "");
6026}
6027
6028HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
6029{
6030#ifndef VBOX_WITH_GUEST_PROPS
6031 ReturnComNotImplemented();
6032#else // VBOX_WITH_GUEST_PROPS
6033 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
6034 if (rc == E_ACCESSDENIED)
6035 /* The VM is not running or the service is not (yet) accessible */
6036 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
6037 return rc;
6038#endif // VBOX_WITH_GUEST_PROPS
6039}
6040
6041#ifdef VBOX_WITH_GUEST_PROPS
6042/**
6043 * Enumerate the guest properties in VBoxSVC's internal structures.
6044 */
6045HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
6046 std::vector<com::Utf8Str> &aNames,
6047 std::vector<com::Utf8Str> &aValues,
6048 std::vector<LONG64> &aTimestamps,
6049 std::vector<com::Utf8Str> &aFlags)
6050{
6051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6052 Utf8Str strPatterns(aPatterns);
6053
6054 /*
6055 * Look for matching patterns and build up a list.
6056 */
6057 HWData::GuestPropertyMap propMap;
6058 for (HWData::GuestPropertyMap::const_iterator
6059 it = mHWData->mGuestProperties.begin();
6060 it != mHWData->mGuestProperties.end();
6061 ++it)
6062 {
6063 if ( strPatterns.isEmpty()
6064 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6065 RTSTR_MAX,
6066 it->first.c_str(),
6067 RTSTR_MAX,
6068 NULL)
6069 )
6070 propMap.insert(*it);
6071 }
6072
6073 alock.release();
6074
6075 /*
6076 * And build up the arrays for returning the property information.
6077 */
6078 size_t cEntries = propMap.size();
6079
6080 aNames.resize(cEntries);
6081 aValues.resize(cEntries);
6082 aTimestamps.resize(cEntries);
6083 aFlags.resize(cEntries);
6084
6085 size_t i = 0;
6086 for (HWData::GuestPropertyMap::const_iterator
6087 it = propMap.begin();
6088 it != propMap.end();
6089 ++it, ++i)
6090 {
6091 aNames[i] = it->first;
6092 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
6093 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
6094
6095 aValues[i] = it->second.strValue;
6096 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
6097 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
6098
6099 aTimestamps[i] = it->second.mTimestamp;
6100
6101 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6102 GuestPropWriteFlags(it->second.mFlags, szFlags);
6103 aFlags[i] = szFlags;
6104 }
6105
6106 return S_OK;
6107}
6108
6109/**
6110 * Enumerate the properties managed by a VM.
6111 * @returns E_ACCESSDENIED if the VM process is not available or not
6112 * currently handling queries and the setting should then be done in
6113 * VBoxSVC.
6114 */
6115HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6116 std::vector<com::Utf8Str> &aNames,
6117 std::vector<com::Utf8Str> &aValues,
6118 std::vector<LONG64> &aTimestamps,
6119 std::vector<com::Utf8Str> &aFlags)
6120{
6121 HRESULT rc;
6122 ComPtr<IInternalSessionControl> directControl;
6123 {
6124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6125 if (mData->mSession.mLockType == LockType_VM)
6126 directControl = mData->mSession.mDirectControl;
6127 }
6128
6129 com::SafeArray<BSTR> bNames;
6130 com::SafeArray<BSTR> bValues;
6131 com::SafeArray<LONG64> bTimestamps;
6132 com::SafeArray<BSTR> bFlags;
6133
6134 if (!directControl)
6135 rc = E_ACCESSDENIED;
6136 else
6137 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6138 ComSafeArrayAsOutParam(bNames),
6139 ComSafeArrayAsOutParam(bValues),
6140 ComSafeArrayAsOutParam(bTimestamps),
6141 ComSafeArrayAsOutParam(bFlags));
6142 size_t i;
6143 aNames.resize(bNames.size());
6144 for (i = 0; i < bNames.size(); ++i)
6145 aNames[i] = Utf8Str(bNames[i]);
6146 aValues.resize(bValues.size());
6147 for (i = 0; i < bValues.size(); ++i)
6148 aValues[i] = Utf8Str(bValues[i]);
6149 aTimestamps.resize(bTimestamps.size());
6150 for (i = 0; i < bTimestamps.size(); ++i)
6151 aTimestamps[i] = bTimestamps[i];
6152 aFlags.resize(bFlags.size());
6153 for (i = 0; i < bFlags.size(); ++i)
6154 aFlags[i] = Utf8Str(bFlags[i]);
6155
6156 return rc;
6157}
6158#endif // VBOX_WITH_GUEST_PROPS
6159HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6160 std::vector<com::Utf8Str> &aNames,
6161 std::vector<com::Utf8Str> &aValues,
6162 std::vector<LONG64> &aTimestamps,
6163 std::vector<com::Utf8Str> &aFlags)
6164{
6165#ifndef VBOX_WITH_GUEST_PROPS
6166 ReturnComNotImplemented();
6167#else // VBOX_WITH_GUEST_PROPS
6168
6169 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6170
6171 if (rc == E_ACCESSDENIED)
6172 /* The VM is not running or the service is not (yet) accessible */
6173 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6174 return rc;
6175#endif // VBOX_WITH_GUEST_PROPS
6176}
6177
6178HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6179 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6180{
6181 MediumAttachmentList atts;
6182
6183 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6184 if (FAILED(rc)) return rc;
6185
6186 aMediumAttachments.resize(atts.size());
6187 size_t i = 0;
6188 for (MediumAttachmentList::const_iterator
6189 it = atts.begin();
6190 it != atts.end();
6191 ++it, ++i)
6192 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6193
6194 return S_OK;
6195}
6196
6197HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6198 LONG aControllerPort,
6199 LONG aDevice,
6200 ComPtr<IMediumAttachment> &aAttachment)
6201{
6202 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6203 aName.c_str(), aControllerPort, aDevice));
6204
6205 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6206
6207 aAttachment = NULL;
6208
6209 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6210 aName,
6211 aControllerPort,
6212 aDevice);
6213 if (pAttach.isNull())
6214 return setError(VBOX_E_OBJECT_NOT_FOUND,
6215 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6216 aDevice, aControllerPort, aName.c_str());
6217
6218 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6219
6220 return S_OK;
6221}
6222
6223
6224HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6225 StorageBus_T aConnectionType,
6226 ComPtr<IStorageController> &aController)
6227{
6228 if ( (aConnectionType <= StorageBus_Null)
6229 || (aConnectionType > StorageBus_VirtioSCSI))
6230 return setError(E_INVALIDARG,
6231 tr("Invalid connection type: %d"),
6232 aConnectionType);
6233
6234 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6235
6236 HRESULT rc = i_checkStateDependency(MutableStateDep);
6237 if (FAILED(rc)) return rc;
6238
6239 /* try to find one with the name first. */
6240 ComObjPtr<StorageController> ctrl;
6241
6242 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6243 if (SUCCEEDED(rc))
6244 return setError(VBOX_E_OBJECT_IN_USE,
6245 tr("Storage controller named '%s' already exists"),
6246 aName.c_str());
6247
6248 ctrl.createObject();
6249
6250 /* get a new instance number for the storage controller */
6251 ULONG ulInstance = 0;
6252 bool fBootable = true;
6253 for (StorageControllerList::const_iterator
6254 it = mStorageControllers->begin();
6255 it != mStorageControllers->end();
6256 ++it)
6257 {
6258 if ((*it)->i_getStorageBus() == aConnectionType)
6259 {
6260 ULONG ulCurInst = (*it)->i_getInstance();
6261
6262 if (ulCurInst >= ulInstance)
6263 ulInstance = ulCurInst + 1;
6264
6265 /* Only one controller of each type can be marked as bootable. */
6266 if ((*it)->i_getBootable())
6267 fBootable = false;
6268 }
6269 }
6270
6271 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6272 if (FAILED(rc)) return rc;
6273
6274 i_setModified(IsModified_Storage);
6275 mStorageControllers.backup();
6276 mStorageControllers->push_back(ctrl);
6277
6278 ctrl.queryInterfaceTo(aController.asOutParam());
6279
6280 /* inform the direct session if any */
6281 alock.release();
6282 i_onStorageControllerChange(i_getId(), aName);
6283
6284 return S_OK;
6285}
6286
6287HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6288 ComPtr<IStorageController> &aStorageController)
6289{
6290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6291
6292 ComObjPtr<StorageController> ctrl;
6293
6294 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6295 if (SUCCEEDED(rc))
6296 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6297
6298 return rc;
6299}
6300
6301HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6302 ULONG aInstance,
6303 ComPtr<IStorageController> &aStorageController)
6304{
6305 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6306
6307 for (StorageControllerList::const_iterator
6308 it = mStorageControllers->begin();
6309 it != mStorageControllers->end();
6310 ++it)
6311 {
6312 if ( (*it)->i_getStorageBus() == aConnectionType
6313 && (*it)->i_getInstance() == aInstance)
6314 {
6315 (*it).queryInterfaceTo(aStorageController.asOutParam());
6316 return S_OK;
6317 }
6318 }
6319
6320 return setError(VBOX_E_OBJECT_NOT_FOUND,
6321 tr("Could not find a storage controller with instance number '%lu'"),
6322 aInstance);
6323}
6324
6325HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6326{
6327 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6328
6329 HRESULT rc = i_checkStateDependency(MutableStateDep);
6330 if (FAILED(rc)) return rc;
6331
6332 ComObjPtr<StorageController> ctrl;
6333
6334 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6335 if (SUCCEEDED(rc))
6336 {
6337 /* Ensure that only one controller of each type is marked as bootable. */
6338 if (aBootable == TRUE)
6339 {
6340 for (StorageControllerList::const_iterator
6341 it = mStorageControllers->begin();
6342 it != mStorageControllers->end();
6343 ++it)
6344 {
6345 ComObjPtr<StorageController> aCtrl = (*it);
6346
6347 if ( (aCtrl->i_getName() != aName)
6348 && aCtrl->i_getBootable() == TRUE
6349 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6350 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6351 {
6352 aCtrl->i_setBootable(FALSE);
6353 break;
6354 }
6355 }
6356 }
6357
6358 if (SUCCEEDED(rc))
6359 {
6360 ctrl->i_setBootable(aBootable);
6361 i_setModified(IsModified_Storage);
6362 }
6363 }
6364
6365 if (SUCCEEDED(rc))
6366 {
6367 /* inform the direct session if any */
6368 alock.release();
6369 i_onStorageControllerChange(i_getId(), aName);
6370 }
6371
6372 return rc;
6373}
6374
6375HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6376{
6377 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6378
6379 HRESULT rc = i_checkStateDependency(MutableStateDep);
6380 if (FAILED(rc)) return rc;
6381
6382 ComObjPtr<StorageController> ctrl;
6383 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6384 if (FAILED(rc)) return rc;
6385
6386 MediumAttachmentList llDetachedAttachments;
6387 {
6388 /* find all attached devices to the appropriate storage controller and detach them all */
6389 // make a temporary list because detachDevice invalidates iterators into
6390 // mMediumAttachments
6391 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6392
6393 for (MediumAttachmentList::const_iterator
6394 it = llAttachments2.begin();
6395 it != llAttachments2.end();
6396 ++it)
6397 {
6398 MediumAttachment *pAttachTemp = *it;
6399
6400 AutoCaller localAutoCaller(pAttachTemp);
6401 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6402
6403 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6404
6405 if (pAttachTemp->i_getControllerName() == aName)
6406 {
6407 llDetachedAttachments.push_back(pAttachTemp);
6408 rc = i_detachDevice(pAttachTemp, alock, NULL);
6409 if (FAILED(rc)) return rc;
6410 }
6411 }
6412 }
6413
6414 /* send event about detached devices before removing parent controller */
6415 for (MediumAttachmentList::const_iterator
6416 it = llDetachedAttachments.begin();
6417 it != llDetachedAttachments.end();
6418 ++it)
6419 {
6420 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6421 }
6422
6423 /* We can remove it now. */
6424 i_setModified(IsModified_Storage);
6425 mStorageControllers.backup();
6426
6427 ctrl->i_unshare();
6428
6429 mStorageControllers->remove(ctrl);
6430
6431 /* inform the direct session if any */
6432 alock.release();
6433 i_onStorageControllerChange(i_getId(), aName);
6434
6435 return S_OK;
6436}
6437
6438HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6439 ComPtr<IUSBController> &aController)
6440{
6441 if ( (aType <= USBControllerType_Null)
6442 || (aType >= USBControllerType_Last))
6443 return setError(E_INVALIDARG,
6444 tr("Invalid USB controller type: %d"),
6445 aType);
6446
6447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6448
6449 HRESULT rc = i_checkStateDependency(MutableStateDep);
6450 if (FAILED(rc)) return rc;
6451
6452 /* try to find one with the same type first. */
6453 ComObjPtr<USBController> ctrl;
6454
6455 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6456 if (SUCCEEDED(rc))
6457 return setError(VBOX_E_OBJECT_IN_USE,
6458 tr("USB controller named '%s' already exists"),
6459 aName.c_str());
6460
6461 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6462 ULONG maxInstances;
6463 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6464 if (FAILED(rc))
6465 return rc;
6466
6467 ULONG cInstances = i_getUSBControllerCountByType(aType);
6468 if (cInstances >= maxInstances)
6469 return setError(E_INVALIDARG,
6470 tr("Too many USB controllers of this type"));
6471
6472 ctrl.createObject();
6473
6474 rc = ctrl->init(this, aName, aType);
6475 if (FAILED(rc)) return rc;
6476
6477 i_setModified(IsModified_USB);
6478 mUSBControllers.backup();
6479 mUSBControllers->push_back(ctrl);
6480
6481 ctrl.queryInterfaceTo(aController.asOutParam());
6482
6483 /* inform the direct session if any */
6484 alock.release();
6485 i_onUSBControllerChange();
6486
6487 return S_OK;
6488}
6489
6490HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6491{
6492 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6493
6494 ComObjPtr<USBController> ctrl;
6495
6496 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6497 if (SUCCEEDED(rc))
6498 ctrl.queryInterfaceTo(aController.asOutParam());
6499
6500 return rc;
6501}
6502
6503HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6504 ULONG *aControllers)
6505{
6506 if ( (aType <= USBControllerType_Null)
6507 || (aType >= USBControllerType_Last))
6508 return setError(E_INVALIDARG,
6509 tr("Invalid USB controller type: %d"),
6510 aType);
6511
6512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6513
6514 ComObjPtr<USBController> ctrl;
6515
6516 *aControllers = i_getUSBControllerCountByType(aType);
6517
6518 return S_OK;
6519}
6520
6521HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6522{
6523
6524 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6525
6526 HRESULT rc = i_checkStateDependency(MutableStateDep);
6527 if (FAILED(rc)) return rc;
6528
6529 ComObjPtr<USBController> ctrl;
6530 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6531 if (FAILED(rc)) return rc;
6532
6533 i_setModified(IsModified_USB);
6534 mUSBControllers.backup();
6535
6536 ctrl->i_unshare();
6537
6538 mUSBControllers->remove(ctrl);
6539
6540 /* inform the direct session if any */
6541 alock.release();
6542 i_onUSBControllerChange();
6543
6544 return S_OK;
6545}
6546
6547HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6548 ULONG *aOriginX,
6549 ULONG *aOriginY,
6550 ULONG *aWidth,
6551 ULONG *aHeight,
6552 BOOL *aEnabled)
6553{
6554 uint32_t u32OriginX= 0;
6555 uint32_t u32OriginY= 0;
6556 uint32_t u32Width = 0;
6557 uint32_t u32Height = 0;
6558 uint16_t u16Flags = 0;
6559
6560#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6561 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6562#else
6563 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6564#endif
6565 int vrc = readSavedGuestScreenInfo(SavedStateStream, mSSData->strStateFilePath, aScreenId,
6566 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6567 if (RT_FAILURE(vrc))
6568 {
6569#ifdef RT_OS_WINDOWS
6570 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6571 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6572 * So just assign fEnable to TRUE again.
6573 * The right fix would be to change GUI API wrappers to make sure that parameters
6574 * are changed only if API succeeds.
6575 */
6576 *aEnabled = TRUE;
6577#endif
6578 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6579 tr("Saved guest size is not available (%Rrc)"),
6580 vrc);
6581 }
6582
6583 *aOriginX = u32OriginX;
6584 *aOriginY = u32OriginY;
6585 *aWidth = u32Width;
6586 *aHeight = u32Height;
6587 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6588
6589 return S_OK;
6590}
6591
6592HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6593 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6594{
6595 if (aScreenId != 0)
6596 return E_NOTIMPL;
6597
6598 if ( aBitmapFormat != BitmapFormat_BGR0
6599 && aBitmapFormat != BitmapFormat_BGRA
6600 && aBitmapFormat != BitmapFormat_RGBA
6601 && aBitmapFormat != BitmapFormat_PNG)
6602 return setError(E_NOTIMPL,
6603 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6604
6605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6606
6607 uint8_t *pu8Data = NULL;
6608 uint32_t cbData = 0;
6609 uint32_t u32Width = 0;
6610 uint32_t u32Height = 0;
6611
6612#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6613 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6614#else
6615 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6616#endif
6617 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 0 /* u32Type */,
6618 &pu8Data, &cbData, &u32Width, &u32Height);
6619 if (RT_FAILURE(vrc))
6620 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6621 tr("Saved thumbnail data is not available (%Rrc)"),
6622 vrc);
6623
6624 HRESULT hr = S_OK;
6625
6626 *aWidth = u32Width;
6627 *aHeight = u32Height;
6628
6629 if (cbData > 0)
6630 {
6631 /* Convert pixels to the format expected by the API caller. */
6632 if (aBitmapFormat == BitmapFormat_BGR0)
6633 {
6634 /* [0] B, [1] G, [2] R, [3] 0. */
6635 aData.resize(cbData);
6636 memcpy(&aData.front(), pu8Data, cbData);
6637 }
6638 else if (aBitmapFormat == BitmapFormat_BGRA)
6639 {
6640 /* [0] B, [1] G, [2] R, [3] A. */
6641 aData.resize(cbData);
6642 for (uint32_t i = 0; i < cbData; i += 4)
6643 {
6644 aData[i] = pu8Data[i];
6645 aData[i + 1] = pu8Data[i + 1];
6646 aData[i + 2] = pu8Data[i + 2];
6647 aData[i + 3] = 0xff;
6648 }
6649 }
6650 else if (aBitmapFormat == BitmapFormat_RGBA)
6651 {
6652 /* [0] R, [1] G, [2] B, [3] A. */
6653 aData.resize(cbData);
6654 for (uint32_t i = 0; i < cbData; i += 4)
6655 {
6656 aData[i] = pu8Data[i + 2];
6657 aData[i + 1] = pu8Data[i + 1];
6658 aData[i + 2] = pu8Data[i];
6659 aData[i + 3] = 0xff;
6660 }
6661 }
6662 else if (aBitmapFormat == BitmapFormat_PNG)
6663 {
6664 uint8_t *pu8PNG = NULL;
6665 uint32_t cbPNG = 0;
6666 uint32_t cxPNG = 0;
6667 uint32_t cyPNG = 0;
6668
6669 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6670
6671 if (RT_SUCCESS(vrc))
6672 {
6673 aData.resize(cbPNG);
6674 if (cbPNG)
6675 memcpy(&aData.front(), pu8PNG, cbPNG);
6676 }
6677 else
6678 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6679 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6680 vrc);
6681
6682 RTMemFree(pu8PNG);
6683 }
6684 }
6685
6686 freeSavedDisplayScreenshot(pu8Data);
6687
6688 return hr;
6689}
6690
6691HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6692 ULONG *aWidth,
6693 ULONG *aHeight,
6694 std::vector<BitmapFormat_T> &aBitmapFormats)
6695{
6696 if (aScreenId != 0)
6697 return E_NOTIMPL;
6698
6699 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6700
6701 uint8_t *pu8Data = NULL;
6702 uint32_t cbData = 0;
6703 uint32_t u32Width = 0;
6704 uint32_t u32Height = 0;
6705
6706#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6707 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6708#else
6709 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6710#endif
6711 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6712 &pu8Data, &cbData, &u32Width, &u32Height);
6713
6714 if (RT_FAILURE(vrc))
6715 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6716 tr("Saved screenshot data is not available (%Rrc)"),
6717 vrc);
6718
6719 *aWidth = u32Width;
6720 *aHeight = u32Height;
6721 aBitmapFormats.resize(1);
6722 aBitmapFormats[0] = BitmapFormat_PNG;
6723
6724 freeSavedDisplayScreenshot(pu8Data);
6725
6726 return S_OK;
6727}
6728
6729HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6730 BitmapFormat_T aBitmapFormat,
6731 ULONG *aWidth,
6732 ULONG *aHeight,
6733 std::vector<BYTE> &aData)
6734{
6735 if (aScreenId != 0)
6736 return E_NOTIMPL;
6737
6738 if (aBitmapFormat != BitmapFormat_PNG)
6739 return E_NOTIMPL;
6740
6741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6742
6743 uint8_t *pu8Data = NULL;
6744 uint32_t cbData = 0;
6745 uint32_t u32Width = 0;
6746 uint32_t u32Height = 0;
6747
6748#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6749 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6750#else
6751 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6752#endif
6753 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6754 &pu8Data, &cbData, &u32Width, &u32Height);
6755
6756 if (RT_FAILURE(vrc))
6757 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6758 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6759 vrc);
6760
6761 *aWidth = u32Width;
6762 *aHeight = u32Height;
6763
6764 aData.resize(cbData);
6765 if (cbData)
6766 memcpy(&aData.front(), pu8Data, cbData);
6767
6768 freeSavedDisplayScreenshot(pu8Data);
6769
6770 return S_OK;
6771}
6772
6773HRESULT Machine::hotPlugCPU(ULONG aCpu)
6774{
6775 HRESULT rc = S_OK;
6776 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6777
6778 if (!mHWData->mCPUHotPlugEnabled)
6779 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6780
6781 if (aCpu >= mHWData->mCPUCount)
6782 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6783
6784 if (mHWData->mCPUAttached[aCpu])
6785 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6786
6787 rc = i_checkStateDependency(MutableOrRunningStateDep);
6788 if (FAILED(rc)) return rc;
6789
6790 alock.release();
6791 rc = i_onCPUChange(aCpu, false);
6792 alock.acquire();
6793 if (FAILED(rc)) return rc;
6794
6795 i_setModified(IsModified_MachineData);
6796 mHWData.backup();
6797 mHWData->mCPUAttached[aCpu] = true;
6798
6799 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6800 if (Global::IsOnline(mData->mMachineState))
6801 i_saveSettings(NULL, alock);
6802
6803 return S_OK;
6804}
6805
6806HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6807{
6808 HRESULT rc = S_OK;
6809
6810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6811
6812 if (!mHWData->mCPUHotPlugEnabled)
6813 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6814
6815 if (aCpu >= SchemaDefs::MaxCPUCount)
6816 return setError(E_INVALIDARG,
6817 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6818 SchemaDefs::MaxCPUCount);
6819
6820 if (!mHWData->mCPUAttached[aCpu])
6821 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6822
6823 /* CPU 0 can't be detached */
6824 if (aCpu == 0)
6825 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6826
6827 rc = i_checkStateDependency(MutableOrRunningStateDep);
6828 if (FAILED(rc)) return rc;
6829
6830 alock.release();
6831 rc = i_onCPUChange(aCpu, true);
6832 alock.acquire();
6833 if (FAILED(rc)) return rc;
6834
6835 i_setModified(IsModified_MachineData);
6836 mHWData.backup();
6837 mHWData->mCPUAttached[aCpu] = false;
6838
6839 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6840 if (Global::IsOnline(mData->mMachineState))
6841 i_saveSettings(NULL, alock);
6842
6843 return S_OK;
6844}
6845
6846HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6847{
6848 *aAttached = false;
6849
6850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6851
6852 /* If hotplug is enabled the CPU is always enabled. */
6853 if (!mHWData->mCPUHotPlugEnabled)
6854 {
6855 if (aCpu < mHWData->mCPUCount)
6856 *aAttached = true;
6857 }
6858 else
6859 {
6860 if (aCpu < SchemaDefs::MaxCPUCount)
6861 *aAttached = mHWData->mCPUAttached[aCpu];
6862 }
6863
6864 return S_OK;
6865}
6866
6867HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6868{
6869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6870
6871 Utf8Str log = i_getLogFilename(aIdx);
6872 if (!RTFileExists(log.c_str()))
6873 log.setNull();
6874 aFilename = log;
6875
6876 return S_OK;
6877}
6878
6879HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6880{
6881 if (aSize < 0)
6882 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6883
6884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6885
6886 HRESULT rc = S_OK;
6887 Utf8Str log = i_getLogFilename(aIdx);
6888
6889 /* do not unnecessarily hold the lock while doing something which does
6890 * not need the lock and potentially takes a long time. */
6891 alock.release();
6892
6893 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6894 * keeps the SOAP reply size under 1M for the webservice (we're using
6895 * base64 encoded strings for binary data for years now, avoiding the
6896 * expansion of each byte array element to approx. 25 bytes of XML. */
6897 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6898 aData.resize(cbData);
6899
6900 int vrc = VINF_SUCCESS;
6901 RTVFSIOSTREAM hVfsIosLog = NIL_RTVFSIOSTREAM;
6902
6903#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6904 if (mData->mstrLogKeyId.isNotEmpty() && mData->mstrLogKeyStore.isNotEmpty())
6905 {
6906 PCVBOXCRYPTOIF pCryptoIf = NULL;
6907 rc = i_getVirtualBox()->i_retainCryptoIf(&pCryptoIf);
6908 if (SUCCEEDED(rc))
6909 {
6910 alock.acquire();
6911
6912 SecretKey *pKey = NULL;
6913 vrc = mData->mpKeyStore->retainSecretKey(mData->mstrLogKeyId, &pKey);
6914 alock.release();
6915
6916 if (RT_SUCCESS(vrc))
6917 {
6918 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6919 if (RT_SUCCESS(vrc))
6920 {
6921 RTVFSIOSTREAM hVfsIosLogDec = NIL_RTVFSIOSTREAM;
6922 vrc = pCryptoIf->pfnCryptoIoStrmFromVfsIoStrmDecrypt(hVfsIosLog, mData->mstrLogKeyStore.c_str(),
6923 (const char *)pKey->getKeyBuffer(), &hVfsIosLogDec);
6924 if (RT_SUCCESS(vrc))
6925 {
6926 RTVfsIoStrmRelease(hVfsIosLog);
6927 hVfsIosLog = hVfsIosLogDec;
6928 }
6929 }
6930
6931 pKey->release();
6932 }
6933
6934 i_getVirtualBox()->i_releaseCryptoIf(pCryptoIf);
6935 }
6936 }
6937 else
6938 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6939#else
6940 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6941#endif
6942 if (RT_SUCCESS(vrc))
6943 {
6944 vrc = RTVfsIoStrmReadAt(hVfsIosLog, aOffset,
6945 cbData ? &aData.front() : NULL, cbData,
6946 true /*fBlocking*/, &cbData);
6947 if (RT_SUCCESS(vrc))
6948 aData.resize(cbData);
6949 else
6950 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6951 tr("Could not read log file '%s' (%Rrc)"),
6952 log.c_str(), vrc);
6953
6954 RTVfsIoStrmRelease(hVfsIosLog);
6955 }
6956 else
6957 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6958 tr("Could not open log file '%s' (%Rrc)"),
6959 log.c_str(), vrc);
6960
6961 if (FAILED(rc))
6962 aData.resize(0);
6963
6964 return rc;
6965}
6966
6967
6968/**
6969 * Currently this method doesn't attach device to the running VM,
6970 * just makes sure it's plugged on next VM start.
6971 */
6972HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6973{
6974 // lock scope
6975 {
6976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6977
6978 HRESULT rc = i_checkStateDependency(MutableStateDep);
6979 if (FAILED(rc)) return rc;
6980
6981 ChipsetType_T aChipset = ChipsetType_PIIX3;
6982 COMGETTER(ChipsetType)(&aChipset);
6983
6984 if (aChipset != ChipsetType_ICH9)
6985 {
6986 return setError(E_INVALIDARG,
6987 tr("Host PCI attachment only supported with ICH9 chipset"));
6988 }
6989
6990 // check if device with this host PCI address already attached
6991 for (HWData::PCIDeviceAssignmentList::const_iterator
6992 it = mHWData->mPCIDeviceAssignments.begin();
6993 it != mHWData->mPCIDeviceAssignments.end();
6994 ++it)
6995 {
6996 LONG iHostAddress = -1;
6997 ComPtr<PCIDeviceAttachment> pAttach;
6998 pAttach = *it;
6999 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7000 if (iHostAddress == aHostAddress)
7001 return setError(E_INVALIDARG,
7002 tr("Device with host PCI address already attached to this VM"));
7003 }
7004
7005 ComObjPtr<PCIDeviceAttachment> pda;
7006 char name[32];
7007
7008 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
7009 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
7010 pda.createObject();
7011 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
7012 i_setModified(IsModified_MachineData);
7013 mHWData.backup();
7014 mHWData->mPCIDeviceAssignments.push_back(pda);
7015 }
7016
7017 return S_OK;
7018}
7019
7020/**
7021 * Currently this method doesn't detach device from the running VM,
7022 * just makes sure it's not plugged on next VM start.
7023 */
7024HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
7025{
7026 ComObjPtr<PCIDeviceAttachment> pAttach;
7027 bool fRemoved = false;
7028 HRESULT rc;
7029
7030 // lock scope
7031 {
7032 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7033
7034 rc = i_checkStateDependency(MutableStateDep);
7035 if (FAILED(rc)) return rc;
7036
7037 for (HWData::PCIDeviceAssignmentList::const_iterator
7038 it = mHWData->mPCIDeviceAssignments.begin();
7039 it != mHWData->mPCIDeviceAssignments.end();
7040 ++it)
7041 {
7042 LONG iHostAddress = -1;
7043 pAttach = *it;
7044 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7045 if (iHostAddress != -1 && iHostAddress == aHostAddress)
7046 {
7047 i_setModified(IsModified_MachineData);
7048 mHWData.backup();
7049 mHWData->mPCIDeviceAssignments.remove(pAttach);
7050 fRemoved = true;
7051 break;
7052 }
7053 }
7054 }
7055
7056
7057 /* Fire event outside of the lock */
7058 if (fRemoved)
7059 {
7060 Assert(!pAttach.isNull());
7061 ComPtr<IEventSource> es;
7062 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7063 Assert(SUCCEEDED(rc));
7064 Bstr mid;
7065 rc = this->COMGETTER(Id)(mid.asOutParam());
7066 Assert(SUCCEEDED(rc));
7067 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7068 }
7069
7070 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7071 tr("No host PCI device %08x attached"),
7072 aHostAddress
7073 );
7074}
7075
7076HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
7077{
7078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7079
7080 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
7081 size_t i = 0;
7082 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
7083 it = mHWData->mPCIDeviceAssignments.begin();
7084 it != mHWData->mPCIDeviceAssignments.end();
7085 ++it, ++i)
7086 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
7087
7088 return S_OK;
7089}
7090
7091HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
7092{
7093 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
7094
7095 return S_OK;
7096}
7097
7098HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
7099{
7100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7101
7102 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
7103
7104 return S_OK;
7105}
7106
7107HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
7108{
7109 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7110 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7111 if (SUCCEEDED(hrc))
7112 {
7113 hrc = mHWData.backupEx();
7114 if (SUCCEEDED(hrc))
7115 {
7116 i_setModified(IsModified_MachineData);
7117 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
7118 }
7119 }
7120 return hrc;
7121}
7122
7123HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
7124{
7125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7126 aTracingConfig = mHWData->mDebugging.strTracingConfig;
7127 return S_OK;
7128}
7129
7130HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
7131{
7132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7133 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7134 if (SUCCEEDED(hrc))
7135 {
7136 hrc = mHWData.backupEx();
7137 if (SUCCEEDED(hrc))
7138 {
7139 mHWData->mDebugging.strTracingConfig = aTracingConfig;
7140 if (SUCCEEDED(hrc))
7141 i_setModified(IsModified_MachineData);
7142 }
7143 }
7144 return hrc;
7145}
7146
7147HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
7148{
7149 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7150
7151 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
7152
7153 return S_OK;
7154}
7155
7156HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
7157{
7158 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7159 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7160 if (SUCCEEDED(hrc))
7161 {
7162 hrc = mHWData.backupEx();
7163 if (SUCCEEDED(hrc))
7164 {
7165 i_setModified(IsModified_MachineData);
7166 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
7167 }
7168 }
7169 return hrc;
7170}
7171
7172HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7173{
7174 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7175
7176 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7177
7178 return S_OK;
7179}
7180
7181HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7182{
7183 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7184
7185 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7186 if ( SUCCEEDED(hrc)
7187 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7188 {
7189 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7190 int vrc;
7191
7192 if (aAutostartEnabled)
7193 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7194 else
7195 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7196
7197 if (RT_SUCCESS(vrc))
7198 {
7199 hrc = mHWData.backupEx();
7200 if (SUCCEEDED(hrc))
7201 {
7202 i_setModified(IsModified_MachineData);
7203 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7204 }
7205 }
7206 else if (vrc == VERR_NOT_SUPPORTED)
7207 hrc = setError(VBOX_E_NOT_SUPPORTED,
7208 tr("The VM autostart feature is not supported on this platform"));
7209 else if (vrc == VERR_PATH_NOT_FOUND)
7210 hrc = setError(E_FAIL,
7211 tr("The path to the autostart database is not set"));
7212 else
7213 hrc = setError(E_UNEXPECTED,
7214 aAutostartEnabled ?
7215 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
7216 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
7217 mUserData->s.strName.c_str(), vrc);
7218 }
7219 return hrc;
7220}
7221
7222HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7223{
7224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7225
7226 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7227
7228 return S_OK;
7229}
7230
7231HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7232{
7233 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7234 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7235 if (SUCCEEDED(hrc))
7236 {
7237 hrc = mHWData.backupEx();
7238 if (SUCCEEDED(hrc))
7239 {
7240 i_setModified(IsModified_MachineData);
7241 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7242 }
7243 }
7244 return hrc;
7245}
7246
7247HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7248{
7249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7250
7251 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7252
7253 return S_OK;
7254}
7255
7256HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7257{
7258 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7259 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7260 if ( SUCCEEDED(hrc)
7261 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7262 {
7263 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7264 int vrc;
7265
7266 if (aAutostopType != AutostopType_Disabled)
7267 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7268 else
7269 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7270
7271 if (RT_SUCCESS(vrc))
7272 {
7273 hrc = mHWData.backupEx();
7274 if (SUCCEEDED(hrc))
7275 {
7276 i_setModified(IsModified_MachineData);
7277 mHWData->mAutostart.enmAutostopType = aAutostopType;
7278 }
7279 }
7280 else if (vrc == VERR_NOT_SUPPORTED)
7281 hrc = setError(VBOX_E_NOT_SUPPORTED,
7282 tr("The VM autostop feature is not supported on this platform"));
7283 else if (vrc == VERR_PATH_NOT_FOUND)
7284 hrc = setError(E_FAIL,
7285 tr("The path to the autostart database is not set"));
7286 else
7287 hrc = setError(E_UNEXPECTED,
7288 aAutostopType != AutostopType_Disabled ?
7289 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
7290 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
7291 mUserData->s.strName.c_str(), vrc);
7292 }
7293 return hrc;
7294}
7295
7296HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7297{
7298 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7299
7300 aDefaultFrontend = mHWData->mDefaultFrontend;
7301
7302 return S_OK;
7303}
7304
7305HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7306{
7307 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7308 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7309 if (SUCCEEDED(hrc))
7310 {
7311 hrc = mHWData.backupEx();
7312 if (SUCCEEDED(hrc))
7313 {
7314 i_setModified(IsModified_MachineData);
7315 mHWData->mDefaultFrontend = aDefaultFrontend;
7316 }
7317 }
7318 return hrc;
7319}
7320
7321HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7322{
7323 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7324 size_t cbIcon = mUserData->s.ovIcon.size();
7325 aIcon.resize(cbIcon);
7326 if (cbIcon)
7327 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7328 return S_OK;
7329}
7330
7331HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7332{
7333 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7334 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7335 if (SUCCEEDED(hrc))
7336 {
7337 i_setModified(IsModified_MachineData);
7338 mUserData.backup();
7339 size_t cbIcon = aIcon.size();
7340 mUserData->s.ovIcon.resize(cbIcon);
7341 if (cbIcon)
7342 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7343 }
7344 return hrc;
7345}
7346
7347HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7348{
7349#ifdef VBOX_WITH_USB
7350 *aUSBProxyAvailable = true;
7351#else
7352 *aUSBProxyAvailable = false;
7353#endif
7354 return S_OK;
7355}
7356
7357HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
7358{
7359 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7360
7361 *aVMProcessPriority = mUserData->s.enmVMPriority;
7362
7363 return S_OK;
7364}
7365
7366HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
7367{
7368 RT_NOREF(aVMProcessPriority);
7369 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7370 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7371 if (SUCCEEDED(hrc))
7372 {
7373 hrc = mUserData.backupEx();
7374 if (SUCCEEDED(hrc))
7375 {
7376 i_setModified(IsModified_MachineData);
7377 mUserData->s.enmVMPriority = aVMProcessPriority;
7378 }
7379 }
7380 alock.release();
7381 if (SUCCEEDED(hrc))
7382 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
7383 return hrc;
7384}
7385
7386HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7387 ComPtr<IProgress> &aProgress)
7388{
7389 ComObjPtr<Progress> pP;
7390 Progress *ppP = pP;
7391 IProgress *iP = static_cast<IProgress *>(ppP);
7392 IProgress **pProgress = &iP;
7393
7394 IMachine *pTarget = aTarget;
7395
7396 /* Convert the options. */
7397 RTCList<CloneOptions_T> optList;
7398 if (aOptions.size())
7399 for (size_t i = 0; i < aOptions.size(); ++i)
7400 optList.append(aOptions[i]);
7401
7402 if (optList.contains(CloneOptions_Link))
7403 {
7404 if (!i_isSnapshotMachine())
7405 return setError(E_INVALIDARG,
7406 tr("Linked clone can only be created from a snapshot"));
7407 if (aMode != CloneMode_MachineState)
7408 return setError(E_INVALIDARG,
7409 tr("Linked clone can only be created for a single machine state"));
7410 }
7411 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7412
7413 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7414
7415 HRESULT rc = pWorker->start(pProgress);
7416
7417 pP = static_cast<Progress *>(*pProgress);
7418 pP.queryInterfaceTo(aProgress.asOutParam());
7419
7420 return rc;
7421
7422}
7423
7424HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7425 const com::Utf8Str &aType,
7426 ComPtr<IProgress> &aProgress)
7427{
7428 LogFlowThisFuncEnter();
7429
7430 ComObjPtr<Progress> ptrProgress;
7431 HRESULT hrc = ptrProgress.createObject();
7432 if (SUCCEEDED(hrc))
7433 {
7434 com::Utf8Str strDefaultPath;
7435 if (aTargetPath.isEmpty())
7436 i_calculateFullPath(".", strDefaultPath);
7437
7438 /* Initialize our worker task */
7439 MachineMoveVM *pTask = NULL;
7440 try
7441 {
7442 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7443 }
7444 catch (std::bad_alloc &)
7445 {
7446 return E_OUTOFMEMORY;
7447 }
7448
7449 hrc = pTask->init();//no exceptions are thrown
7450
7451 if (SUCCEEDED(hrc))
7452 {
7453 hrc = pTask->createThread();
7454 pTask = NULL; /* Consumed by createThread(). */
7455 if (SUCCEEDED(hrc))
7456 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7457 else
7458 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7459 }
7460 else
7461 delete pTask;
7462 }
7463
7464 LogFlowThisFuncLeave();
7465 return hrc;
7466
7467}
7468
7469HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7470{
7471 NOREF(aProgress);
7472 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7473
7474 // This check should always fail.
7475 HRESULT rc = i_checkStateDependency(MutableStateDep);
7476 if (FAILED(rc)) return rc;
7477
7478 AssertFailedReturn(E_NOTIMPL);
7479}
7480
7481HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7482{
7483 NOREF(aSavedStateFile);
7484 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7485
7486 // This check should always fail.
7487 HRESULT rc = i_checkStateDependency(MutableStateDep);
7488 if (FAILED(rc)) return rc;
7489
7490 AssertFailedReturn(E_NOTIMPL);
7491}
7492
7493HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7494{
7495 NOREF(aFRemoveFile);
7496 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7497
7498 // This check should always fail.
7499 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7500 if (FAILED(rc)) return rc;
7501
7502 AssertFailedReturn(E_NOTIMPL);
7503}
7504
7505// public methods for internal purposes
7506/////////////////////////////////////////////////////////////////////////////
7507
7508/**
7509 * Adds the given IsModified_* flag to the dirty flags of the machine.
7510 * This must be called either during i_loadSettings or under the machine write lock.
7511 * @param fl Flag
7512 * @param fAllowStateModification If state modifications are allowed.
7513 */
7514void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7515{
7516 mData->flModifications |= fl;
7517 if (fAllowStateModification && i_isStateModificationAllowed())
7518 mData->mCurrentStateModified = true;
7519}
7520
7521/**
7522 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7523 * care of the write locking.
7524 *
7525 * @param fModification The flag to add.
7526 * @param fAllowStateModification If state modifications are allowed.
7527 */
7528void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7529{
7530 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7531 i_setModified(fModification, fAllowStateModification);
7532}
7533
7534/**
7535 * Saves the registry entry of this machine to the given configuration node.
7536 *
7537 * @param data Machine registry data.
7538 *
7539 * @note locks this object for reading.
7540 */
7541HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7542{
7543 AutoLimitedCaller autoCaller(this);
7544 AssertComRCReturnRC(autoCaller.rc());
7545
7546 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7547
7548 data.uuid = mData->mUuid;
7549 data.strSettingsFile = mData->m_strConfigFile;
7550
7551 return S_OK;
7552}
7553
7554/**
7555 * Calculates the absolute path of the given path taking the directory of the
7556 * machine settings file as the current directory.
7557 *
7558 * @param strPath Path to calculate the absolute path for.
7559 * @param aResult Where to put the result (used only on success, can be the
7560 * same Utf8Str instance as passed in @a aPath).
7561 * @return IPRT result.
7562 *
7563 * @note Locks this object for reading.
7564 */
7565int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7566{
7567 AutoCaller autoCaller(this);
7568 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7569
7570 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7571
7572 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7573
7574 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7575
7576 strSettingsDir.stripFilename();
7577 char szFolder[RTPATH_MAX];
7578 size_t cbFolder = sizeof(szFolder);
7579 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7580 if (RT_SUCCESS(vrc))
7581 aResult = szFolder;
7582
7583 return vrc;
7584}
7585
7586/**
7587 * Copies strSource to strTarget, making it relative to the machine folder
7588 * if it is a subdirectory thereof, or simply copying it otherwise.
7589 *
7590 * @param strSource Path to evaluate and copy.
7591 * @param strTarget Buffer to receive target path.
7592 *
7593 * @note Locks this object for reading.
7594 */
7595void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7596 Utf8Str &strTarget)
7597{
7598 AutoCaller autoCaller(this);
7599 AssertComRCReturn(autoCaller.rc(), (void)0);
7600
7601 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7602
7603 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7604 // use strTarget as a temporary buffer to hold the machine settings dir
7605 strTarget = mData->m_strConfigFileFull;
7606 strTarget.stripFilename();
7607 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7608 {
7609 // is relative: then append what's left
7610 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7611 // for empty paths (only possible for subdirs) use "." to avoid
7612 // triggering default settings for not present config attributes.
7613 if (strTarget.isEmpty())
7614 strTarget = ".";
7615 }
7616 else
7617 // is not relative: then overwrite
7618 strTarget = strSource;
7619}
7620
7621/**
7622 * Returns the full path to the machine's log folder in the
7623 * \a aLogFolder argument.
7624 */
7625void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7626{
7627 AutoCaller autoCaller(this);
7628 AssertComRCReturnVoid(autoCaller.rc());
7629
7630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7631
7632 char szTmp[RTPATH_MAX];
7633 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7634 if (RT_SUCCESS(vrc))
7635 {
7636 if (szTmp[0] && !mUserData.isNull())
7637 {
7638 char szTmp2[RTPATH_MAX];
7639 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7640 if (RT_SUCCESS(vrc))
7641 aLogFolder.printf("%s%c%s",
7642 szTmp2,
7643 RTPATH_DELIMITER,
7644 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7645 }
7646 else
7647 vrc = VERR_PATH_IS_RELATIVE;
7648 }
7649
7650 if (RT_FAILURE(vrc))
7651 {
7652 // fallback if VBOX_USER_LOGHOME is not set or invalid
7653 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7654 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7655 aLogFolder.append(RTPATH_DELIMITER);
7656 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7657 }
7658}
7659
7660/**
7661 * Returns the full path to the machine's log file for an given index.
7662 */
7663Utf8Str Machine::i_getLogFilename(ULONG idx)
7664{
7665 Utf8Str logFolder;
7666 getLogFolder(logFolder);
7667 Assert(logFolder.length());
7668
7669 Utf8Str log;
7670 if (idx == 0)
7671 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7672#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7673 else if (idx == 1)
7674 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7675 else
7676 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7677#else
7678 else
7679 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7680#endif
7681 return log;
7682}
7683
7684/**
7685 * Returns the full path to the machine's hardened log file.
7686 */
7687Utf8Str Machine::i_getHardeningLogFilename(void)
7688{
7689 Utf8Str strFilename;
7690 getLogFolder(strFilename);
7691 Assert(strFilename.length());
7692 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7693 return strFilename;
7694}
7695
7696/**
7697 * Returns the default NVRAM filename based on the location of the VM config.
7698 * Note that this is a relative path.
7699 */
7700Utf8Str Machine::i_getDefaultNVRAMFilename()
7701{
7702 AutoCaller autoCaller(this);
7703 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7704
7705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7706
7707 if (i_isSnapshotMachine())
7708 return Utf8Str::Empty;
7709
7710 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7711 strNVRAMFilePath.stripPath();
7712 strNVRAMFilePath.stripSuffix();
7713 strNVRAMFilePath += ".nvram";
7714
7715 return strNVRAMFilePath;
7716}
7717
7718/**
7719 * Returns the NVRAM filename for a new snapshot. This intentionally works
7720 * similarly to the saved state file naming. Note that this is usually
7721 * a relative path, unless the snapshot folder is absolute.
7722 */
7723Utf8Str Machine::i_getSnapshotNVRAMFilename()
7724{
7725 AutoCaller autoCaller(this);
7726 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7727
7728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7729
7730 RTTIMESPEC ts;
7731 RTTimeNow(&ts);
7732 RTTIME time;
7733 RTTimeExplode(&time, &ts);
7734
7735 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7736 strNVRAMFilePath += RTPATH_DELIMITER;
7737 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7738 time.i32Year, time.u8Month, time.u8MonthDay,
7739 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7740
7741 return strNVRAMFilePath;
7742}
7743
7744/**
7745 * Returns the version of the settings file.
7746 */
7747SettingsVersion_T Machine::i_getSettingsVersion(void)
7748{
7749 AutoCaller autoCaller(this);
7750 AssertComRCReturn(autoCaller.rc(), SettingsVersion_Null);
7751
7752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7753
7754 return mData->pMachineConfigFile->getSettingsVersion();
7755}
7756
7757/**
7758 * Composes a unique saved state filename based on the current system time. The filename is
7759 * granular to the second so this will work so long as no more than one snapshot is taken on
7760 * a machine per second.
7761 *
7762 * Before version 4.1, we used this formula for saved state files:
7763 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7764 * which no longer works because saved state files can now be shared between the saved state of the
7765 * "saved" machine and an online snapshot, and the following would cause problems:
7766 * 1) save machine
7767 * 2) create online snapshot from that machine state --> reusing saved state file
7768 * 3) save machine again --> filename would be reused, breaking the online snapshot
7769 *
7770 * So instead we now use a timestamp.
7771 *
7772 * @param strStateFilePath
7773 */
7774
7775void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7776{
7777 AutoCaller autoCaller(this);
7778 AssertComRCReturnVoid(autoCaller.rc());
7779
7780 {
7781 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7782 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7783 }
7784
7785 RTTIMESPEC ts;
7786 RTTimeNow(&ts);
7787 RTTIME time;
7788 RTTimeExplode(&time, &ts);
7789
7790 strStateFilePath += RTPATH_DELIMITER;
7791 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7792 time.i32Year, time.u8Month, time.u8MonthDay,
7793 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7794}
7795
7796/**
7797 * Returns whether at least one USB controller is present for the VM.
7798 */
7799bool Machine::i_isUSBControllerPresent()
7800{
7801 AutoCaller autoCaller(this);
7802 AssertComRCReturn(autoCaller.rc(), false);
7803
7804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7805
7806 return (mUSBControllers->size() > 0);
7807}
7808
7809
7810/**
7811 * @note Locks this object for writing, calls the client process
7812 * (inside the lock).
7813 */
7814HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7815 const Utf8Str &strFrontend,
7816 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7817 ProgressProxy *aProgress)
7818{
7819 LogFlowThisFuncEnter();
7820
7821 AssertReturn(aControl, E_FAIL);
7822 AssertReturn(aProgress, E_FAIL);
7823 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7824
7825 AutoCaller autoCaller(this);
7826 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7827
7828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7829
7830 if (!mData->mRegistered)
7831 return setError(E_UNEXPECTED,
7832 tr("The machine '%s' is not registered"),
7833 mUserData->s.strName.c_str());
7834
7835 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7836
7837 /* The process started when launching a VM with separate UI/VM processes is always
7838 * the UI process, i.e. needs special handling as it won't claim the session. */
7839 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7840
7841 if (fSeparate)
7842 {
7843 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7844 return setError(VBOX_E_INVALID_OBJECT_STATE,
7845 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7846 mUserData->s.strName.c_str());
7847 }
7848 else
7849 {
7850 if ( mData->mSession.mState == SessionState_Locked
7851 || mData->mSession.mState == SessionState_Spawning
7852 || mData->mSession.mState == SessionState_Unlocking)
7853 return setError(VBOX_E_INVALID_OBJECT_STATE,
7854 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7855 mUserData->s.strName.c_str());
7856
7857 /* may not be busy */
7858 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7859 }
7860
7861 /* Hardening logging */
7862#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7863 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7864 {
7865 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7866 int vrc2;
7867 /* ignore rc */ i_deleteFile(strHardeningLogFile, false /* fIgnoreFailures */, tr("hardening log file"), &vrc2);
7868 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7869 {
7870 Utf8Str strStartupLogDir = strHardeningLogFile;
7871 strStartupLogDir.stripFilename();
7872 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7873 file without stripping the file. */
7874 }
7875 strSupHardeningLogArg.append(strHardeningLogFile);
7876
7877 /* Remove legacy log filename to avoid confusion. */
7878 Utf8Str strOldStartupLogFile;
7879 getLogFolder(strOldStartupLogFile);
7880 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7881 i_deleteFile(strOldStartupLogFile, true /* fIgnoreFailures */);
7882 }
7883#else
7884 Utf8Str strSupHardeningLogArg;
7885#endif
7886
7887 Utf8Str strAppOverride;
7888#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7889 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7890#endif
7891
7892 bool fUseVBoxSDS = false;
7893 Utf8Str strCanonicalName;
7894 if (false)
7895 { }
7896#ifdef VBOX_WITH_QTGUI
7897 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7898 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7899 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7900 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7901 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7902 {
7903 strCanonicalName = "GUI/Qt";
7904 fUseVBoxSDS = true;
7905 }
7906#endif
7907#ifdef VBOX_WITH_VBOXSDL
7908 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7909 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7910 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7911 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7912 {
7913 strCanonicalName = "GUI/SDL";
7914 fUseVBoxSDS = true;
7915 }
7916#endif
7917#ifdef VBOX_WITH_HEADLESS
7918 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7919 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7920 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7921 {
7922 strCanonicalName = "headless";
7923 }
7924#endif
7925 else
7926 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7927
7928 Utf8Str idStr = mData->mUuid.toString();
7929 Utf8Str const &strMachineName = mUserData->s.strName;
7930 RTPROCESS pid = NIL_RTPROCESS;
7931
7932#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7933 RT_NOREF(fUseVBoxSDS);
7934#else
7935 DWORD idCallerSession = ~(DWORD)0;
7936 if (fUseVBoxSDS)
7937 {
7938 /*
7939 * The VBoxSDS should be used for process launching the VM with
7940 * GUI only if the caller and the VBoxSDS are in different Windows
7941 * sessions and the caller in the interactive one.
7942 */
7943 fUseVBoxSDS = false;
7944
7945 /* Get windows session of the current process. The process token used
7946 due to several reasons:
7947 1. The token is absent for the current thread except someone set it
7948 for us.
7949 2. Needs to get the id of the session where the process is started.
7950 We only need to do this once, though. */
7951 static DWORD s_idCurrentSession = ~(DWORD)0;
7952 DWORD idCurrentSession = s_idCurrentSession;
7953 if (idCurrentSession == ~(DWORD)0)
7954 {
7955 HANDLE hCurrentProcessToken = NULL;
7956 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7957 {
7958 DWORD cbIgn = 0;
7959 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7960 s_idCurrentSession = idCurrentSession;
7961 else
7962 {
7963 idCurrentSession = ~(DWORD)0;
7964 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7965 }
7966 CloseHandle(hCurrentProcessToken);
7967 }
7968 else
7969 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7970 }
7971
7972 /* get the caller's session */
7973 HRESULT hrc = CoImpersonateClient();
7974 if (SUCCEEDED(hrc))
7975 {
7976 HANDLE hCallerThreadToken;
7977 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7978 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7979 &hCallerThreadToken))
7980 {
7981 SetLastError(NO_ERROR);
7982 DWORD cbIgn = 0;
7983 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7984 {
7985 /* Only need to use SDS if the session ID differs: */
7986 if (idCurrentSession != idCallerSession)
7987 {
7988 fUseVBoxSDS = false;
7989
7990 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7991 DWORD cbTokenGroups = 0;
7992 PTOKEN_GROUPS pTokenGroups = NULL;
7993 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7994 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7995 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7996 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7997 {
7998 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7999 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
8000 PSID pInteractiveSid = NULL;
8001 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
8002 {
8003 /* Iterate over the groups looking for the interactive SID: */
8004 fUseVBoxSDS = false;
8005 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
8006 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
8007 {
8008 fUseVBoxSDS = true;
8009 break;
8010 }
8011 FreeSid(pInteractiveSid);
8012 }
8013 }
8014 else
8015 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
8016 RTMemTmpFree(pTokenGroups);
8017 }
8018 }
8019 else
8020 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
8021 CloseHandle(hCallerThreadToken);
8022 }
8023 else
8024 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
8025 CoRevertToSelf();
8026 }
8027 else
8028 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
8029 }
8030 if (fUseVBoxSDS)
8031 {
8032 /* connect to VBoxSDS */
8033 ComPtr<IVirtualBoxSDS> pVBoxSDS;
8034 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
8035 if (FAILED(rc))
8036 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
8037 strMachineName.c_str());
8038
8039 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
8040 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
8041 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
8042 service to access the files. */
8043 rc = CoSetProxyBlanket(pVBoxSDS,
8044 RPC_C_AUTHN_DEFAULT,
8045 RPC_C_AUTHZ_DEFAULT,
8046 COLE_DEFAULT_PRINCIPAL,
8047 RPC_C_AUTHN_LEVEL_DEFAULT,
8048 RPC_C_IMP_LEVEL_IMPERSONATE,
8049 NULL,
8050 EOAC_DEFAULT);
8051 if (FAILED(rc))
8052 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
8053
8054 size_t const cEnvVars = aEnvironmentChanges.size();
8055 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
8056 for (size_t i = 0; i < cEnvVars; i++)
8057 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
8058
8059 ULONG uPid = 0;
8060 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
8061 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
8062 idCallerSession, &uPid);
8063 if (FAILED(rc))
8064 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
8065 pid = (RTPROCESS)uPid;
8066 }
8067 else
8068#endif /* VBOX_WITH_SDS && RT_OS_WINDOWS */
8069 {
8070 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
8071 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
8072 if (RT_FAILURE(vrc))
8073 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
8074 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
8075 }
8076
8077 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
8078 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
8079
8080 if (!fSeparate)
8081 {
8082 /*
8083 * Note that we don't release the lock here before calling the client,
8084 * because it doesn't need to call us back if called with a NULL argument.
8085 * Releasing the lock here is dangerous because we didn't prepare the
8086 * launch data yet, but the client we've just started may happen to be
8087 * too fast and call LockMachine() that will fail (because of PID, etc.),
8088 * so that the Machine will never get out of the Spawning session state.
8089 */
8090
8091 /* inform the session that it will be a remote one */
8092 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8093#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8094 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8095#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8096 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8097#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8098 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8099
8100 if (FAILED(rc))
8101 {
8102 /* restore the session state */
8103 mData->mSession.mState = SessionState_Unlocked;
8104 alock.release();
8105 mParent->i_addProcessToReap(pid);
8106 /* The failure may occur w/o any error info (from RPC), so provide one */
8107 return setError(VBOX_E_VM_ERROR,
8108 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8109 }
8110
8111 /* attach launch data to the machine */
8112 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8113 mData->mSession.mRemoteControls.push_back(aControl);
8114 mData->mSession.mProgress = aProgress;
8115 mData->mSession.mPID = pid;
8116 mData->mSession.mState = SessionState_Spawning;
8117 Assert(strCanonicalName.isNotEmpty());
8118 mData->mSession.mName = strCanonicalName;
8119 }
8120 else
8121 {
8122 /* For separate UI process we declare the launch as completed instantly, as the
8123 * actual headless VM start may or may not come. No point in remembering anything
8124 * yet, as what matters for us is when the headless VM gets started. */
8125 aProgress->i_notifyComplete(S_OK);
8126 }
8127
8128 alock.release();
8129 mParent->i_addProcessToReap(pid);
8130
8131 LogFlowThisFuncLeave();
8132 return S_OK;
8133}
8134
8135/**
8136 * Returns @c true if the given session machine instance has an open direct
8137 * session (and optionally also for direct sessions which are closing) and
8138 * returns the session control machine instance if so.
8139 *
8140 * Note that when the method returns @c false, the arguments remain unchanged.
8141 *
8142 * @param aMachine Session machine object.
8143 * @param aControl Direct session control object (optional).
8144 * @param aRequireVM If true then only allow VM sessions.
8145 * @param aAllowClosing If true then additionally a session which is currently
8146 * being closed will also be allowed.
8147 *
8148 * @note locks this object for reading.
8149 */
8150bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8151 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8152 bool aRequireVM /*= false*/,
8153 bool aAllowClosing /*= false*/)
8154{
8155 AutoLimitedCaller autoCaller(this);
8156 AssertComRCReturn(autoCaller.rc(), false);
8157
8158 /* just return false for inaccessible machines */
8159 if (getObjectState().getState() != ObjectState::Ready)
8160 return false;
8161
8162 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8163
8164 if ( ( mData->mSession.mState == SessionState_Locked
8165 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
8166 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8167 )
8168 {
8169 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8170
8171 aMachine = mData->mSession.mMachine;
8172
8173 if (aControl != NULL)
8174 *aControl = mData->mSession.mDirectControl;
8175
8176 return true;
8177 }
8178
8179 return false;
8180}
8181
8182/**
8183 * Returns @c true if the given machine has an spawning direct session.
8184 *
8185 * @note locks this object for reading.
8186 */
8187bool Machine::i_isSessionSpawning()
8188{
8189 AutoLimitedCaller autoCaller(this);
8190 AssertComRCReturn(autoCaller.rc(), false);
8191
8192 /* just return false for inaccessible machines */
8193 if (getObjectState().getState() != ObjectState::Ready)
8194 return false;
8195
8196 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8197
8198 if (mData->mSession.mState == SessionState_Spawning)
8199 return true;
8200
8201 return false;
8202}
8203
8204/**
8205 * Called from the client watcher thread to check for unexpected client process
8206 * death during Session_Spawning state (e.g. before it successfully opened a
8207 * direct session).
8208 *
8209 * On Win32 and on OS/2, this method is called only when we've got the
8210 * direct client's process termination notification, so it always returns @c
8211 * true.
8212 *
8213 * On other platforms, this method returns @c true if the client process is
8214 * terminated and @c false if it's still alive.
8215 *
8216 * @note Locks this object for writing.
8217 */
8218bool Machine::i_checkForSpawnFailure()
8219{
8220 AutoCaller autoCaller(this);
8221 if (!autoCaller.isOk())
8222 {
8223 /* nothing to do */
8224 LogFlowThisFunc(("Already uninitialized!\n"));
8225 return true;
8226 }
8227
8228 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8229
8230 if (mData->mSession.mState != SessionState_Spawning)
8231 {
8232 /* nothing to do */
8233 LogFlowThisFunc(("Not spawning any more!\n"));
8234 return true;
8235 }
8236
8237 HRESULT rc = S_OK;
8238
8239 /* PID not yet initialized, skip check. */
8240 if (mData->mSession.mPID == NIL_RTPROCESS)
8241 return false;
8242
8243 RTPROCSTATUS status;
8244 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8245
8246 if (vrc != VERR_PROCESS_RUNNING)
8247 {
8248 Utf8Str strExtraInfo;
8249
8250#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8251 /* If the startup logfile exists and is of non-zero length, tell the
8252 user to look there for more details to encourage them to attach it
8253 when reporting startup issues. */
8254 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8255 uint64_t cbStartupLogFile = 0;
8256 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
8257 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8258 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
8259#endif
8260
8261 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8262 rc = setError(E_FAIL,
8263 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8264 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8265 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8266 rc = setError(E_FAIL,
8267 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8268 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8269 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8270 rc = setError(E_FAIL,
8271 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8272 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8273 else
8274 rc = setErrorBoth(E_FAIL, vrc,
8275 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8276 i_getName().c_str(), vrc, strExtraInfo.c_str());
8277 }
8278
8279 if (FAILED(rc))
8280 {
8281 /* Close the remote session, remove the remote control from the list
8282 * and reset session state to Closed (@note keep the code in sync with
8283 * the relevant part in LockMachine()). */
8284
8285 Assert(mData->mSession.mRemoteControls.size() == 1);
8286 if (mData->mSession.mRemoteControls.size() == 1)
8287 {
8288 ErrorInfoKeeper eik;
8289 mData->mSession.mRemoteControls.front()->Uninitialize();
8290 }
8291
8292 mData->mSession.mRemoteControls.clear();
8293 mData->mSession.mState = SessionState_Unlocked;
8294
8295 /* finalize the progress after setting the state */
8296 if (!mData->mSession.mProgress.isNull())
8297 {
8298 mData->mSession.mProgress->notifyComplete(rc);
8299 mData->mSession.mProgress.setNull();
8300 }
8301
8302 mData->mSession.mPID = NIL_RTPROCESS;
8303
8304 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
8305 return true;
8306 }
8307
8308 return false;
8309}
8310
8311/**
8312 * Checks whether the machine can be registered. If so, commits and saves
8313 * all settings.
8314 *
8315 * @note Must be called from mParent's write lock. Locks this object and
8316 * children for writing.
8317 */
8318HRESULT Machine::i_prepareRegister()
8319{
8320 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8321
8322 AutoLimitedCaller autoCaller(this);
8323 AssertComRCReturnRC(autoCaller.rc());
8324
8325 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8326
8327 /* wait for state dependents to drop to zero */
8328 i_ensureNoStateDependencies(alock);
8329
8330 if (!mData->mAccessible)
8331 return setError(VBOX_E_INVALID_OBJECT_STATE,
8332 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8333 mUserData->s.strName.c_str(),
8334 mData->mUuid.toString().c_str());
8335
8336 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8337
8338 if (mData->mRegistered)
8339 return setError(VBOX_E_INVALID_OBJECT_STATE,
8340 tr("The machine '%s' with UUID {%s} is already registered"),
8341 mUserData->s.strName.c_str(),
8342 mData->mUuid.toString().c_str());
8343
8344 HRESULT rc = S_OK;
8345
8346 // Ensure the settings are saved. If we are going to be registered and
8347 // no config file exists yet, create it by calling i_saveSettings() too.
8348 if ( (mData->flModifications)
8349 || (!mData->pMachineConfigFile->fileExists())
8350 )
8351 {
8352 rc = i_saveSettings(NULL, alock);
8353 // no need to check whether VirtualBox.xml needs saving too since
8354 // we can't have a machine XML file rename pending
8355 if (FAILED(rc)) return rc;
8356 }
8357
8358 /* more config checking goes here */
8359
8360 if (SUCCEEDED(rc))
8361 {
8362 /* we may have had implicit modifications we want to fix on success */
8363 i_commit();
8364
8365 mData->mRegistered = true;
8366 }
8367 else
8368 {
8369 /* we may have had implicit modifications we want to cancel on failure*/
8370 i_rollback(false /* aNotify */);
8371 }
8372
8373 return rc;
8374}
8375
8376/**
8377 * Increases the number of objects dependent on the machine state or on the
8378 * registered state. Guarantees that these two states will not change at least
8379 * until #i_releaseStateDependency() is called.
8380 *
8381 * Depending on the @a aDepType value, additional state checks may be made.
8382 * These checks will set extended error info on failure. See
8383 * #i_checkStateDependency() for more info.
8384 *
8385 * If this method returns a failure, the dependency is not added and the caller
8386 * is not allowed to rely on any particular machine state or registration state
8387 * value and may return the failed result code to the upper level.
8388 *
8389 * @param aDepType Dependency type to add.
8390 * @param aState Current machine state (NULL if not interested).
8391 * @param aRegistered Current registered state (NULL if not interested).
8392 *
8393 * @note Locks this object for writing.
8394 */
8395HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8396 MachineState_T *aState /* = NULL */,
8397 BOOL *aRegistered /* = NULL */)
8398{
8399 AutoCaller autoCaller(this);
8400 AssertComRCReturnRC(autoCaller.rc());
8401
8402 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8403
8404 HRESULT rc = i_checkStateDependency(aDepType);
8405 if (FAILED(rc)) return rc;
8406
8407 {
8408 if (mData->mMachineStateChangePending != 0)
8409 {
8410 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8411 * drop to zero so don't add more. It may make sense to wait a bit
8412 * and retry before reporting an error (since the pending state
8413 * transition should be really quick) but let's just assert for
8414 * now to see if it ever happens on practice. */
8415
8416 AssertFailed();
8417
8418 return setError(E_ACCESSDENIED,
8419 tr("Machine state change is in progress. Please retry the operation later."));
8420 }
8421
8422 ++mData->mMachineStateDeps;
8423 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8424 }
8425
8426 if (aState)
8427 *aState = mData->mMachineState;
8428 if (aRegistered)
8429 *aRegistered = mData->mRegistered;
8430
8431 return S_OK;
8432}
8433
8434/**
8435 * Decreases the number of objects dependent on the machine state.
8436 * Must always complete the #i_addStateDependency() call after the state
8437 * dependency is no more necessary.
8438 */
8439void Machine::i_releaseStateDependency()
8440{
8441 AutoCaller autoCaller(this);
8442 AssertComRCReturnVoid(autoCaller.rc());
8443
8444 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8445
8446 /* releaseStateDependency() w/o addStateDependency()? */
8447 AssertReturnVoid(mData->mMachineStateDeps != 0);
8448 -- mData->mMachineStateDeps;
8449
8450 if (mData->mMachineStateDeps == 0)
8451 {
8452 /* inform i_ensureNoStateDependencies() that there are no more deps */
8453 if (mData->mMachineStateChangePending != 0)
8454 {
8455 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8456 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8457 }
8458 }
8459}
8460
8461Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8462{
8463 /* start with nothing found */
8464 Utf8Str strResult("");
8465
8466 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8467
8468 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8469 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8470 // found:
8471 strResult = it->second; // source is a Utf8Str
8472
8473 return strResult;
8474}
8475
8476// protected methods
8477/////////////////////////////////////////////////////////////////////////////
8478
8479/**
8480 * Performs machine state checks based on the @a aDepType value. If a check
8481 * fails, this method will set extended error info, otherwise it will return
8482 * S_OK. It is supposed, that on failure, the caller will immediately return
8483 * the return value of this method to the upper level.
8484 *
8485 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8486 *
8487 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8488 * current state of this machine object allows to change settings of the
8489 * machine (i.e. the machine is not registered, or registered but not running
8490 * and not saved). It is useful to call this method from Machine setters
8491 * before performing any change.
8492 *
8493 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8494 * as for MutableStateDep except that if the machine is saved, S_OK is also
8495 * returned. This is useful in setters which allow changing machine
8496 * properties when it is in the saved state.
8497 *
8498 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8499 * if the current state of this machine object allows to change runtime
8500 * changeable settings of the machine (i.e. the machine is not registered, or
8501 * registered but either running or not running and not saved). It is useful
8502 * to call this method from Machine setters before performing any changes to
8503 * runtime changeable settings.
8504 *
8505 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8506 * the same as for MutableOrRunningStateDep except that if the machine is
8507 * saved, S_OK is also returned. This is useful in setters which allow
8508 * changing runtime and saved state changeable machine properties.
8509 *
8510 * @param aDepType Dependency type to check.
8511 *
8512 * @note Non Machine based classes should use #i_addStateDependency() and
8513 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8514 * template.
8515 *
8516 * @note This method must be called from under this object's read or write
8517 * lock.
8518 */
8519HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8520{
8521 switch (aDepType)
8522 {
8523 case AnyStateDep:
8524 {
8525 break;
8526 }
8527 case MutableStateDep:
8528 {
8529 if ( mData->mRegistered
8530 && ( !i_isSessionMachine()
8531 || ( mData->mMachineState != MachineState_Aborted
8532 && mData->mMachineState != MachineState_Teleported
8533 && mData->mMachineState != MachineState_PoweredOff
8534 )
8535 )
8536 )
8537 return setError(VBOX_E_INVALID_VM_STATE,
8538 tr("The machine is not mutable (state is %s)"),
8539 Global::stringifyMachineState(mData->mMachineState));
8540 break;
8541 }
8542 case MutableOrSavedStateDep:
8543 {
8544 if ( mData->mRegistered
8545 && ( !i_isSessionMachine()
8546 || ( mData->mMachineState != MachineState_Aborted
8547 && mData->mMachineState != MachineState_Teleported
8548 && mData->mMachineState != MachineState_Saved
8549 && mData->mMachineState != MachineState_AbortedSaved
8550 && mData->mMachineState != MachineState_PoweredOff
8551 )
8552 )
8553 )
8554 return setError(VBOX_E_INVALID_VM_STATE,
8555 tr("The machine is not mutable or saved (state is %s)"),
8556 Global::stringifyMachineState(mData->mMachineState));
8557 break;
8558 }
8559 case MutableOrRunningStateDep:
8560 {
8561 if ( mData->mRegistered
8562 && ( !i_isSessionMachine()
8563 || ( mData->mMachineState != MachineState_Aborted
8564 && mData->mMachineState != MachineState_Teleported
8565 && mData->mMachineState != MachineState_PoweredOff
8566 && !Global::IsOnline(mData->mMachineState)
8567 )
8568 )
8569 )
8570 return setError(VBOX_E_INVALID_VM_STATE,
8571 tr("The machine is not mutable or running (state is %s)"),
8572 Global::stringifyMachineState(mData->mMachineState));
8573 break;
8574 }
8575 case MutableOrSavedOrRunningStateDep:
8576 {
8577 if ( mData->mRegistered
8578 && ( !i_isSessionMachine()
8579 || ( mData->mMachineState != MachineState_Aborted
8580 && mData->mMachineState != MachineState_Teleported
8581 && mData->mMachineState != MachineState_Saved
8582 && mData->mMachineState != MachineState_AbortedSaved
8583 && mData->mMachineState != MachineState_PoweredOff
8584 && !Global::IsOnline(mData->mMachineState)
8585 )
8586 )
8587 )
8588 return setError(VBOX_E_INVALID_VM_STATE,
8589 tr("The machine is not mutable, saved or running (state is %s)"),
8590 Global::stringifyMachineState(mData->mMachineState));
8591 break;
8592 }
8593 }
8594
8595 return S_OK;
8596}
8597
8598/**
8599 * Helper to initialize all associated child objects and allocate data
8600 * structures.
8601 *
8602 * This method must be called as a part of the object's initialization procedure
8603 * (usually done in the #init() method).
8604 *
8605 * @note Must be called only from #init() or from #i_registeredInit().
8606 */
8607HRESULT Machine::initDataAndChildObjects()
8608{
8609 AutoCaller autoCaller(this);
8610 AssertComRCReturnRC(autoCaller.rc());
8611 AssertReturn( getObjectState().getState() == ObjectState::InInit
8612 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8613
8614 AssertReturn(!mData->mAccessible, E_FAIL);
8615
8616 /* allocate data structures */
8617 mSSData.allocate();
8618 mUserData.allocate();
8619 mHWData.allocate();
8620 mMediumAttachments.allocate();
8621 mStorageControllers.allocate();
8622 mUSBControllers.allocate();
8623
8624 /* initialize mOSTypeId */
8625 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8626
8627/** @todo r=bird: init() methods never fails, right? Why don't we make them
8628 * return void then! */
8629
8630 /* create associated BIOS settings object */
8631 unconst(mBIOSSettings).createObject();
8632 mBIOSSettings->init(this);
8633
8634 /* create associated trusted platform module object */
8635 unconst(mTrustedPlatformModule).createObject();
8636 mTrustedPlatformModule->init(this);
8637
8638 /* create associated NVRAM store object */
8639 unconst(mNvramStore).createObject();
8640 mNvramStore->init(this);
8641
8642 /* create associated record settings object */
8643 unconst(mRecordingSettings).createObject();
8644 mRecordingSettings->init(this);
8645
8646 /* create the graphics adapter object (always present) */
8647 unconst(mGraphicsAdapter).createObject();
8648 mGraphicsAdapter->init(this);
8649
8650 /* create an associated VRDE object (default is disabled) */
8651 unconst(mVRDEServer).createObject();
8652 mVRDEServer->init(this);
8653
8654 /* create associated serial port objects */
8655 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8656 {
8657 unconst(mSerialPorts[slot]).createObject();
8658 mSerialPorts[slot]->init(this, slot);
8659 }
8660
8661 /* create associated parallel port objects */
8662 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8663 {
8664 unconst(mParallelPorts[slot]).createObject();
8665 mParallelPorts[slot]->init(this, slot);
8666 }
8667
8668 /* create the audio settings object */
8669 unconst(mAudioSettings).createObject();
8670 mAudioSettings->init(this);
8671
8672 /* create the USB device filters object (always present) */
8673 unconst(mUSBDeviceFilters).createObject();
8674 mUSBDeviceFilters->init(this);
8675
8676 /* create associated network adapter objects */
8677 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8678 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8679 {
8680 unconst(mNetworkAdapters[slot]).createObject();
8681 mNetworkAdapters[slot]->init(this, slot);
8682 }
8683
8684 /* create the bandwidth control */
8685 unconst(mBandwidthControl).createObject();
8686 mBandwidthControl->init(this);
8687
8688 return S_OK;
8689}
8690
8691/**
8692 * Helper to uninitialize all associated child objects and to free all data
8693 * structures.
8694 *
8695 * This method must be called as a part of the object's uninitialization
8696 * procedure (usually done in the #uninit() method).
8697 *
8698 * @note Must be called only from #uninit() or from #i_registeredInit().
8699 */
8700void Machine::uninitDataAndChildObjects()
8701{
8702 AutoCaller autoCaller(this);
8703 AssertComRCReturnVoid(autoCaller.rc());
8704 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8705 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8706 || getObjectState().getState() == ObjectState::InUninit
8707 || getObjectState().getState() == ObjectState::Limited);
8708
8709 /* tell all our other child objects we've been uninitialized */
8710 if (mBandwidthControl)
8711 {
8712 mBandwidthControl->uninit();
8713 unconst(mBandwidthControl).setNull();
8714 }
8715
8716 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8717 {
8718 if (mNetworkAdapters[slot])
8719 {
8720 mNetworkAdapters[slot]->uninit();
8721 unconst(mNetworkAdapters[slot]).setNull();
8722 }
8723 }
8724
8725 if (mUSBDeviceFilters)
8726 {
8727 mUSBDeviceFilters->uninit();
8728 unconst(mUSBDeviceFilters).setNull();
8729 }
8730
8731 if (mAudioSettings)
8732 {
8733 mAudioSettings->uninit();
8734 unconst(mAudioSettings).setNull();
8735 }
8736
8737 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8738 {
8739 if (mParallelPorts[slot])
8740 {
8741 mParallelPorts[slot]->uninit();
8742 unconst(mParallelPorts[slot]).setNull();
8743 }
8744 }
8745
8746 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8747 {
8748 if (mSerialPorts[slot])
8749 {
8750 mSerialPorts[slot]->uninit();
8751 unconst(mSerialPorts[slot]).setNull();
8752 }
8753 }
8754
8755 if (mVRDEServer)
8756 {
8757 mVRDEServer->uninit();
8758 unconst(mVRDEServer).setNull();
8759 }
8760
8761 if (mGraphicsAdapter)
8762 {
8763 mGraphicsAdapter->uninit();
8764 unconst(mGraphicsAdapter).setNull();
8765 }
8766
8767 if (mBIOSSettings)
8768 {
8769 mBIOSSettings->uninit();
8770 unconst(mBIOSSettings).setNull();
8771 }
8772
8773 if (mTrustedPlatformModule)
8774 {
8775 mTrustedPlatformModule->uninit();
8776 unconst(mTrustedPlatformModule).setNull();
8777 }
8778
8779 if (mNvramStore)
8780 {
8781 mNvramStore->uninit();
8782 unconst(mNvramStore).setNull();
8783 }
8784
8785 if (mRecordingSettings)
8786 {
8787 mRecordingSettings->uninit();
8788 unconst(mRecordingSettings).setNull();
8789 }
8790
8791 /* Deassociate media (only when a real Machine or a SnapshotMachine
8792 * instance is uninitialized; SessionMachine instances refer to real
8793 * Machine media). This is necessary for a clean re-initialization of
8794 * the VM after successfully re-checking the accessibility state. Note
8795 * that in case of normal Machine or SnapshotMachine uninitialization (as
8796 * a result of unregistering or deleting the snapshot), outdated media
8797 * attachments will already be uninitialized and deleted, so this
8798 * code will not affect them. */
8799 if ( !mMediumAttachments.isNull()
8800 && !i_isSessionMachine()
8801 )
8802 {
8803 for (MediumAttachmentList::const_iterator
8804 it = mMediumAttachments->begin();
8805 it != mMediumAttachments->end();
8806 ++it)
8807 {
8808 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8809 if (pMedium.isNull())
8810 continue;
8811 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8812 AssertComRC(rc);
8813 }
8814 }
8815
8816 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8817 {
8818 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children)
8819 if (mData->mFirstSnapshot)
8820 {
8821 // Snapshots tree is protected by machine write lock.
8822 // Otherwise we assert in Snapshot::uninit()
8823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8824 mData->mFirstSnapshot->uninit();
8825 mData->mFirstSnapshot.setNull();
8826 }
8827
8828 mData->mCurrentSnapshot.setNull();
8829 }
8830
8831 /* free data structures (the essential mData structure is not freed here
8832 * since it may be still in use) */
8833 mMediumAttachments.free();
8834 mStorageControllers.free();
8835 mUSBControllers.free();
8836 mHWData.free();
8837 mUserData.free();
8838 mSSData.free();
8839}
8840
8841/**
8842 * Returns a pointer to the Machine object for this machine that acts like a
8843 * parent for complex machine data objects such as shared folders, etc.
8844 *
8845 * For primary Machine objects and for SnapshotMachine objects, returns this
8846 * object's pointer itself. For SessionMachine objects, returns the peer
8847 * (primary) machine pointer.
8848 */
8849Machine *Machine::i_getMachine()
8850{
8851 if (i_isSessionMachine())
8852 return (Machine*)mPeer;
8853 return this;
8854}
8855
8856/**
8857 * Makes sure that there are no machine state dependents. If necessary, waits
8858 * for the number of dependents to drop to zero.
8859 *
8860 * Make sure this method is called from under this object's write lock to
8861 * guarantee that no new dependents may be added when this method returns
8862 * control to the caller.
8863 *
8864 * @note Receives a lock to this object for writing. The lock will be released
8865 * while waiting (if necessary).
8866 *
8867 * @warning To be used only in methods that change the machine state!
8868 */
8869void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8870{
8871 AssertReturnVoid(isWriteLockOnCurrentThread());
8872
8873 /* Wait for all state dependents if necessary */
8874 if (mData->mMachineStateDeps != 0)
8875 {
8876 /* lazy semaphore creation */
8877 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8878 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8879
8880 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8881 mData->mMachineStateDeps));
8882
8883 ++mData->mMachineStateChangePending;
8884
8885 /* reset the semaphore before waiting, the last dependent will signal
8886 * it */
8887 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8888
8889 alock.release();
8890
8891 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8892
8893 alock.acquire();
8894
8895 -- mData->mMachineStateChangePending;
8896 }
8897}
8898
8899/**
8900 * Changes the machine state and informs callbacks.
8901 *
8902 * This method is not intended to fail so it either returns S_OK or asserts (and
8903 * returns a failure).
8904 *
8905 * @note Locks this object for writing.
8906 */
8907HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8908{
8909 LogFlowThisFuncEnter();
8910 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8911 Assert(aMachineState != MachineState_Null);
8912
8913 AutoCaller autoCaller(this);
8914 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8915
8916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8917
8918 /* wait for state dependents to drop to zero */
8919 i_ensureNoStateDependencies(alock);
8920
8921 MachineState_T const enmOldState = mData->mMachineState;
8922 if (enmOldState != aMachineState)
8923 {
8924 mData->mMachineState = aMachineState;
8925 RTTimeNow(&mData->mLastStateChange);
8926
8927#ifdef VBOX_WITH_DTRACE_R3_MAIN
8928 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8929#endif
8930 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8931 }
8932
8933 LogFlowThisFuncLeave();
8934 return S_OK;
8935}
8936
8937/**
8938 * Searches for a shared folder with the given logical name
8939 * in the collection of shared folders.
8940 *
8941 * @param aName logical name of the shared folder
8942 * @param aSharedFolder where to return the found object
8943 * @param aSetError whether to set the error info if the folder is
8944 * not found
8945 * @return
8946 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8947 *
8948 * @note
8949 * must be called from under the object's lock!
8950 */
8951HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8952 ComObjPtr<SharedFolder> &aSharedFolder,
8953 bool aSetError /* = false */)
8954{
8955 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8956 for (HWData::SharedFolderList::const_iterator
8957 it = mHWData->mSharedFolders.begin();
8958 it != mHWData->mSharedFolders.end();
8959 ++it)
8960 {
8961 SharedFolder *pSF = *it;
8962 AutoCaller autoCaller(pSF);
8963 if (pSF->i_getName() == aName)
8964 {
8965 aSharedFolder = pSF;
8966 rc = S_OK;
8967 break;
8968 }
8969 }
8970
8971 if (aSetError && FAILED(rc))
8972 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8973
8974 return rc;
8975}
8976
8977/**
8978 * Initializes all machine instance data from the given settings structures
8979 * from XML. The exception is the machine UUID which needs special handling
8980 * depending on the caller's use case, so the caller needs to set that herself.
8981 *
8982 * This gets called in several contexts during machine initialization:
8983 *
8984 * -- When machine XML exists on disk already and needs to be loaded into memory,
8985 * for example, from #i_registeredInit() to load all registered machines on
8986 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8987 * attached to the machine should be part of some media registry already.
8988 *
8989 * -- During OVF import, when a machine config has been constructed from an
8990 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8991 * ensure that the media listed as attachments in the config (which have
8992 * been imported from the OVF) receive the correct registry ID.
8993 *
8994 * -- During VM cloning.
8995 *
8996 * @param config Machine settings from XML.
8997 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8998 * for each attached medium in the config.
8999 * @return
9000 */
9001HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
9002 const Guid *puuidRegistry)
9003{
9004 // copy name, description, OS type, teleporter, UTC etc.
9005 mUserData->s = config.machineUserData;
9006
9007 // look up the object by Id to check it is valid
9008 ComObjPtr<GuestOSType> pGuestOSType;
9009 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9010 if (!pGuestOSType.isNull())
9011 mUserData->s.strOsType = pGuestOSType->i_id();
9012
9013#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9014 // stateFile encryption (optional)
9015 mSSData->strStateKeyId = config.strStateKeyId;
9016 mSSData->strStateKeyStore = config.strStateKeyStore;
9017 mData->mstrLogKeyId = config.strLogKeyId;
9018 mData->mstrLogKeyStore = config.strLogKeyStore;
9019#endif
9020
9021 // stateFile (optional)
9022 if (config.strStateFile.isEmpty())
9023 mSSData->strStateFilePath.setNull();
9024 else
9025 {
9026 Utf8Str stateFilePathFull(config.strStateFile);
9027 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
9028 if (RT_FAILURE(vrc))
9029 return setErrorBoth(E_FAIL, vrc,
9030 tr("Invalid saved state file path '%s' (%Rrc)"),
9031 config.strStateFile.c_str(),
9032 vrc);
9033 mSSData->strStateFilePath = stateFilePathFull;
9034 }
9035
9036 // snapshot folder needs special processing so set it again
9037 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
9038 if (FAILED(rc)) return rc;
9039
9040 /* Copy the extra data items (config may or may not be the same as
9041 * mData->pMachineConfigFile) if necessary. When loading the XML files
9042 * from disk they are the same, but not for OVF import. */
9043 if (mData->pMachineConfigFile != &config)
9044 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
9045
9046 /* currentStateModified (optional, default is true) */
9047 mData->mCurrentStateModified = config.fCurrentStateModified;
9048
9049 mData->mLastStateChange = config.timeLastStateChange;
9050
9051 /*
9052 * note: all mUserData members must be assigned prior this point because
9053 * we need to commit changes in order to let mUserData be shared by all
9054 * snapshot machine instances.
9055 */
9056 mUserData.commitCopy();
9057
9058 // machine registry, if present (must be loaded before snapshots)
9059 if (config.canHaveOwnMediaRegistry())
9060 {
9061 // determine machine folder
9062 Utf8Str strMachineFolder = i_getSettingsFileFull();
9063 strMachineFolder.stripFilename();
9064 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
9065 config.mediaRegistry,
9066 strMachineFolder);
9067 if (FAILED(rc)) return rc;
9068 }
9069
9070 /* Snapshot node (optional) */
9071 size_t cRootSnapshots;
9072 if ((cRootSnapshots = config.llFirstSnapshot.size()))
9073 {
9074 // there must be only one root snapshot
9075 Assert(cRootSnapshots == 1);
9076 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9077
9078 rc = i_loadSnapshot(snap,
9079 config.uuidCurrentSnapshot);
9080 if (FAILED(rc)) return rc;
9081 }
9082
9083 // hardware data
9084 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
9085 if (FAILED(rc)) return rc;
9086
9087 /*
9088 * NOTE: the assignment below must be the last thing to do,
9089 * otherwise it will be not possible to change the settings
9090 * somewhere in the code above because all setters will be
9091 * blocked by i_checkStateDependency(MutableStateDep).
9092 */
9093
9094 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
9095 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
9096 {
9097 /* no need to use i_setMachineState() during init() */
9098 mData->mMachineState = MachineState_AbortedSaved;
9099 }
9100 else if (config.fAborted)
9101 {
9102 mSSData->strStateFilePath.setNull();
9103
9104 /* no need to use i_setMachineState() during init() */
9105 mData->mMachineState = MachineState_Aborted;
9106 }
9107 else if (!mSSData->strStateFilePath.isEmpty())
9108 {
9109 /* no need to use i_setMachineState() during init() */
9110 mData->mMachineState = MachineState_Saved;
9111 }
9112
9113 // after loading settings, we are no longer different from the XML on disk
9114 mData->flModifications = 0;
9115
9116 return S_OK;
9117}
9118
9119/**
9120 * Loads all snapshots starting from the given settings.
9121 *
9122 * @param data snapshot settings.
9123 * @param aCurSnapshotId Current snapshot ID from the settings file.
9124 */
9125HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
9126 const Guid &aCurSnapshotId)
9127{
9128 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
9129 AssertReturn(!i_isSessionMachine(), E_FAIL);
9130
9131 HRESULT rc = S_OK;
9132
9133 std::list<const settings::Snapshot *> llSettingsTodo;
9134 llSettingsTodo.push_back(&data);
9135 std::list<Snapshot *> llParentsTodo;
9136 llParentsTodo.push_back(NULL);
9137
9138 while (llSettingsTodo.size() > 0)
9139 {
9140 const settings::Snapshot *current = llSettingsTodo.front();
9141 llSettingsTodo.pop_front();
9142 Snapshot *pParent = llParentsTodo.front();
9143 llParentsTodo.pop_front();
9144
9145 Utf8Str strStateFile;
9146 if (!current->strStateFile.isEmpty())
9147 {
9148 /* optional */
9149 strStateFile = current->strStateFile;
9150 int vrc = i_calculateFullPath(strStateFile, strStateFile);
9151 if (RT_FAILURE(vrc))
9152 {
9153 setErrorBoth(E_FAIL, vrc,
9154 tr("Invalid saved state file path '%s' (%Rrc)"),
9155 strStateFile.c_str(), vrc);
9156 }
9157 }
9158
9159 /* create a snapshot machine object */
9160 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9161 pSnapshotMachine.createObject();
9162 rc = pSnapshotMachine->initFromSettings(this,
9163 current->hardware,
9164 &current->debugging,
9165 &current->autostart,
9166 current->uuid.ref(),
9167 strStateFile);
9168 if (FAILED(rc)) break;
9169
9170 /* create a snapshot object */
9171 ComObjPtr<Snapshot> pSnapshot;
9172 pSnapshot.createObject();
9173 /* initialize the snapshot */
9174 rc = pSnapshot->init(mParent, // VirtualBox object
9175 current->uuid,
9176 current->strName,
9177 current->strDescription,
9178 current->timestamp,
9179 pSnapshotMachine,
9180 pParent);
9181 if (FAILED(rc)) break;
9182
9183 /* memorize the first snapshot if necessary */
9184 if (!mData->mFirstSnapshot)
9185 {
9186 Assert(pParent == NULL);
9187 mData->mFirstSnapshot = pSnapshot;
9188 }
9189
9190 /* memorize the current snapshot when appropriate */
9191 if ( !mData->mCurrentSnapshot
9192 && pSnapshot->i_getId() == aCurSnapshotId
9193 )
9194 mData->mCurrentSnapshot = pSnapshot;
9195
9196 /* create all children */
9197 std::list<settings::Snapshot>::const_iterator itBegin = current->llChildSnapshots.begin();
9198 std::list<settings::Snapshot>::const_iterator itEnd = current->llChildSnapshots.end();
9199 for (std::list<settings::Snapshot>::const_iterator it = itBegin; it != itEnd; ++it)
9200 {
9201 llSettingsTodo.push_back(&*it);
9202 llParentsTodo.push_back(pSnapshot);
9203 }
9204 }
9205
9206 return rc;
9207}
9208
9209/**
9210 * Loads settings into mHWData.
9211 *
9212 * @param puuidRegistry Registry ID.
9213 * @param puuidSnapshot Snapshot ID
9214 * @param data Reference to the hardware settings.
9215 * @param pDbg Pointer to the debugging settings.
9216 * @param pAutostart Pointer to the autostart settings.
9217 */
9218HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
9219 const Guid *puuidSnapshot,
9220 const settings::Hardware &data,
9221 const settings::Debugging *pDbg,
9222 const settings::Autostart *pAutostart)
9223{
9224 AssertReturn(!i_isSessionMachine(), E_FAIL);
9225
9226 HRESULT rc = S_OK;
9227
9228 try
9229 {
9230 ComObjPtr<GuestOSType> pGuestOSType;
9231 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9232
9233 /* The hardware version attribute (optional). */
9234 mHWData->mHWVersion = data.strVersion;
9235 mHWData->mHardwareUUID = data.uuid;
9236
9237 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9238 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9239 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9240 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9241 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9242 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9243 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
9244 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
9245 mHWData->mPAEEnabled = data.fPAE;
9246 mHWData->mLongMode = data.enmLongMode;
9247 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9248 mHWData->mAPIC = data.fAPIC;
9249 mHWData->mX2APIC = data.fX2APIC;
9250 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9251 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9252 mHWData->mSpecCtrl = data.fSpecCtrl;
9253 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
9254 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
9255 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
9256 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
9257 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
9258 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9259 mHWData->mCPUCount = data.cCPUs;
9260 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9261 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9262 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9263 mHWData->mCpuProfile = data.strCpuProfile;
9264
9265 // cpu
9266 if (mHWData->mCPUHotPlugEnabled)
9267 {
9268 for (settings::CpuList::const_iterator
9269 it = data.llCpus.begin();
9270 it != data.llCpus.end();
9271 ++it)
9272 {
9273 const settings::Cpu &cpu = *it;
9274
9275 mHWData->mCPUAttached[cpu.ulId] = true;
9276 }
9277 }
9278
9279 // cpuid leafs
9280 for (settings::CpuIdLeafsList::const_iterator
9281 it = data.llCpuIdLeafs.begin();
9282 it != data.llCpuIdLeafs.end();
9283 ++it)
9284 {
9285 const settings::CpuIdLeaf &rLeaf= *it;
9286 if ( rLeaf.idx < UINT32_C(0x20)
9287 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9288 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9289 mHWData->mCpuIdLeafList.push_back(rLeaf);
9290 /* else: just ignore */
9291 }
9292
9293 mHWData->mMemorySize = data.ulMemorySizeMB;
9294 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9295
9296 // boot order
9297 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9298 {
9299 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9300 if (it == data.mapBootOrder.end())
9301 mHWData->mBootOrder[i] = DeviceType_Null;
9302 else
9303 mHWData->mBootOrder[i] = it->second;
9304 }
9305
9306 mHWData->mFirmwareType = data.firmwareType;
9307 mHWData->mPointingHIDType = data.pointingHIDType;
9308 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9309 mHWData->mChipsetType = data.chipsetType;
9310 mHWData->mIommuType = data.iommuType;
9311 mHWData->mParavirtProvider = data.paravirtProvider;
9312 mHWData->mParavirtDebug = data.strParavirtDebug;
9313 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9314 mHWData->mHPETEnabled = data.fHPETEnabled;
9315
9316 /* GraphicsAdapter */
9317 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
9318 if (FAILED(rc)) return rc;
9319
9320 /* VRDEServer */
9321 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9322 if (FAILED(rc)) return rc;
9323
9324 /* BIOS */
9325 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9326 if (FAILED(rc)) return rc;
9327
9328 /* Trusted Platform Module */
9329 rc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
9330 if (FAILED(rc)) return rc;
9331
9332 rc = mNvramStore->i_loadSettings(data.nvramSettings);
9333 if (FAILED(rc)) return rc;
9334
9335 /* Recording settings */
9336 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
9337 if (FAILED(rc)) return rc;
9338
9339 // Bandwidth control (must come before network adapters)
9340 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9341 if (FAILED(rc)) return rc;
9342
9343 /* USB controllers */
9344 for (settings::USBControllerList::const_iterator
9345 it = data.usbSettings.llUSBControllers.begin();
9346 it != data.usbSettings.llUSBControllers.end();
9347 ++it)
9348 {
9349 const settings::USBController &settingsCtrl = *it;
9350 ComObjPtr<USBController> newCtrl;
9351
9352 newCtrl.createObject();
9353 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9354 mUSBControllers->push_back(newCtrl);
9355 }
9356
9357 /* USB device filters */
9358 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9359 if (FAILED(rc)) return rc;
9360
9361 // network adapters (establish array size first and apply defaults, to
9362 // ensure reading the same settings as we saved, since the list skips
9363 // adapters having defaults)
9364 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9365 size_t oldCount = mNetworkAdapters.size();
9366 if (newCount > oldCount)
9367 {
9368 mNetworkAdapters.resize(newCount);
9369 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9370 {
9371 unconst(mNetworkAdapters[slot]).createObject();
9372 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9373 }
9374 }
9375 else if (newCount < oldCount)
9376 mNetworkAdapters.resize(newCount);
9377 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9378 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9379 for (settings::NetworkAdaptersList::const_iterator
9380 it = data.llNetworkAdapters.begin();
9381 it != data.llNetworkAdapters.end();
9382 ++it)
9383 {
9384 const settings::NetworkAdapter &nic = *it;
9385
9386 /* slot uniqueness is guaranteed by XML Schema */
9387 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9388 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9389 if (FAILED(rc)) return rc;
9390 }
9391
9392 // serial ports (establish defaults first, to ensure reading the same
9393 // settings as we saved, since the list skips ports having defaults)
9394 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9395 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9396 for (settings::SerialPortsList::const_iterator
9397 it = data.llSerialPorts.begin();
9398 it != data.llSerialPorts.end();
9399 ++it)
9400 {
9401 const settings::SerialPort &s = *it;
9402
9403 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9404 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9405 if (FAILED(rc)) return rc;
9406 }
9407
9408 // parallel ports (establish defaults first, to ensure reading the same
9409 // settings as we saved, since the list skips ports having defaults)
9410 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9411 mParallelPorts[i]->i_applyDefaults();
9412 for (settings::ParallelPortsList::const_iterator
9413 it = data.llParallelPorts.begin();
9414 it != data.llParallelPorts.end();
9415 ++it)
9416 {
9417 const settings::ParallelPort &p = *it;
9418
9419 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9420 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9421 if (FAILED(rc)) return rc;
9422 }
9423
9424 /* Audio settings */
9425 rc = mAudioSettings->i_loadSettings(data.audioAdapter);
9426 if (FAILED(rc)) return rc;
9427
9428 /* storage controllers */
9429 rc = i_loadStorageControllers(data.storage,
9430 puuidRegistry,
9431 puuidSnapshot);
9432 if (FAILED(rc)) return rc;
9433
9434 /* Shared folders */
9435 for (settings::SharedFoldersList::const_iterator
9436 it = data.llSharedFolders.begin();
9437 it != data.llSharedFolders.end();
9438 ++it)
9439 {
9440 const settings::SharedFolder &sf = *it;
9441
9442 ComObjPtr<SharedFolder> sharedFolder;
9443 /* Check for double entries. Not allowed! */
9444 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9445 if (SUCCEEDED(rc))
9446 return setError(VBOX_E_OBJECT_IN_USE,
9447 tr("Shared folder named '%s' already exists"),
9448 sf.strName.c_str());
9449
9450 /* Create the new shared folder. Don't break on error. This will be
9451 * reported when the machine starts. */
9452 sharedFolder.createObject();
9453 rc = sharedFolder->init(i_getMachine(),
9454 sf.strName,
9455 sf.strHostPath,
9456 RT_BOOL(sf.fWritable),
9457 RT_BOOL(sf.fAutoMount),
9458 sf.strAutoMountPoint,
9459 false /* fFailOnError */);
9460 if (FAILED(rc)) return rc;
9461 mHWData->mSharedFolders.push_back(sharedFolder);
9462 }
9463
9464 // Clipboard
9465 mHWData->mClipboardMode = data.clipboardMode;
9466 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9467
9468 // drag'n'drop
9469 mHWData->mDnDMode = data.dndMode;
9470
9471 // guest settings
9472 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9473
9474 // IO settings
9475 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9476 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9477
9478 // Host PCI devices
9479 for (settings::HostPCIDeviceAttachmentList::const_iterator
9480 it = data.pciAttachments.begin();
9481 it != data.pciAttachments.end();
9482 ++it)
9483 {
9484 const settings::HostPCIDeviceAttachment &hpda = *it;
9485 ComObjPtr<PCIDeviceAttachment> pda;
9486
9487 pda.createObject();
9488 pda->i_loadSettings(this, hpda);
9489 mHWData->mPCIDeviceAssignments.push_back(pda);
9490 }
9491
9492 /*
9493 * (The following isn't really real hardware, but it lives in HWData
9494 * for reasons of convenience.)
9495 */
9496
9497#ifdef VBOX_WITH_GUEST_PROPS
9498 /* Guest properties (optional) */
9499
9500 /* Only load transient guest properties for configs which have saved
9501 * state, because there shouldn't be any for powered off VMs. The same
9502 * logic applies for snapshots, as offline snapshots shouldn't have
9503 * any such properties. They confuse the code in various places.
9504 * Note: can't rely on the machine state, as it isn't set yet. */
9505 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9506 /* apologies for the hacky unconst() usage, but this needs hacking
9507 * actually inconsistent settings into consistency, otherwise there
9508 * will be some corner cases where the inconsistency survives
9509 * surprisingly long without getting fixed, especially for snapshots
9510 * as there are no config changes. */
9511 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9512 for (settings::GuestPropertiesList::iterator
9513 it = llGuestProperties.begin();
9514 it != llGuestProperties.end();
9515 /*nothing*/)
9516 {
9517 const settings::GuestProperty &prop = *it;
9518 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9519 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9520 if ( fSkipTransientGuestProperties
9521 && ( fFlags & GUEST_PROP_F_TRANSIENT
9522 || fFlags & GUEST_PROP_F_TRANSRESET))
9523 {
9524 it = llGuestProperties.erase(it);
9525 continue;
9526 }
9527 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9528 mHWData->mGuestProperties[prop.strName] = property;
9529 ++it;
9530 }
9531#endif /* VBOX_WITH_GUEST_PROPS defined */
9532
9533 rc = i_loadDebugging(pDbg);
9534 if (FAILED(rc))
9535 return rc;
9536
9537 mHWData->mAutostart = *pAutostart;
9538
9539 /* default frontend */
9540 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9541 }
9542 catch (std::bad_alloc &)
9543 {
9544 return E_OUTOFMEMORY;
9545 }
9546
9547 AssertComRC(rc);
9548 return rc;
9549}
9550
9551/**
9552 * Called from i_loadHardware() to load the debugging settings of the
9553 * machine.
9554 *
9555 * @param pDbg Pointer to the settings.
9556 */
9557HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9558{
9559 mHWData->mDebugging = *pDbg;
9560 /* no more processing currently required, this will probably change. */
9561 return S_OK;
9562}
9563
9564/**
9565 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9566 *
9567 * @param data storage settings.
9568 * @param puuidRegistry media registry ID to set media to or NULL;
9569 * see Machine::i_loadMachineDataFromSettings()
9570 * @param puuidSnapshot snapshot ID
9571 * @return
9572 */
9573HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9574 const Guid *puuidRegistry,
9575 const Guid *puuidSnapshot)
9576{
9577 AssertReturn(!i_isSessionMachine(), E_FAIL);
9578
9579 HRESULT rc = S_OK;
9580
9581 for (settings::StorageControllersList::const_iterator
9582 it = data.llStorageControllers.begin();
9583 it != data.llStorageControllers.end();
9584 ++it)
9585 {
9586 const settings::StorageController &ctlData = *it;
9587
9588 ComObjPtr<StorageController> pCtl;
9589 /* Try to find one with the name first. */
9590 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9591 if (SUCCEEDED(rc))
9592 return setError(VBOX_E_OBJECT_IN_USE,
9593 tr("Storage controller named '%s' already exists"),
9594 ctlData.strName.c_str());
9595
9596 pCtl.createObject();
9597 rc = pCtl->init(this,
9598 ctlData.strName,
9599 ctlData.storageBus,
9600 ctlData.ulInstance,
9601 ctlData.fBootable);
9602 if (FAILED(rc)) return rc;
9603
9604 mStorageControllers->push_back(pCtl);
9605
9606 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9607 if (FAILED(rc)) return rc;
9608
9609 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9610 if (FAILED(rc)) return rc;
9611
9612 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9613 if (FAILED(rc)) return rc;
9614
9615 /* Load the attached devices now. */
9616 rc = i_loadStorageDevices(pCtl,
9617 ctlData,
9618 puuidRegistry,
9619 puuidSnapshot);
9620 if (FAILED(rc)) return rc;
9621 }
9622
9623 return S_OK;
9624}
9625
9626/**
9627 * Called from i_loadStorageControllers for a controller's devices.
9628 *
9629 * @param aStorageController
9630 * @param data
9631 * @param puuidRegistry media registry ID to set media to or NULL; see
9632 * Machine::i_loadMachineDataFromSettings()
9633 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9634 * @return
9635 */
9636HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9637 const settings::StorageController &data,
9638 const Guid *puuidRegistry,
9639 const Guid *puuidSnapshot)
9640{
9641 HRESULT rc = S_OK;
9642
9643 /* paranoia: detect duplicate attachments */
9644 for (settings::AttachedDevicesList::const_iterator
9645 it = data.llAttachedDevices.begin();
9646 it != data.llAttachedDevices.end();
9647 ++it)
9648 {
9649 const settings::AttachedDevice &ad = *it;
9650
9651 for (settings::AttachedDevicesList::const_iterator it2 = it;
9652 it2 != data.llAttachedDevices.end();
9653 ++it2)
9654 {
9655 if (it == it2)
9656 continue;
9657
9658 const settings::AttachedDevice &ad2 = *it2;
9659
9660 if ( ad.lPort == ad2.lPort
9661 && ad.lDevice == ad2.lDevice)
9662 {
9663 return setError(E_FAIL,
9664 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9665 aStorageController->i_getName().c_str(),
9666 ad.lPort,
9667 ad.lDevice,
9668 mUserData->s.strName.c_str());
9669 }
9670 }
9671 }
9672
9673 for (settings::AttachedDevicesList::const_iterator
9674 it = data.llAttachedDevices.begin();
9675 it != data.llAttachedDevices.end();
9676 ++it)
9677 {
9678 const settings::AttachedDevice &dev = *it;
9679 ComObjPtr<Medium> medium;
9680
9681 switch (dev.deviceType)
9682 {
9683 case DeviceType_Floppy:
9684 case DeviceType_DVD:
9685 if (dev.strHostDriveSrc.isNotEmpty())
9686 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9687 false /* fRefresh */, medium);
9688 else
9689 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9690 dev.uuid,
9691 false /* fRefresh */,
9692 false /* aSetError */,
9693 medium);
9694 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9695 // This is not an error. The host drive or UUID might have vanished, so just go
9696 // ahead without this removeable medium attachment
9697 rc = S_OK;
9698 break;
9699
9700 case DeviceType_HardDisk:
9701 {
9702 /* find a hard disk by UUID */
9703 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9704 if (FAILED(rc))
9705 {
9706 if (i_isSnapshotMachine())
9707 {
9708 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9709 // so the user knows that the bad disk is in a snapshot somewhere
9710 com::ErrorInfo info;
9711 return setError(E_FAIL,
9712 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9713 puuidSnapshot->raw(),
9714 info.getText().raw());
9715 }
9716 else
9717 return rc;
9718 }
9719
9720 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9721
9722 if (medium->i_getType() == MediumType_Immutable)
9723 {
9724 if (i_isSnapshotMachine())
9725 return setError(E_FAIL,
9726 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9727 "of the virtual machine '%s' ('%s')"),
9728 medium->i_getLocationFull().c_str(),
9729 dev.uuid.raw(),
9730 puuidSnapshot->raw(),
9731 mUserData->s.strName.c_str(),
9732 mData->m_strConfigFileFull.c_str());
9733
9734 return setError(E_FAIL,
9735 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9736 medium->i_getLocationFull().c_str(),
9737 dev.uuid.raw(),
9738 mUserData->s.strName.c_str(),
9739 mData->m_strConfigFileFull.c_str());
9740 }
9741
9742 if (medium->i_getType() == MediumType_MultiAttach)
9743 {
9744 if (i_isSnapshotMachine())
9745 return setError(E_FAIL,
9746 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9747 "of the virtual machine '%s' ('%s')"),
9748 medium->i_getLocationFull().c_str(),
9749 dev.uuid.raw(),
9750 puuidSnapshot->raw(),
9751 mUserData->s.strName.c_str(),
9752 mData->m_strConfigFileFull.c_str());
9753
9754 return setError(E_FAIL,
9755 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9756 medium->i_getLocationFull().c_str(),
9757 dev.uuid.raw(),
9758 mUserData->s.strName.c_str(),
9759 mData->m_strConfigFileFull.c_str());
9760 }
9761
9762 if ( !i_isSnapshotMachine()
9763 && medium->i_getChildren().size() != 0
9764 )
9765 return setError(E_FAIL,
9766 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9767 "because it has %d differencing child hard disks"),
9768 medium->i_getLocationFull().c_str(),
9769 dev.uuid.raw(),
9770 mUserData->s.strName.c_str(),
9771 mData->m_strConfigFileFull.c_str(),
9772 medium->i_getChildren().size());
9773
9774 if (i_findAttachment(*mMediumAttachments.data(),
9775 medium))
9776 return setError(E_FAIL,
9777 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9778 medium->i_getLocationFull().c_str(),
9779 dev.uuid.raw(),
9780 mUserData->s.strName.c_str(),
9781 mData->m_strConfigFileFull.c_str());
9782
9783 break;
9784 }
9785
9786 default:
9787 return setError(E_FAIL,
9788 tr("Controller '%s' port %u unit %u has device with unknown type (%d) - virtual machine '%s' ('%s')"),
9789 data.strName.c_str(), dev.lPort, dev.lDevice, dev.deviceType,
9790 mUserData->s.strName.c_str(), mData->m_strConfigFileFull.c_str());
9791 }
9792
9793 if (FAILED(rc))
9794 break;
9795
9796 /* Bandwidth groups are loaded at this point. */
9797 ComObjPtr<BandwidthGroup> pBwGroup;
9798
9799 if (!dev.strBwGroup.isEmpty())
9800 {
9801 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9802 if (FAILED(rc))
9803 return setError(E_FAIL,
9804 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9805 medium->i_getLocationFull().c_str(),
9806 dev.strBwGroup.c_str(),
9807 mUserData->s.strName.c_str(),
9808 mData->m_strConfigFileFull.c_str());
9809 pBwGroup->i_reference();
9810 }
9811
9812 const Utf8Str controllerName = aStorageController->i_getName();
9813 ComObjPtr<MediumAttachment> pAttachment;
9814 pAttachment.createObject();
9815 rc = pAttachment->init(this,
9816 medium,
9817 controllerName,
9818 dev.lPort,
9819 dev.lDevice,
9820 dev.deviceType,
9821 false,
9822 dev.fPassThrough,
9823 dev.fTempEject,
9824 dev.fNonRotational,
9825 dev.fDiscard,
9826 dev.fHotPluggable,
9827 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9828 if (FAILED(rc)) break;
9829
9830 /* associate the medium with this machine and snapshot */
9831 if (!medium.isNull())
9832 {
9833 AutoCaller medCaller(medium);
9834 if (FAILED(medCaller.rc())) return medCaller.rc();
9835 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9836
9837 if (i_isSnapshotMachine())
9838 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9839 else
9840 rc = medium->i_addBackReference(mData->mUuid);
9841 /* If the medium->addBackReference fails it sets an appropriate
9842 * error message, so no need to do any guesswork here. */
9843
9844 if (puuidRegistry)
9845 // caller wants registry ID to be set on all attached media (OVF import case)
9846 medium->i_addRegistry(*puuidRegistry);
9847 }
9848
9849 if (FAILED(rc))
9850 break;
9851
9852 /* back up mMediumAttachments to let registeredInit() properly rollback
9853 * on failure (= limited accessibility) */
9854 i_setModified(IsModified_Storage);
9855 mMediumAttachments.backup();
9856 mMediumAttachments->push_back(pAttachment);
9857 }
9858
9859 return rc;
9860}
9861
9862/**
9863 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9864 *
9865 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9866 * @param aSnapshot where to return the found snapshot
9867 * @param aSetError true to set extended error info on failure
9868 */
9869HRESULT Machine::i_findSnapshotById(const Guid &aId,
9870 ComObjPtr<Snapshot> &aSnapshot,
9871 bool aSetError /* = false */)
9872{
9873 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9874
9875 if (!mData->mFirstSnapshot)
9876 {
9877 if (aSetError)
9878 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9879 return E_FAIL;
9880 }
9881
9882 if (aId.isZero())
9883 aSnapshot = mData->mFirstSnapshot;
9884 else
9885 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9886
9887 if (!aSnapshot)
9888 {
9889 if (aSetError)
9890 return setError(E_FAIL,
9891 tr("Could not find a snapshot with UUID {%s}"),
9892 aId.toString().c_str());
9893 return E_FAIL;
9894 }
9895
9896 return S_OK;
9897}
9898
9899/**
9900 * Returns the snapshot with the given name or fails of no such snapshot.
9901 *
9902 * @param strName snapshot name to find
9903 * @param aSnapshot where to return the found snapshot
9904 * @param aSetError true to set extended error info on failure
9905 */
9906HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9907 ComObjPtr<Snapshot> &aSnapshot,
9908 bool aSetError /* = false */)
9909{
9910 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9911
9912 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9913
9914 if (!mData->mFirstSnapshot)
9915 {
9916 if (aSetError)
9917 return setError(VBOX_E_OBJECT_NOT_FOUND,
9918 tr("This machine does not have any snapshots"));
9919 return VBOX_E_OBJECT_NOT_FOUND;
9920 }
9921
9922 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9923
9924 if (!aSnapshot)
9925 {
9926 if (aSetError)
9927 return setError(VBOX_E_OBJECT_NOT_FOUND,
9928 tr("Could not find a snapshot named '%s'"), strName.c_str());
9929 return VBOX_E_OBJECT_NOT_FOUND;
9930 }
9931
9932 return S_OK;
9933}
9934
9935/**
9936 * Returns a storage controller object with the given name.
9937 *
9938 * @param aName storage controller name to find
9939 * @param aStorageController where to return the found storage controller
9940 * @param aSetError true to set extended error info on failure
9941 */
9942HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9943 ComObjPtr<StorageController> &aStorageController,
9944 bool aSetError /* = false */)
9945{
9946 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9947
9948 for (StorageControllerList::const_iterator
9949 it = mStorageControllers->begin();
9950 it != mStorageControllers->end();
9951 ++it)
9952 {
9953 if ((*it)->i_getName() == aName)
9954 {
9955 aStorageController = (*it);
9956 return S_OK;
9957 }
9958 }
9959
9960 if (aSetError)
9961 return setError(VBOX_E_OBJECT_NOT_FOUND,
9962 tr("Could not find a storage controller named '%s'"),
9963 aName.c_str());
9964 return VBOX_E_OBJECT_NOT_FOUND;
9965}
9966
9967/**
9968 * Returns a USB controller object with the given name.
9969 *
9970 * @param aName USB controller name to find
9971 * @param aUSBController where to return the found USB controller
9972 * @param aSetError true to set extended error info on failure
9973 */
9974HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9975 ComObjPtr<USBController> &aUSBController,
9976 bool aSetError /* = false */)
9977{
9978 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9979
9980 for (USBControllerList::const_iterator
9981 it = mUSBControllers->begin();
9982 it != mUSBControllers->end();
9983 ++it)
9984 {
9985 if ((*it)->i_getName() == aName)
9986 {
9987 aUSBController = (*it);
9988 return S_OK;
9989 }
9990 }
9991
9992 if (aSetError)
9993 return setError(VBOX_E_OBJECT_NOT_FOUND,
9994 tr("Could not find a storage controller named '%s'"),
9995 aName.c_str());
9996 return VBOX_E_OBJECT_NOT_FOUND;
9997}
9998
9999/**
10000 * Returns the number of USB controller instance of the given type.
10001 *
10002 * @param enmType USB controller type.
10003 */
10004ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
10005{
10006 ULONG cCtrls = 0;
10007
10008 for (USBControllerList::const_iterator
10009 it = mUSBControllers->begin();
10010 it != mUSBControllers->end();
10011 ++it)
10012 {
10013 if ((*it)->i_getControllerType() == enmType)
10014 cCtrls++;
10015 }
10016
10017 return cCtrls;
10018}
10019
10020HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
10021 MediumAttachmentList &atts)
10022{
10023 AutoCaller autoCaller(this);
10024 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10025
10026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10027
10028 for (MediumAttachmentList::const_iterator
10029 it = mMediumAttachments->begin();
10030 it != mMediumAttachments->end();
10031 ++it)
10032 {
10033 const ComObjPtr<MediumAttachment> &pAtt = *it;
10034 // should never happen, but deal with NULL pointers in the list.
10035 AssertContinue(!pAtt.isNull());
10036
10037 // getControllerName() needs caller+read lock
10038 AutoCaller autoAttCaller(pAtt);
10039 if (FAILED(autoAttCaller.rc()))
10040 {
10041 atts.clear();
10042 return autoAttCaller.rc();
10043 }
10044 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
10045
10046 if (pAtt->i_getControllerName() == aName)
10047 atts.push_back(pAtt);
10048 }
10049
10050 return S_OK;
10051}
10052
10053
10054/**
10055 * Helper for #i_saveSettings. Cares about renaming the settings directory and
10056 * file if the machine name was changed and about creating a new settings file
10057 * if this is a new machine.
10058 *
10059 * @note Must be never called directly but only from #saveSettings().
10060 */
10061HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
10062 bool *pfSettingsFileIsNew)
10063{
10064 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10065
10066 HRESULT rc = S_OK;
10067
10068 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
10069 /// @todo need to handle primary group change, too
10070
10071 /* attempt to rename the settings file if machine name is changed */
10072 if ( mUserData->s.fNameSync
10073 && mUserData.isBackedUp()
10074 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
10075 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
10076 )
10077 {
10078 bool dirRenamed = false;
10079 bool fileRenamed = false;
10080
10081 Utf8Str configFile, newConfigFile;
10082 Utf8Str configFilePrev, newConfigFilePrev;
10083 Utf8Str NVRAMFile, newNVRAMFile;
10084 Utf8Str configDir, newConfigDir;
10085
10086 do
10087 {
10088 int vrc = VINF_SUCCESS;
10089
10090 Utf8Str name = mUserData.backedUpData()->s.strName;
10091 Utf8Str newName = mUserData->s.strName;
10092 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
10093 if (group == "/")
10094 group.setNull();
10095 Utf8Str newGroup = mUserData->s.llGroups.front();
10096 if (newGroup == "/")
10097 newGroup.setNull();
10098
10099 configFile = mData->m_strConfigFileFull;
10100
10101 /* first, rename the directory if it matches the group and machine name */
10102 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
10103 /** @todo hack, make somehow use of ComposeMachineFilename */
10104 if (mUserData->s.fDirectoryIncludesUUID)
10105 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10106 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
10107 /** @todo hack, make somehow use of ComposeMachineFilename */
10108 if (mUserData->s.fDirectoryIncludesUUID)
10109 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10110 configDir = configFile;
10111 configDir.stripFilename();
10112 newConfigDir = configDir;
10113 if ( configDir.length() >= groupPlusName.length()
10114 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
10115 groupPlusName.c_str()))
10116 {
10117 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10118 Utf8Str newConfigBaseDir(newConfigDir);
10119 newConfigDir.append(newGroupPlusName);
10120 /* consistency: use \ if appropriate on the platform */
10121 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10122 /* new dir and old dir cannot be equal here because of 'if'
10123 * above and because name != newName */
10124 Assert(configDir != newConfigDir);
10125 if (!fSettingsFileIsNew)
10126 {
10127 /* perform real rename only if the machine is not new */
10128 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10129 if ( vrc == VERR_FILE_NOT_FOUND
10130 || vrc == VERR_PATH_NOT_FOUND)
10131 {
10132 /* create the parent directory, then retry renaming */
10133 Utf8Str parent(newConfigDir);
10134 parent.stripFilename();
10135 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10136 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10137 }
10138 if (RT_FAILURE(vrc))
10139 {
10140 rc = setErrorBoth(E_FAIL, vrc,
10141 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10142 configDir.c_str(),
10143 newConfigDir.c_str(),
10144 vrc);
10145 break;
10146 }
10147 /* delete subdirectories which are no longer needed */
10148 Utf8Str dir(configDir);
10149 dir.stripFilename();
10150 while (dir != newConfigBaseDir && dir != ".")
10151 {
10152 vrc = RTDirRemove(dir.c_str());
10153 if (RT_FAILURE(vrc))
10154 break;
10155 dir.stripFilename();
10156 }
10157 dirRenamed = true;
10158 }
10159 }
10160
10161 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10162
10163 /* then try to rename the settings file itself */
10164 if (newConfigFile != configFile)
10165 {
10166 /* get the path to old settings file in renamed directory */
10167 Assert(mData->m_strConfigFileFull == configFile);
10168 configFile.printf("%s%c%s",
10169 newConfigDir.c_str(),
10170 RTPATH_DELIMITER,
10171 RTPathFilename(mData->m_strConfigFileFull.c_str()));
10172 if (!fSettingsFileIsNew)
10173 {
10174 /* perform real rename only if the machine is not new */
10175 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10176 if (RT_FAILURE(vrc))
10177 {
10178 rc = setErrorBoth(E_FAIL, vrc,
10179 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10180 configFile.c_str(),
10181 newConfigFile.c_str(),
10182 vrc);
10183 break;
10184 }
10185 fileRenamed = true;
10186 configFilePrev = configFile;
10187 configFilePrev += "-prev";
10188 newConfigFilePrev = newConfigFile;
10189 newConfigFilePrev += "-prev";
10190 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10191 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
10192 if (NVRAMFile.isNotEmpty())
10193 {
10194 // in the NVRAM file path, replace the old directory with the new directory
10195 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
10196 {
10197 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
10198 NVRAMFile = newConfigDir + strNVRAMFile;
10199 }
10200 newNVRAMFile = newConfigFile;
10201 newNVRAMFile.stripSuffix();
10202 newNVRAMFile += ".nvram";
10203 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
10204 }
10205 }
10206 }
10207
10208 // update m_strConfigFileFull amd mConfigFile
10209 mData->m_strConfigFileFull = newConfigFile;
10210 // compute the relative path too
10211 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10212
10213 // store the old and new so that VirtualBox::i_saveSettings() can update
10214 // the media registry
10215 if ( mData->mRegistered
10216 && (configDir != newConfigDir || configFile != newConfigFile))
10217 {
10218 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10219
10220 if (pfNeedsGlobalSaveSettings)
10221 *pfNeedsGlobalSaveSettings = true;
10222 }
10223
10224 // in the saved state file path, replace the old directory with the new directory
10225 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10226 {
10227 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
10228 mSSData->strStateFilePath = newConfigDir + strStateFileName;
10229 }
10230 if (newNVRAMFile.isNotEmpty())
10231 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
10232
10233 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
10234 if (mData->mFirstSnapshot)
10235 {
10236 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10237 newConfigDir.c_str());
10238 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
10239 newConfigDir.c_str());
10240 }
10241 }
10242 while (0);
10243
10244 if (FAILED(rc))
10245 {
10246 /* silently try to rename everything back */
10247 if (fileRenamed)
10248 {
10249 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10250 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10251 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
10252 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
10253 }
10254 if (dirRenamed)
10255 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10256 }
10257
10258 if (FAILED(rc)) return rc;
10259 }
10260
10261 if (fSettingsFileIsNew)
10262 {
10263 /* create a virgin config file */
10264 int vrc = VINF_SUCCESS;
10265
10266 /* ensure the settings directory exists */
10267 Utf8Str path(mData->m_strConfigFileFull);
10268 path.stripFilename();
10269 if (!RTDirExists(path.c_str()))
10270 {
10271 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10272 if (RT_FAILURE(vrc))
10273 {
10274 return setErrorBoth(E_FAIL, vrc,
10275 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10276 path.c_str(),
10277 vrc);
10278 }
10279 }
10280
10281 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10282 path = mData->m_strConfigFileFull;
10283 RTFILE f = NIL_RTFILE;
10284 vrc = RTFileOpen(&f, path.c_str(),
10285 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10286 if (RT_FAILURE(vrc))
10287 return setErrorBoth(E_FAIL, vrc,
10288 tr("Could not create the settings file '%s' (%Rrc)"),
10289 path.c_str(),
10290 vrc);
10291 RTFileClose(f);
10292 }
10293 if (pfSettingsFileIsNew)
10294 *pfSettingsFileIsNew = fSettingsFileIsNew;
10295
10296 return rc;
10297}
10298
10299/**
10300 * Saves and commits machine data, user data and hardware data.
10301 *
10302 * Note that on failure, the data remains uncommitted.
10303 *
10304 * @a aFlags may combine the following flags:
10305 *
10306 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10307 * Used when saving settings after an operation that makes them 100%
10308 * correspond to the settings from the current snapshot.
10309 * - SaveS_Force: settings will be saved without doing a deep compare of the
10310 * settings structures. This is used when this is called because snapshots
10311 * have changed to avoid the overhead of the deep compare.
10312 *
10313 * @note Must be called from under this object's write lock. Locks children for
10314 * writing.
10315 *
10316 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10317 * initialized to false and that will be set to true by this function if
10318 * the caller must invoke VirtualBox::i_saveSettings() because the global
10319 * settings have changed. This will happen if a machine rename has been
10320 * saved and the global machine and media registries will therefore need
10321 * updating.
10322 * @param alock Reference to the lock for this machine object.
10323 * @param aFlags Flags.
10324 */
10325HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10326 AutoWriteLock &alock,
10327 int aFlags /*= 0*/)
10328{
10329 LogFlowThisFuncEnter();
10330
10331 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10332
10333 /* make sure child objects are unable to modify the settings while we are
10334 * saving them */
10335 i_ensureNoStateDependencies(alock);
10336
10337 AssertReturn(!i_isSnapshotMachine(),
10338 E_FAIL);
10339
10340 if (!mData->mAccessible)
10341 return setError(VBOX_E_INVALID_VM_STATE,
10342 tr("The machine is not accessible, so cannot save settings"));
10343
10344 HRESULT rc = S_OK;
10345 PCVBOXCRYPTOIF pCryptoIf = NULL;
10346 const char *pszPassword = NULL;
10347 SecretKey *pKey = NULL;
10348
10349#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10350 if (mData->mstrKeyId.isNotEmpty())
10351 {
10352 /* VM is going to be encrypted. */
10353 alock.release(); /** @todo Revise the locking. */
10354 rc = mParent->i_retainCryptoIf(&pCryptoIf);
10355 alock.acquire();
10356 if (FAILED(rc)) return rc; /* Error is set. */
10357
10358 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
10359 if (RT_SUCCESS(vrc))
10360 pszPassword = (const char *)pKey->getKeyBuffer();
10361 else
10362 {
10363 mParent->i_releaseCryptoIf(pCryptoIf);
10364 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
10365 tr("Failed to retain VM encryption password using ID '%s' with %Rrc"),
10366 mData->mstrKeyId.c_str(), vrc);
10367 }
10368 }
10369#else
10370 RT_NOREF(pKey);
10371#endif
10372
10373 bool fNeedsWrite = false;
10374 bool fSettingsFileIsNew = false;
10375
10376 /* First, prepare to save settings. It will care about renaming the
10377 * settings directory and file if the machine name was changed and about
10378 * creating a new settings file if this is a new machine. */
10379 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings,
10380 &fSettingsFileIsNew);
10381 if (FAILED(rc))
10382 {
10383#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10384 if (pCryptoIf)
10385 {
10386 alock.release(); /** @todo Revise the locking. */
10387 mParent->i_releaseCryptoIf(pCryptoIf);
10388 alock.acquire();
10389 }
10390 if (pKey)
10391 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10392#endif
10393 return rc;
10394 }
10395
10396 // keep a pointer to the current settings structures
10397 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10398 settings::MachineConfigFile *pNewConfig = NULL;
10399
10400 try
10401 {
10402 // make a fresh one to have everyone write stuff into
10403 pNewConfig = new settings::MachineConfigFile(NULL);
10404 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10405#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10406 pNewConfig->strKeyId = mData->mstrKeyId;
10407 pNewConfig->strKeyStore = mData->mstrKeyStore;
10408#endif
10409
10410 // now go and copy all the settings data from COM to the settings structures
10411 // (this calls i_saveSettings() on all the COM objects in the machine)
10412 i_copyMachineDataToSettings(*pNewConfig);
10413
10414 if (aFlags & SaveS_ResetCurStateModified)
10415 {
10416 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10417 mData->mCurrentStateModified = FALSE;
10418 fNeedsWrite = true; // always, no need to compare
10419 }
10420 else if (aFlags & SaveS_Force)
10421 {
10422 fNeedsWrite = true; // always, no need to compare
10423 }
10424 else
10425 {
10426 if (!mData->mCurrentStateModified)
10427 {
10428 // do a deep compare of the settings that we just saved with the settings
10429 // previously stored in the config file; this invokes MachineConfigFile::operator==
10430 // which does a deep compare of all the settings, which is expensive but less expensive
10431 // than writing out XML in vain
10432 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10433
10434 // could still be modified if any settings changed
10435 mData->mCurrentStateModified = fAnySettingsChanged;
10436
10437 fNeedsWrite = fAnySettingsChanged;
10438 }
10439 else
10440 fNeedsWrite = true;
10441 }
10442
10443 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10444
10445 if (fNeedsWrite)
10446 {
10447 // now spit it all out!
10448 pNewConfig->write(mData->m_strConfigFileFull, pCryptoIf, pszPassword);
10449 if (aFlags & SaveS_RemoveBackup)
10450 i_deleteFile(mData->m_strConfigFileFull + "-prev", true /* fIgnoreFailures */);
10451 }
10452
10453 mData->pMachineConfigFile = pNewConfig;
10454 delete pOldConfig;
10455 i_commit();
10456
10457 // after saving settings, we are no longer different from the XML on disk
10458 mData->flModifications = 0;
10459 }
10460 catch (HRESULT err)
10461 {
10462 // we assume that error info is set by the thrower
10463 rc = err;
10464
10465 // delete any newly created settings file
10466 if (fSettingsFileIsNew)
10467 i_deleteFile(mData->m_strConfigFileFull, true /* fIgnoreFailures */);
10468
10469 // restore old config
10470 delete pNewConfig;
10471 mData->pMachineConfigFile = pOldConfig;
10472 }
10473 catch (...)
10474 {
10475 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10476 }
10477
10478#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10479 if (pCryptoIf)
10480 {
10481 alock.release(); /** @todo Revise the locking. */
10482 mParent->i_releaseCryptoIf(pCryptoIf);
10483 alock.acquire();
10484 }
10485 if (pKey)
10486 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10487#endif
10488
10489 if (fNeedsWrite)
10490 {
10491 /* Fire the data change event, even on failure (since we've already
10492 * committed all data). This is done only for SessionMachines because
10493 * mutable Machine instances are always not registered (i.e. private
10494 * to the client process that creates them) and thus don't need to
10495 * inform callbacks. */
10496 if (i_isSessionMachine())
10497 mParent->i_onMachineDataChanged(mData->mUuid);
10498 }
10499
10500 LogFlowThisFunc(("rc=%08X\n", rc));
10501 LogFlowThisFuncLeave();
10502 return rc;
10503}
10504
10505/**
10506 * Implementation for saving the machine settings into the given
10507 * settings::MachineConfigFile instance. This copies machine extradata
10508 * from the previous machine config file in the instance data, if any.
10509 *
10510 * This gets called from two locations:
10511 *
10512 * -- Machine::i_saveSettings(), during the regular XML writing;
10513 *
10514 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10515 * exported to OVF and we write the VirtualBox proprietary XML
10516 * into a <vbox:Machine> tag.
10517 *
10518 * This routine fills all the fields in there, including snapshots, *except*
10519 * for the following:
10520 *
10521 * -- fCurrentStateModified. There is some special logic associated with that.
10522 *
10523 * The caller can then call MachineConfigFile::write() or do something else
10524 * with it.
10525 *
10526 * Caller must hold the machine lock!
10527 *
10528 * This throws XML errors and HRESULT, so the caller must have a catch block!
10529 */
10530void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10531{
10532 // deep copy extradata, being extra careful with self assignment (the STL
10533 // map assignment on Mac OS X clang based Xcode isn't checking)
10534 if (&config != mData->pMachineConfigFile)
10535 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10536
10537 config.uuid = mData->mUuid;
10538
10539 // copy name, description, OS type, teleport, UTC etc.
10540 config.machineUserData = mUserData->s;
10541
10542#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10543 config.strStateKeyId = mSSData->strStateKeyId;
10544 config.strStateKeyStore = mSSData->strStateKeyStore;
10545 config.strLogKeyId = mData->mstrLogKeyId;
10546 config.strLogKeyStore = mData->mstrLogKeyStore;
10547#endif
10548
10549 if ( mData->mMachineState == MachineState_Saved
10550 || mData->mMachineState == MachineState_AbortedSaved
10551 || mData->mMachineState == MachineState_Restoring
10552 // when doing certain snapshot operations we may or may not have
10553 // a saved state in the current state, so keep everything as is
10554 || ( ( mData->mMachineState == MachineState_Snapshotting
10555 || mData->mMachineState == MachineState_DeletingSnapshot
10556 || mData->mMachineState == MachineState_RestoringSnapshot)
10557 && (!mSSData->strStateFilePath.isEmpty())
10558 )
10559 )
10560 {
10561 Assert(!mSSData->strStateFilePath.isEmpty());
10562 /* try to make the file name relative to the settings file dir */
10563 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10564 }
10565 else
10566 {
10567 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10568 config.strStateFile.setNull();
10569 }
10570
10571 if (mData->mCurrentSnapshot)
10572 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10573 else
10574 config.uuidCurrentSnapshot.clear();
10575
10576 config.timeLastStateChange = mData->mLastStateChange;
10577 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10578 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10579
10580 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10581 if (FAILED(rc)) throw rc;
10582
10583 // save machine's media registry if this is VirtualBox 4.0 or later
10584 if (config.canHaveOwnMediaRegistry())
10585 {
10586 // determine machine folder
10587 Utf8Str strMachineFolder = i_getSettingsFileFull();
10588 strMachineFolder.stripFilename();
10589 mParent->i_saveMediaRegistry(config.mediaRegistry,
10590 i_getId(), // only media with registry ID == machine UUID
10591 strMachineFolder);
10592 // this throws HRESULT
10593 }
10594
10595 // save snapshots
10596 rc = i_saveAllSnapshots(config);
10597 if (FAILED(rc)) throw rc;
10598}
10599
10600/**
10601 * Saves all snapshots of the machine into the given machine config file. Called
10602 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10603 * @param config
10604 * @return
10605 */
10606HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10607{
10608 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10609
10610 HRESULT rc = S_OK;
10611
10612 try
10613 {
10614 config.llFirstSnapshot.clear();
10615
10616 if (mData->mFirstSnapshot)
10617 {
10618 // the settings use a list for "the first snapshot"
10619 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10620
10621 // get reference to the snapshot on the list and work on that
10622 // element straight in the list to avoid excessive copying later
10623 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10624 if (FAILED(rc)) throw rc;
10625 }
10626
10627// if (mType == IsSessionMachine)
10628// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10629
10630 }
10631 catch (HRESULT err)
10632 {
10633 /* we assume that error info is set by the thrower */
10634 rc = err;
10635 }
10636 catch (...)
10637 {
10638 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10639 }
10640
10641 return rc;
10642}
10643
10644/**
10645 * Saves the VM hardware configuration. It is assumed that the
10646 * given node is empty.
10647 *
10648 * @param data Reference to the settings object for the hardware config.
10649 * @param pDbg Pointer to the settings object for the debugging config
10650 * which happens to live in mHWData.
10651 * @param pAutostart Pointer to the settings object for the autostart config
10652 * which happens to live in mHWData.
10653 */
10654HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10655 settings::Autostart *pAutostart)
10656{
10657 HRESULT rc = S_OK;
10658
10659 try
10660 {
10661 /* The hardware version attribute (optional).
10662 Automatically upgrade from 1 to current default hardware version
10663 when there is no saved state. (ugly!) */
10664 if ( mHWData->mHWVersion == "1"
10665 && mSSData->strStateFilePath.isEmpty()
10666 )
10667 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10668
10669 data.strVersion = mHWData->mHWVersion;
10670 data.uuid = mHWData->mHardwareUUID;
10671
10672 // CPU
10673 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10674 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10675 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10676 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10677 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10678 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10679 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10680 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10681 data.fPAE = !!mHWData->mPAEEnabled;
10682 data.enmLongMode = mHWData->mLongMode;
10683 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10684 data.fAPIC = !!mHWData->mAPIC;
10685 data.fX2APIC = !!mHWData->mX2APIC;
10686 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10687 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10688 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10689 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10690 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10691 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10692 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10693 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10694 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10695 data.cCPUs = mHWData->mCPUCount;
10696 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10697 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10698 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10699 data.strCpuProfile = mHWData->mCpuProfile;
10700
10701 data.llCpus.clear();
10702 if (data.fCpuHotPlug)
10703 {
10704 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10705 {
10706 if (mHWData->mCPUAttached[idx])
10707 {
10708 settings::Cpu cpu;
10709 cpu.ulId = idx;
10710 data.llCpus.push_back(cpu);
10711 }
10712 }
10713 }
10714
10715 /* Standard and Extended CPUID leafs. */
10716 data.llCpuIdLeafs.clear();
10717 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10718
10719 // memory
10720 data.ulMemorySizeMB = mHWData->mMemorySize;
10721 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10722
10723 // firmware
10724 data.firmwareType = mHWData->mFirmwareType;
10725
10726 // HID
10727 data.pointingHIDType = mHWData->mPointingHIDType;
10728 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10729
10730 // chipset
10731 data.chipsetType = mHWData->mChipsetType;
10732
10733 // iommu
10734 data.iommuType = mHWData->mIommuType;
10735
10736 // paravirt
10737 data.paravirtProvider = mHWData->mParavirtProvider;
10738 data.strParavirtDebug = mHWData->mParavirtDebug;
10739
10740 // emulated USB card reader
10741 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10742
10743 // HPET
10744 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10745
10746 // boot order
10747 data.mapBootOrder.clear();
10748 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10749 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10750
10751 /* VRDEServer settings (optional) */
10752 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10753 if (FAILED(rc)) throw rc;
10754
10755 /* BIOS settings (required) */
10756 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10757 if (FAILED(rc)) throw rc;
10758
10759 /* Trusted Platform Module settings (required) */
10760 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10761 if (FAILED(rc)) throw rc;
10762
10763 /* NVRAM settings (required) */
10764 rc = mNvramStore->i_saveSettings(data.nvramSettings);
10765 if (FAILED(rc)) throw rc;
10766
10767 /* Recording settings (required) */
10768 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10769 if (FAILED(rc)) throw rc;
10770
10771 /* GraphicsAdapter settings (required) */
10772 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10773 if (FAILED(rc)) throw rc;
10774
10775 /* USB Controller (required) */
10776 data.usbSettings.llUSBControllers.clear();
10777 for (USBControllerList::const_iterator
10778 it = mUSBControllers->begin();
10779 it != mUSBControllers->end();
10780 ++it)
10781 {
10782 ComObjPtr<USBController> ctrl = *it;
10783 settings::USBController settingsCtrl;
10784
10785 settingsCtrl.strName = ctrl->i_getName();
10786 settingsCtrl.enmType = ctrl->i_getControllerType();
10787
10788 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10789 }
10790
10791 /* USB device filters (required) */
10792 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10793 if (FAILED(rc)) throw rc;
10794
10795 /* Network adapters (required) */
10796 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10797 data.llNetworkAdapters.clear();
10798 /* Write out only the nominal number of network adapters for this
10799 * chipset type. Since Machine::commit() hasn't been called there
10800 * may be extra NIC settings in the vector. */
10801 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10802 {
10803 settings::NetworkAdapter nic;
10804 nic.ulSlot = (uint32_t)slot;
10805 /* paranoia check... must not be NULL, but must not crash either. */
10806 if (mNetworkAdapters[slot])
10807 {
10808 if (mNetworkAdapters[slot]->i_hasDefaults())
10809 continue;
10810
10811 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10812 if (FAILED(rc)) throw rc;
10813
10814 data.llNetworkAdapters.push_back(nic);
10815 }
10816 }
10817
10818 /* Serial ports */
10819 data.llSerialPorts.clear();
10820 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10821 {
10822 if (mSerialPorts[slot]->i_hasDefaults())
10823 continue;
10824
10825 settings::SerialPort s;
10826 s.ulSlot = slot;
10827 rc = mSerialPorts[slot]->i_saveSettings(s);
10828 if (FAILED(rc)) return rc;
10829
10830 data.llSerialPorts.push_back(s);
10831 }
10832
10833 /* Parallel ports */
10834 data.llParallelPorts.clear();
10835 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10836 {
10837 if (mParallelPorts[slot]->i_hasDefaults())
10838 continue;
10839
10840 settings::ParallelPort p;
10841 p.ulSlot = slot;
10842 rc = mParallelPorts[slot]->i_saveSettings(p);
10843 if (FAILED(rc)) return rc;
10844
10845 data.llParallelPorts.push_back(p);
10846 }
10847
10848 /* Audio settings */
10849 rc = mAudioSettings->i_saveSettings(data.audioAdapter);
10850 if (FAILED(rc)) return rc;
10851
10852 rc = i_saveStorageControllers(data.storage);
10853 if (FAILED(rc)) return rc;
10854
10855 /* Shared folders */
10856 data.llSharedFolders.clear();
10857 for (HWData::SharedFolderList::const_iterator
10858 it = mHWData->mSharedFolders.begin();
10859 it != mHWData->mSharedFolders.end();
10860 ++it)
10861 {
10862 SharedFolder *pSF = *it;
10863 AutoCaller sfCaller(pSF);
10864 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10865 settings::SharedFolder sf;
10866 sf.strName = pSF->i_getName();
10867 sf.strHostPath = pSF->i_getHostPath();
10868 sf.fWritable = !!pSF->i_isWritable();
10869 sf.fAutoMount = !!pSF->i_isAutoMounted();
10870 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10871
10872 data.llSharedFolders.push_back(sf);
10873 }
10874
10875 // clipboard
10876 data.clipboardMode = mHWData->mClipboardMode;
10877 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10878
10879 // drag'n'drop
10880 data.dndMode = mHWData->mDnDMode;
10881
10882 /* Guest */
10883 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10884
10885 // IO settings
10886 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10887 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10888
10889 /* BandwidthControl (required) */
10890 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10891 if (FAILED(rc)) throw rc;
10892
10893 /* Host PCI devices */
10894 data.pciAttachments.clear();
10895 for (HWData::PCIDeviceAssignmentList::const_iterator
10896 it = mHWData->mPCIDeviceAssignments.begin();
10897 it != mHWData->mPCIDeviceAssignments.end();
10898 ++it)
10899 {
10900 ComObjPtr<PCIDeviceAttachment> pda = *it;
10901 settings::HostPCIDeviceAttachment hpda;
10902
10903 rc = pda->i_saveSettings(hpda);
10904 if (FAILED(rc)) throw rc;
10905
10906 data.pciAttachments.push_back(hpda);
10907 }
10908
10909 // guest properties
10910 data.llGuestProperties.clear();
10911#ifdef VBOX_WITH_GUEST_PROPS
10912 for (HWData::GuestPropertyMap::const_iterator
10913 it = mHWData->mGuestProperties.begin();
10914 it != mHWData->mGuestProperties.end();
10915 ++it)
10916 {
10917 HWData::GuestProperty property = it->second;
10918
10919 /* Remove transient guest properties at shutdown unless we
10920 * are saving state. Note that restoring snapshot intentionally
10921 * keeps them, they will be removed if appropriate once the final
10922 * machine state is set (as crashes etc. need to work). */
10923 if ( ( mData->mMachineState == MachineState_PoweredOff
10924 || mData->mMachineState == MachineState_Aborted
10925 || mData->mMachineState == MachineState_Teleported)
10926 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10927 continue;
10928 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10929 prop.strName = it->first;
10930 prop.strValue = property.strValue;
10931 prop.timestamp = (uint64_t)property.mTimestamp;
10932 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10933 GuestPropWriteFlags(property.mFlags, szFlags);
10934 prop.strFlags = szFlags;
10935
10936 data.llGuestProperties.push_back(prop);
10937 }
10938
10939 /* I presume this doesn't require a backup(). */
10940 mData->mGuestPropertiesModified = FALSE;
10941#endif /* VBOX_WITH_GUEST_PROPS defined */
10942
10943 *pDbg = mHWData->mDebugging;
10944 *pAutostart = mHWData->mAutostart;
10945
10946 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10947 }
10948 catch (std::bad_alloc &)
10949 {
10950 return E_OUTOFMEMORY;
10951 }
10952
10953 AssertComRC(rc);
10954 return rc;
10955}
10956
10957/**
10958 * Saves the storage controller configuration.
10959 *
10960 * @param data storage settings.
10961 */
10962HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10963{
10964 data.llStorageControllers.clear();
10965
10966 for (StorageControllerList::const_iterator
10967 it = mStorageControllers->begin();
10968 it != mStorageControllers->end();
10969 ++it)
10970 {
10971 HRESULT rc;
10972 ComObjPtr<StorageController> pCtl = *it;
10973
10974 settings::StorageController ctl;
10975 ctl.strName = pCtl->i_getName();
10976 ctl.controllerType = pCtl->i_getControllerType();
10977 ctl.storageBus = pCtl->i_getStorageBus();
10978 ctl.ulInstance = pCtl->i_getInstance();
10979 ctl.fBootable = pCtl->i_getBootable();
10980
10981 /* Save the port count. */
10982 ULONG portCount;
10983 rc = pCtl->COMGETTER(PortCount)(&portCount);
10984 ComAssertComRCRet(rc, rc);
10985 ctl.ulPortCount = portCount;
10986
10987 /* Save fUseHostIOCache */
10988 BOOL fUseHostIOCache;
10989 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10990 ComAssertComRCRet(rc, rc);
10991 ctl.fUseHostIOCache = !!fUseHostIOCache;
10992
10993 /* save the devices now. */
10994 rc = i_saveStorageDevices(pCtl, ctl);
10995 ComAssertComRCRet(rc, rc);
10996
10997 data.llStorageControllers.push_back(ctl);
10998 }
10999
11000 return S_OK;
11001}
11002
11003/**
11004 * Saves the hard disk configuration.
11005 */
11006HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
11007 settings::StorageController &data)
11008{
11009 MediumAttachmentList atts;
11010
11011 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
11012 if (FAILED(rc)) return rc;
11013
11014 data.llAttachedDevices.clear();
11015 for (MediumAttachmentList::const_iterator
11016 it = atts.begin();
11017 it != atts.end();
11018 ++it)
11019 {
11020 settings::AttachedDevice dev;
11021 IMediumAttachment *iA = *it;
11022 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
11023 Medium *pMedium = pAttach->i_getMedium();
11024
11025 dev.deviceType = pAttach->i_getType();
11026 dev.lPort = pAttach->i_getPort();
11027 dev.lDevice = pAttach->i_getDevice();
11028 dev.fPassThrough = pAttach->i_getPassthrough();
11029 dev.fHotPluggable = pAttach->i_getHotPluggable();
11030 if (pMedium)
11031 {
11032 if (pMedium->i_isHostDrive())
11033 dev.strHostDriveSrc = pMedium->i_getLocationFull();
11034 else
11035 dev.uuid = pMedium->i_getId();
11036 dev.fTempEject = pAttach->i_getTempEject();
11037 dev.fNonRotational = pAttach->i_getNonRotational();
11038 dev.fDiscard = pAttach->i_getDiscard();
11039 }
11040
11041 dev.strBwGroup = pAttach->i_getBandwidthGroup();
11042
11043 data.llAttachedDevices.push_back(dev);
11044 }
11045
11046 return S_OK;
11047}
11048
11049/**
11050 * Saves machine state settings as defined by aFlags
11051 * (SaveSTS_* values).
11052 *
11053 * @param aFlags Combination of SaveSTS_* flags.
11054 *
11055 * @note Locks objects for writing.
11056 */
11057HRESULT Machine::i_saveStateSettings(int aFlags)
11058{
11059 if (aFlags == 0)
11060 return S_OK;
11061
11062 AutoCaller autoCaller(this);
11063 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11064
11065 /* This object's write lock is also necessary to serialize file access
11066 * (prevent concurrent reads and writes) */
11067 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11068
11069 HRESULT rc = S_OK;
11070
11071 Assert(mData->pMachineConfigFile);
11072
11073 try
11074 {
11075 if (aFlags & SaveSTS_CurStateModified)
11076 mData->pMachineConfigFile->fCurrentStateModified = true;
11077
11078 if (aFlags & SaveSTS_StateFilePath)
11079 {
11080 if (!mSSData->strStateFilePath.isEmpty())
11081 /* try to make the file name relative to the settings file dir */
11082 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
11083 else
11084 mData->pMachineConfigFile->strStateFile.setNull();
11085 }
11086
11087 if (aFlags & SaveSTS_StateTimeStamp)
11088 {
11089 Assert( mData->mMachineState != MachineState_Aborted
11090 || mSSData->strStateFilePath.isEmpty());
11091
11092 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
11093
11094 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
11095 || mData->mMachineState == MachineState_AbortedSaved);
11096/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
11097 }
11098
11099 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
11100 }
11101 catch (...)
11102 {
11103 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
11104 }
11105
11106 return rc;
11107}
11108
11109/**
11110 * Ensures that the given medium is added to a media registry. If this machine
11111 * was created with 4.0 or later, then the machine registry is used. Otherwise
11112 * the global VirtualBox media registry is used.
11113 *
11114 * Caller must NOT hold machine lock, media tree or any medium locks!
11115 *
11116 * @param pMedium
11117 */
11118void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
11119{
11120 /* Paranoia checks: do not hold machine or media tree locks. */
11121 AssertReturnVoid(!isWriteLockOnCurrentThread());
11122 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
11123
11124 ComObjPtr<Medium> pBase;
11125 {
11126 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11127 pBase = pMedium->i_getBase();
11128 }
11129
11130 /* Paranoia checks: do not hold medium locks. */
11131 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
11132 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
11133
11134 // decide which medium registry to use now that the medium is attached:
11135 Guid uuid;
11136 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
11137 if (fCanHaveOwnMediaRegistry)
11138 // machine XML is VirtualBox 4.0 or higher:
11139 uuid = i_getId(); // machine UUID
11140 else
11141 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
11142
11143 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
11144 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11145 if (pMedium->i_addRegistry(uuid))
11146 mParent->i_markRegistryModified(uuid);
11147
11148 /* For more complex hard disk structures it can happen that the base
11149 * medium isn't yet associated with any medium registry. Do that now. */
11150 if (pMedium != pBase)
11151 {
11152 /* Tree lock needed by Medium::addRegistryAll. */
11153 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11154 if (fCanHaveOwnMediaRegistry && pBase->i_removeRegistryAll(mParent->i_getGlobalRegistryId()))
11155 {
11156 treeLock.release();
11157 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11158 treeLock.acquire();
11159 }
11160 if (pBase->i_addRegistryAll(uuid))
11161 {
11162 treeLock.release();
11163 mParent->i_markRegistryModified(uuid);
11164 }
11165 }
11166}
11167
11168/**
11169 * Physically deletes a file belonging to a machine.
11170 *
11171 * @returns HRESULT
11172 * @retval VBOX_E_FILE_ERROR on failure.
11173 * @param strFile File to delete.
11174 * @param fIgnoreFailures Whether to ignore deletion failures. Defaults to \c false.
11175 * VERR_FILE_NOT_FOUND and VERR_PATH_NOT_FOUND always will be ignored.
11176 * @param strWhat File hint which will be used when setting an error. Optional.
11177 * @param prc Where to return IPRT's error code on failure. Optional and can be NULL.
11178 */
11179HRESULT Machine::i_deleteFile(const Utf8Str &strFile, bool fIgnoreFailures /* = false */,
11180 const Utf8Str &strWhat /* = "" */, int *prc /* = NULL */)
11181{
11182 AssertReturn(strFile.isNotEmpty(), E_INVALIDARG);
11183
11184 HRESULT hrc = S_OK;
11185
11186 LogFunc(("Deleting file '%s'\n", strFile.c_str()));
11187
11188 int vrc = RTFileDelete(strFile.c_str());
11189 if (RT_FAILURE(vrc))
11190 {
11191 if ( !fIgnoreFailures
11192 /* Don't (externally) bitch about stuff which doesn't exist. */
11193 && ( vrc != VERR_FILE_NOT_FOUND
11194 && vrc != VERR_PATH_NOT_FOUND
11195 )
11196 )
11197 {
11198 LogRel(("Deleting file '%s' failed: %Rrc\n", strFile.c_str(), vrc));
11199
11200 Utf8StrFmt strError("Error deleting %s '%s' (%Rrc)",
11201 strWhat.isEmpty() ? tr("file") : strWhat.c_str(), strFile.c_str(), vrc);
11202 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, strError.c_str(),
11203 strFile.c_str(), vrc);
11204 }
11205
11206 if (prc)
11207 *prc = vrc;
11208 }
11209
11210 return hrc;
11211}
11212
11213/**
11214 * Creates differencing hard disks for all normal hard disks attached to this
11215 * machine and a new set of attachments to refer to created disks.
11216 *
11217 * Used when taking a snapshot or when deleting the current state. Gets called
11218 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
11219 *
11220 * This method assumes that mMediumAttachments contains the original hard disk
11221 * attachments it needs to create diffs for. On success, these attachments will
11222 * be replaced with the created diffs.
11223 *
11224 * Attachments with non-normal hard disks are left as is.
11225 *
11226 * If @a aOnline is @c false then the original hard disks that require implicit
11227 * diffs will be locked for reading. Otherwise it is assumed that they are
11228 * already locked for writing (when the VM was started). Note that in the latter
11229 * case it is responsibility of the caller to lock the newly created diffs for
11230 * writing if this method succeeds.
11231 *
11232 * @param aProgress Progress object to run (must contain at least as
11233 * many operations left as the number of hard disks
11234 * attached).
11235 * @param aWeight Weight of this operation.
11236 * @param aOnline Whether the VM was online prior to this operation.
11237 *
11238 * @note The progress object is not marked as completed, neither on success nor
11239 * on failure. This is a responsibility of the caller.
11240 *
11241 * @note Locks this object and the media tree for writing.
11242 */
11243HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
11244 ULONG aWeight,
11245 bool aOnline)
11246{
11247 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11248
11249 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
11250 AssertReturn(!!pProgressControl, E_INVALIDARG);
11251
11252 AutoCaller autoCaller(this);
11253 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11254
11255 AutoMultiWriteLock2 alock(this->lockHandle(),
11256 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11257
11258 /* must be in a protective state because we release the lock below */
11259 AssertReturn( mData->mMachineState == MachineState_Snapshotting
11260 || mData->mMachineState == MachineState_OnlineSnapshotting
11261 || mData->mMachineState == MachineState_LiveSnapshotting
11262 || mData->mMachineState == MachineState_RestoringSnapshot
11263 || mData->mMachineState == MachineState_DeletingSnapshot
11264 , E_FAIL);
11265
11266 HRESULT rc = S_OK;
11267
11268 // use appropriate locked media map (online or offline)
11269 MediumLockListMap lockedMediaOffline;
11270 MediumLockListMap *lockedMediaMap;
11271 if (aOnline)
11272 lockedMediaMap = &mData->mSession.mLockedMedia;
11273 else
11274 lockedMediaMap = &lockedMediaOffline;
11275
11276 try
11277 {
11278 if (!aOnline)
11279 {
11280 /* lock all attached hard disks early to detect "in use"
11281 * situations before creating actual diffs */
11282 for (MediumAttachmentList::const_iterator
11283 it = mMediumAttachments->begin();
11284 it != mMediumAttachments->end();
11285 ++it)
11286 {
11287 MediumAttachment *pAtt = *it;
11288 if (pAtt->i_getType() == DeviceType_HardDisk)
11289 {
11290 Medium *pMedium = pAtt->i_getMedium();
11291 Assert(pMedium);
11292
11293 MediumLockList *pMediumLockList(new MediumLockList());
11294 alock.release();
11295 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11296 NULL /* pToLockWrite */,
11297 false /* fMediumLockWriteAll */,
11298 NULL,
11299 *pMediumLockList);
11300 alock.acquire();
11301 if (FAILED(rc))
11302 {
11303 delete pMediumLockList;
11304 throw rc;
11305 }
11306 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11307 if (FAILED(rc))
11308 {
11309 throw setError(rc,
11310 tr("Collecting locking information for all attached media failed"));
11311 }
11312 }
11313 }
11314
11315 /* Now lock all media. If this fails, nothing is locked. */
11316 alock.release();
11317 rc = lockedMediaMap->Lock();
11318 alock.acquire();
11319 if (FAILED(rc))
11320 {
11321 throw setError(rc,
11322 tr("Locking of attached media failed"));
11323 }
11324 }
11325
11326 /* remember the current list (note that we don't use backup() since
11327 * mMediumAttachments may be already backed up) */
11328 MediumAttachmentList atts = *mMediumAttachments.data();
11329
11330 /* start from scratch */
11331 mMediumAttachments->clear();
11332
11333 /* go through remembered attachments and create diffs for normal hard
11334 * disks and attach them */
11335 for (MediumAttachmentList::const_iterator
11336 it = atts.begin();
11337 it != atts.end();
11338 ++it)
11339 {
11340 MediumAttachment *pAtt = *it;
11341
11342 DeviceType_T devType = pAtt->i_getType();
11343 Medium *pMedium = pAtt->i_getMedium();
11344
11345 if ( devType != DeviceType_HardDisk
11346 || pMedium == NULL
11347 || pMedium->i_getType() != MediumType_Normal)
11348 {
11349 /* copy the attachment as is */
11350
11351 /** @todo the progress object created in SessionMachine::TakeSnaphot
11352 * only expects operations for hard disks. Later other
11353 * device types need to show up in the progress as well. */
11354 if (devType == DeviceType_HardDisk)
11355 {
11356 if (pMedium == NULL)
11357 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11358 aWeight); // weight
11359 else
11360 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11361 pMedium->i_getBase()->i_getName().c_str()).raw(),
11362 aWeight); // weight
11363 }
11364
11365 mMediumAttachments->push_back(pAtt);
11366 continue;
11367 }
11368
11369 /* need a diff */
11370 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11371 pMedium->i_getBase()->i_getName().c_str()).raw(),
11372 aWeight); // weight
11373
11374 Utf8Str strFullSnapshotFolder;
11375 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11376
11377 ComObjPtr<Medium> diff;
11378 diff.createObject();
11379 // store the diff in the same registry as the parent
11380 // (this cannot fail here because we can't create implicit diffs for
11381 // unregistered images)
11382 Guid uuidRegistryParent;
11383 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11384 Assert(fInRegistry); NOREF(fInRegistry);
11385 rc = diff->init(mParent,
11386 pMedium->i_getPreferredDiffFormat(),
11387 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11388 uuidRegistryParent,
11389 DeviceType_HardDisk);
11390 if (FAILED(rc)) throw rc;
11391
11392 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11393 * the push_back? Looks like we're going to release medium with the
11394 * wrong kind of lock (general issue with if we fail anywhere at all)
11395 * and an orphaned VDI in the snapshots folder. */
11396
11397 /* update the appropriate lock list */
11398 MediumLockList *pMediumLockList;
11399 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11400 AssertComRCThrowRC(rc);
11401 if (aOnline)
11402 {
11403 alock.release();
11404 /* The currently attached medium will be read-only, change
11405 * the lock type to read. */
11406 rc = pMediumLockList->Update(pMedium, false);
11407 alock.acquire();
11408 AssertComRCThrowRC(rc);
11409 }
11410
11411 /* release the locks before the potentially lengthy operation */
11412 alock.release();
11413 rc = pMedium->i_createDiffStorage(diff,
11414 pMedium->i_getPreferredDiffVariant(),
11415 pMediumLockList,
11416 NULL /* aProgress */,
11417 true /* aWait */,
11418 false /* aNotify */);
11419 alock.acquire();
11420 if (FAILED(rc)) throw rc;
11421
11422 /* actual lock list update is done in Machine::i_commitMedia */
11423
11424 rc = diff->i_addBackReference(mData->mUuid);
11425 AssertComRCThrowRC(rc);
11426
11427 /* add a new attachment */
11428 ComObjPtr<MediumAttachment> attachment;
11429 attachment.createObject();
11430 rc = attachment->init(this,
11431 diff,
11432 pAtt->i_getControllerName(),
11433 pAtt->i_getPort(),
11434 pAtt->i_getDevice(),
11435 DeviceType_HardDisk,
11436 true /* aImplicit */,
11437 false /* aPassthrough */,
11438 false /* aTempEject */,
11439 pAtt->i_getNonRotational(),
11440 pAtt->i_getDiscard(),
11441 pAtt->i_getHotPluggable(),
11442 pAtt->i_getBandwidthGroup());
11443 if (FAILED(rc)) throw rc;
11444
11445 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11446 AssertComRCThrowRC(rc);
11447 mMediumAttachments->push_back(attachment);
11448 }
11449 }
11450 catch (HRESULT aRC) { rc = aRC; }
11451
11452 /* unlock all hard disks we locked when there is no VM */
11453 if (!aOnline)
11454 {
11455 ErrorInfoKeeper eik;
11456
11457 HRESULT rc1 = lockedMediaMap->Clear();
11458 AssertComRC(rc1);
11459 }
11460
11461 return rc;
11462}
11463
11464/**
11465 * Deletes implicit differencing hard disks created either by
11466 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11467 * mMediumAttachments.
11468 *
11469 * Note that to delete hard disks created by #attachDevice() this method is
11470 * called from #i_rollbackMedia() when the changes are rolled back.
11471 *
11472 * @note Locks this object and the media tree for writing.
11473 */
11474HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11475{
11476 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11477
11478 AutoCaller autoCaller(this);
11479 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11480
11481 AutoMultiWriteLock2 alock(this->lockHandle(),
11482 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11483
11484 /* We absolutely must have backed up state. */
11485 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11486
11487 /* Check if there are any implicitly created diff images. */
11488 bool fImplicitDiffs = false;
11489 for (MediumAttachmentList::const_iterator
11490 it = mMediumAttachments->begin();
11491 it != mMediumAttachments->end();
11492 ++it)
11493 {
11494 const ComObjPtr<MediumAttachment> &pAtt = *it;
11495 if (pAtt->i_isImplicit())
11496 {
11497 fImplicitDiffs = true;
11498 break;
11499 }
11500 }
11501 /* If there is nothing to do, leave early. This saves lots of image locking
11502 * effort. It also avoids a MachineStateChanged event without real reason.
11503 * This is important e.g. when loading a VM config, because there should be
11504 * no events. Otherwise API clients can become thoroughly confused for
11505 * inaccessible VMs (the code for loading VM configs uses this method for
11506 * cleanup if the config makes no sense), as they take such events as an
11507 * indication that the VM is alive, and they would force the VM config to
11508 * be reread, leading to an endless loop. */
11509 if (!fImplicitDiffs)
11510 return S_OK;
11511
11512 HRESULT rc = S_OK;
11513 MachineState_T oldState = mData->mMachineState;
11514
11515 /* will release the lock before the potentially lengthy operation,
11516 * so protect with the special state (unless already protected) */
11517 if ( oldState != MachineState_Snapshotting
11518 && oldState != MachineState_OnlineSnapshotting
11519 && oldState != MachineState_LiveSnapshotting
11520 && oldState != MachineState_RestoringSnapshot
11521 && oldState != MachineState_DeletingSnapshot
11522 && oldState != MachineState_DeletingSnapshotOnline
11523 && oldState != MachineState_DeletingSnapshotPaused
11524 )
11525 i_setMachineState(MachineState_SettingUp);
11526
11527 // use appropriate locked media map (online or offline)
11528 MediumLockListMap lockedMediaOffline;
11529 MediumLockListMap *lockedMediaMap;
11530 if (aOnline)
11531 lockedMediaMap = &mData->mSession.mLockedMedia;
11532 else
11533 lockedMediaMap = &lockedMediaOffline;
11534
11535 try
11536 {
11537 if (!aOnline)
11538 {
11539 /* lock all attached hard disks early to detect "in use"
11540 * situations before deleting actual diffs */
11541 for (MediumAttachmentList::const_iterator
11542 it = mMediumAttachments->begin();
11543 it != mMediumAttachments->end();
11544 ++it)
11545 {
11546 MediumAttachment *pAtt = *it;
11547 if (pAtt->i_getType() == DeviceType_HardDisk)
11548 {
11549 Medium *pMedium = pAtt->i_getMedium();
11550 Assert(pMedium);
11551
11552 MediumLockList *pMediumLockList(new MediumLockList());
11553 alock.release();
11554 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11555 NULL /* pToLockWrite */,
11556 false /* fMediumLockWriteAll */,
11557 NULL,
11558 *pMediumLockList);
11559 alock.acquire();
11560
11561 if (FAILED(rc))
11562 {
11563 delete pMediumLockList;
11564 throw rc;
11565 }
11566
11567 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11568 if (FAILED(rc))
11569 throw rc;
11570 }
11571 }
11572
11573 if (FAILED(rc))
11574 throw rc;
11575 } // end of offline
11576
11577 /* Lock lists are now up to date and include implicitly created media */
11578
11579 /* Go through remembered attachments and delete all implicitly created
11580 * diffs and fix up the attachment information */
11581 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11582 MediumAttachmentList implicitAtts;
11583 for (MediumAttachmentList::const_iterator
11584 it = mMediumAttachments->begin();
11585 it != mMediumAttachments->end();
11586 ++it)
11587 {
11588 ComObjPtr<MediumAttachment> pAtt = *it;
11589 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11590 if (pMedium.isNull())
11591 continue;
11592
11593 // Implicit attachments go on the list for deletion and back references are removed.
11594 if (pAtt->i_isImplicit())
11595 {
11596 /* Deassociate and mark for deletion */
11597 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11598 rc = pMedium->i_removeBackReference(mData->mUuid);
11599 if (FAILED(rc))
11600 throw rc;
11601 implicitAtts.push_back(pAtt);
11602 continue;
11603 }
11604
11605 /* Was this medium attached before? */
11606 if (!i_findAttachment(oldAtts, pMedium))
11607 {
11608 /* no: de-associate */
11609 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11610 rc = pMedium->i_removeBackReference(mData->mUuid);
11611 if (FAILED(rc))
11612 throw rc;
11613 continue;
11614 }
11615 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11616 }
11617
11618 /* If there are implicit attachments to delete, throw away the lock
11619 * map contents (which will unlock all media) since the medium
11620 * attachments will be rolled back. Below we need to completely
11621 * recreate the lock map anyway since it is infinitely complex to
11622 * do this incrementally (would need reconstructing each attachment
11623 * change, which would be extremely hairy). */
11624 if (implicitAtts.size() != 0)
11625 {
11626 ErrorInfoKeeper eik;
11627
11628 HRESULT rc1 = lockedMediaMap->Clear();
11629 AssertComRC(rc1);
11630 }
11631
11632 /* rollback hard disk changes */
11633 mMediumAttachments.rollback();
11634
11635 MultiResult mrc(S_OK);
11636
11637 // Delete unused implicit diffs.
11638 if (implicitAtts.size() != 0)
11639 {
11640 alock.release();
11641
11642 for (MediumAttachmentList::const_iterator
11643 it = implicitAtts.begin();
11644 it != implicitAtts.end();
11645 ++it)
11646 {
11647 // Remove medium associated with this attachment.
11648 ComObjPtr<MediumAttachment> pAtt = *it;
11649 Assert(pAtt);
11650 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11651 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11652 Assert(pMedium);
11653
11654 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11655 // continue on delete failure, just collect error messages
11656 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11657 pMedium->i_getLocationFull().c_str() ));
11658 mrc = rc;
11659 }
11660 // Clear the list of deleted implicit attachments now, while not
11661 // holding the lock, as it will ultimately trigger Medium::uninit()
11662 // calls which assume that the media tree lock isn't held.
11663 implicitAtts.clear();
11664
11665 alock.acquire();
11666
11667 /* if there is a VM recreate media lock map as mentioned above,
11668 * otherwise it is a waste of time and we leave things unlocked */
11669 if (aOnline)
11670 {
11671 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11672 /* must never be NULL, but better safe than sorry */
11673 if (!pMachine.isNull())
11674 {
11675 alock.release();
11676 rc = mData->mSession.mMachine->i_lockMedia();
11677 alock.acquire();
11678 if (FAILED(rc))
11679 throw rc;
11680 }
11681 }
11682 }
11683 }
11684 catch (HRESULT aRC) {rc = aRC;}
11685
11686 if (mData->mMachineState == MachineState_SettingUp)
11687 i_setMachineState(oldState);
11688
11689 /* unlock all hard disks we locked when there is no VM */
11690 if (!aOnline)
11691 {
11692 ErrorInfoKeeper eik;
11693
11694 HRESULT rc1 = lockedMediaMap->Clear();
11695 AssertComRC(rc1);
11696 }
11697
11698 return rc;
11699}
11700
11701
11702/**
11703 * Looks through the given list of media attachments for one with the given parameters
11704 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11705 * can be searched as well if needed.
11706 *
11707 * @param ll
11708 * @param aControllerName
11709 * @param aControllerPort
11710 * @param aDevice
11711 * @return
11712 */
11713MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11714 const Utf8Str &aControllerName,
11715 LONG aControllerPort,
11716 LONG aDevice)
11717{
11718 for (MediumAttachmentList::const_iterator
11719 it = ll.begin();
11720 it != ll.end();
11721 ++it)
11722 {
11723 MediumAttachment *pAttach = *it;
11724 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11725 return pAttach;
11726 }
11727
11728 return NULL;
11729}
11730
11731/**
11732 * Looks through the given list of media attachments for one with the given parameters
11733 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11734 * can be searched as well if needed.
11735 *
11736 * @param ll
11737 * @param pMedium
11738 * @return
11739 */
11740MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11741 ComObjPtr<Medium> pMedium)
11742{
11743 for (MediumAttachmentList::const_iterator
11744 it = ll.begin();
11745 it != ll.end();
11746 ++it)
11747 {
11748 MediumAttachment *pAttach = *it;
11749 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11750 if (pMediumThis == pMedium)
11751 return pAttach;
11752 }
11753
11754 return NULL;
11755}
11756
11757/**
11758 * Looks through the given list of media attachments for one with the given parameters
11759 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11760 * can be searched as well if needed.
11761 *
11762 * @param ll
11763 * @param id
11764 * @return
11765 */
11766MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11767 Guid &id)
11768{
11769 for (MediumAttachmentList::const_iterator
11770 it = ll.begin();
11771 it != ll.end();
11772 ++it)
11773 {
11774 MediumAttachment *pAttach = *it;
11775 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11776 if (pMediumThis->i_getId() == id)
11777 return pAttach;
11778 }
11779
11780 return NULL;
11781}
11782
11783/**
11784 * Main implementation for Machine::DetachDevice. This also gets called
11785 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11786 *
11787 * @param pAttach Medium attachment to detach.
11788 * @param writeLock Machine write lock which the caller must have locked once.
11789 * This may be released temporarily in here.
11790 * @param pSnapshot If NULL, then the detachment is for the current machine.
11791 * Otherwise this is for a SnapshotMachine, and this must be
11792 * its snapshot.
11793 * @return
11794 */
11795HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11796 AutoWriteLock &writeLock,
11797 Snapshot *pSnapshot)
11798{
11799 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11800 DeviceType_T mediumType = pAttach->i_getType();
11801
11802 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11803
11804 if (pAttach->i_isImplicit())
11805 {
11806 /* attempt to implicitly delete the implicitly created diff */
11807
11808 /// @todo move the implicit flag from MediumAttachment to Medium
11809 /// and forbid any hard disk operation when it is implicit. Or maybe
11810 /// a special media state for it to make it even more simple.
11811
11812 Assert(mMediumAttachments.isBackedUp());
11813
11814 /* will release the lock before the potentially lengthy operation, so
11815 * protect with the special state */
11816 MachineState_T oldState = mData->mMachineState;
11817 i_setMachineState(MachineState_SettingUp);
11818
11819 writeLock.release();
11820
11821 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11822 true /*aWait*/,
11823 false /*aNotify*/);
11824
11825 writeLock.acquire();
11826
11827 i_setMachineState(oldState);
11828
11829 if (FAILED(rc)) return rc;
11830 }
11831
11832 i_setModified(IsModified_Storage);
11833 mMediumAttachments.backup();
11834 mMediumAttachments->remove(pAttach);
11835
11836 if (!oldmedium.isNull())
11837 {
11838 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11839 if (pSnapshot)
11840 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11841 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11842 else if (mediumType != DeviceType_HardDisk)
11843 oldmedium->i_removeBackReference(mData->mUuid);
11844 }
11845
11846 return S_OK;
11847}
11848
11849/**
11850 * Goes thru all media of the given list and
11851 *
11852 * 1) calls i_detachDevice() on each of them for this machine and
11853 * 2) adds all Medium objects found in the process to the given list,
11854 * depending on cleanupMode.
11855 *
11856 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11857 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11858 * media to the list.
11859 *
11860 * This gets called from Machine::Unregister, both for the actual Machine and
11861 * the SnapshotMachine objects that might be found in the snapshots.
11862 *
11863 * Requires caller and locking. The machine lock must be passed in because it
11864 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11865 *
11866 * @param writeLock Machine lock from top-level caller; this gets passed to
11867 * i_detachDevice.
11868 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11869 * object if called for a SnapshotMachine.
11870 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11871 * added to llMedia; if Full, then all media get added;
11872 * otherwise no media get added.
11873 * @param llMedia Caller's list to receive Medium objects which got detached so
11874 * caller can close() them, depending on cleanupMode.
11875 * @return
11876 */
11877HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11878 Snapshot *pSnapshot,
11879 CleanupMode_T cleanupMode,
11880 MediaList &llMedia)
11881{
11882 Assert(isWriteLockOnCurrentThread());
11883
11884 HRESULT rc;
11885
11886 // make a temporary list because i_detachDevice invalidates iterators into
11887 // mMediumAttachments
11888 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11889
11890 for (MediumAttachmentList::iterator
11891 it = llAttachments2.begin();
11892 it != llAttachments2.end();
11893 ++it)
11894 {
11895 ComObjPtr<MediumAttachment> &pAttach = *it;
11896 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11897
11898 if (!pMedium.isNull())
11899 {
11900 AutoCaller mac(pMedium);
11901 if (FAILED(mac.rc())) return mac.rc();
11902 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11903 DeviceType_T devType = pMedium->i_getDeviceType();
11904 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11905 && devType == DeviceType_HardDisk)
11906 || (cleanupMode == CleanupMode_Full)
11907 )
11908 {
11909 llMedia.push_back(pMedium);
11910 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11911 /* Not allowed to keep this lock as below we need the parent
11912 * medium lock, and the lock order is parent to child. */
11913 lock.release();
11914 /*
11915 * Search for medias which are not attached to any machine, but
11916 * in the chain to an attached disk. Mediums are only consided
11917 * if they are:
11918 * - have only one child
11919 * - no references to any machines
11920 * - are of normal medium type
11921 */
11922 while (!pParent.isNull())
11923 {
11924 AutoCaller mac1(pParent);
11925 if (FAILED(mac1.rc())) return mac1.rc();
11926 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11927 if (pParent->i_getChildren().size() == 1)
11928 {
11929 if ( pParent->i_getMachineBackRefCount() == 0
11930 && pParent->i_getType() == MediumType_Normal
11931 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11932 llMedia.push_back(pParent);
11933 }
11934 else
11935 break;
11936 pParent = pParent->i_getParent();
11937 }
11938 }
11939 }
11940
11941 // real machine: then we need to use the proper method
11942 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11943
11944 if (FAILED(rc))
11945 return rc;
11946 }
11947
11948 return S_OK;
11949}
11950
11951/**
11952 * Perform deferred hard disk detachments.
11953 *
11954 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11955 * changed (not backed up).
11956 *
11957 * If @a aOnline is @c true then this method will also unlock the old hard
11958 * disks for which the new implicit diffs were created and will lock these new
11959 * diffs for writing.
11960 *
11961 * @param aOnline Whether the VM was online prior to this operation.
11962 *
11963 * @note Locks this object for writing!
11964 */
11965void Machine::i_commitMedia(bool aOnline /*= false*/)
11966{
11967 AutoCaller autoCaller(this);
11968 AssertComRCReturnVoid(autoCaller.rc());
11969
11970 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11971
11972 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11973
11974 HRESULT rc = S_OK;
11975
11976 /* no attach/detach operations -- nothing to do */
11977 if (!mMediumAttachments.isBackedUp())
11978 return;
11979
11980 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11981 bool fMediaNeedsLocking = false;
11982
11983 /* enumerate new attachments */
11984 for (MediumAttachmentList::const_iterator
11985 it = mMediumAttachments->begin();
11986 it != mMediumAttachments->end();
11987 ++it)
11988 {
11989 MediumAttachment *pAttach = *it;
11990
11991 pAttach->i_commit();
11992
11993 Medium *pMedium = pAttach->i_getMedium();
11994 bool fImplicit = pAttach->i_isImplicit();
11995
11996 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11997 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11998 fImplicit));
11999
12000 /** @todo convert all this Machine-based voodoo to MediumAttachment
12001 * based commit logic. */
12002 if (fImplicit)
12003 {
12004 /* convert implicit attachment to normal */
12005 pAttach->i_setImplicit(false);
12006
12007 if ( aOnline
12008 && pMedium
12009 && pAttach->i_getType() == DeviceType_HardDisk
12010 )
12011 {
12012 /* update the appropriate lock list */
12013 MediumLockList *pMediumLockList;
12014 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
12015 AssertComRC(rc);
12016 if (pMediumLockList)
12017 {
12018 /* unlock if there's a need to change the locking */
12019 if (!fMediaNeedsLocking)
12020 {
12021 rc = mData->mSession.mLockedMedia.Unlock();
12022 AssertComRC(rc);
12023 fMediaNeedsLocking = true;
12024 }
12025 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
12026 AssertComRC(rc);
12027 rc = pMediumLockList->Append(pMedium, true);
12028 AssertComRC(rc);
12029 }
12030 }
12031
12032 continue;
12033 }
12034
12035 if (pMedium)
12036 {
12037 /* was this medium attached before? */
12038 for (MediumAttachmentList::iterator
12039 oldIt = oldAtts.begin();
12040 oldIt != oldAtts.end();
12041 ++oldIt)
12042 {
12043 MediumAttachment *pOldAttach = *oldIt;
12044 if (pOldAttach->i_getMedium() == pMedium)
12045 {
12046 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
12047
12048 /* yes: remove from old to avoid de-association */
12049 oldAtts.erase(oldIt);
12050 break;
12051 }
12052 }
12053 }
12054 }
12055
12056 /* enumerate remaining old attachments and de-associate from the
12057 * current machine state */
12058 for (MediumAttachmentList::const_iterator
12059 it = oldAtts.begin();
12060 it != oldAtts.end();
12061 ++it)
12062 {
12063 MediumAttachment *pAttach = *it;
12064 Medium *pMedium = pAttach->i_getMedium();
12065
12066 /* Detach only hard disks, since DVD/floppy media is detached
12067 * instantly in MountMedium. */
12068 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
12069 {
12070 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
12071
12072 /* now de-associate from the current machine state */
12073 rc = pMedium->i_removeBackReference(mData->mUuid);
12074 AssertComRC(rc);
12075
12076 if (aOnline)
12077 {
12078 /* unlock since medium is not used anymore */
12079 MediumLockList *pMediumLockList;
12080 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
12081 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
12082 {
12083 /* this happens for online snapshots, there the attachment
12084 * is changing, but only to a diff image created under
12085 * the old one, so there is no separate lock list */
12086 Assert(!pMediumLockList);
12087 }
12088 else
12089 {
12090 AssertComRC(rc);
12091 if (pMediumLockList)
12092 {
12093 rc = mData->mSession.mLockedMedia.Remove(pAttach);
12094 AssertComRC(rc);
12095 }
12096 }
12097 }
12098 }
12099 }
12100
12101 /* take media locks again so that the locking state is consistent */
12102 if (fMediaNeedsLocking)
12103 {
12104 Assert(aOnline);
12105 rc = mData->mSession.mLockedMedia.Lock();
12106 AssertComRC(rc);
12107 }
12108
12109 /* commit the hard disk changes */
12110 mMediumAttachments.commit();
12111
12112 if (i_isSessionMachine())
12113 {
12114 /*
12115 * Update the parent machine to point to the new owner.
12116 * This is necessary because the stored parent will point to the
12117 * session machine otherwise and cause crashes or errors later
12118 * when the session machine gets invalid.
12119 */
12120 /** @todo Change the MediumAttachment class to behave like any other
12121 * class in this regard by creating peer MediumAttachment
12122 * objects for session machines and share the data with the peer
12123 * machine.
12124 */
12125 for (MediumAttachmentList::const_iterator
12126 it = mMediumAttachments->begin();
12127 it != mMediumAttachments->end();
12128 ++it)
12129 (*it)->i_updateParentMachine(mPeer);
12130
12131 /* attach new data to the primary machine and reshare it */
12132 mPeer->mMediumAttachments.attach(mMediumAttachments);
12133 }
12134
12135 return;
12136}
12137
12138/**
12139 * Perform deferred deletion of implicitly created diffs.
12140 *
12141 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
12142 * changed (not backed up).
12143 *
12144 * @note Locks this object for writing!
12145 */
12146void Machine::i_rollbackMedia()
12147{
12148 AutoCaller autoCaller(this);
12149 AssertComRCReturnVoid(autoCaller.rc());
12150
12151 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12152 LogFlowThisFunc(("Entering rollbackMedia\n"));
12153
12154 HRESULT rc = S_OK;
12155
12156 /* no attach/detach operations -- nothing to do */
12157 if (!mMediumAttachments.isBackedUp())
12158 return;
12159
12160 /* enumerate new attachments */
12161 for (MediumAttachmentList::const_iterator
12162 it = mMediumAttachments->begin();
12163 it != mMediumAttachments->end();
12164 ++it)
12165 {
12166 MediumAttachment *pAttach = *it;
12167 /* Fix up the backrefs for DVD/floppy media. */
12168 if (pAttach->i_getType() != DeviceType_HardDisk)
12169 {
12170 Medium *pMedium = pAttach->i_getMedium();
12171 if (pMedium)
12172 {
12173 rc = pMedium->i_removeBackReference(mData->mUuid);
12174 AssertComRC(rc);
12175 }
12176 }
12177
12178 (*it)->i_rollback();
12179
12180 pAttach = *it;
12181 /* Fix up the backrefs for DVD/floppy media. */
12182 if (pAttach->i_getType() != DeviceType_HardDisk)
12183 {
12184 Medium *pMedium = pAttach->i_getMedium();
12185 if (pMedium)
12186 {
12187 rc = pMedium->i_addBackReference(mData->mUuid);
12188 AssertComRC(rc);
12189 }
12190 }
12191 }
12192
12193 /** @todo convert all this Machine-based voodoo to MediumAttachment
12194 * based rollback logic. */
12195 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
12196
12197 return;
12198}
12199
12200/**
12201 * Returns true if the settings file is located in the directory named exactly
12202 * as the machine; this means, among other things, that the machine directory
12203 * should be auto-renamed.
12204 *
12205 * @param aSettingsDir if not NULL, the full machine settings file directory
12206 * name will be assigned there.
12207 *
12208 * @note Doesn't lock anything.
12209 * @note Not thread safe (must be called from this object's lock).
12210 */
12211bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
12212{
12213 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12214 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
12215 if (aSettingsDir)
12216 *aSettingsDir = strMachineDirName;
12217 strMachineDirName.stripPath(); // vmname
12218 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12219 strConfigFileOnly.stripPath() // vmname.vbox
12220 .stripSuffix(); // vmname
12221 /** @todo hack, make somehow use of ComposeMachineFilename */
12222 if (mUserData->s.fDirectoryIncludesUUID)
12223 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
12224
12225 AssertReturn(!strMachineDirName.isEmpty(), false);
12226 AssertReturn(!strConfigFileOnly.isEmpty(), false);
12227
12228 return strMachineDirName == strConfigFileOnly;
12229}
12230
12231/**
12232 * Discards all changes to machine settings.
12233 *
12234 * @param aNotify Whether to notify the direct session about changes or not.
12235 *
12236 * @note Locks objects for writing!
12237 */
12238void Machine::i_rollback(bool aNotify)
12239{
12240 AutoCaller autoCaller(this);
12241 AssertComRCReturn(autoCaller.rc(), (void)0);
12242
12243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12244
12245 if (!mStorageControllers.isNull())
12246 {
12247 if (mStorageControllers.isBackedUp())
12248 {
12249 /* unitialize all new devices (absent in the backed up list). */
12250 StorageControllerList *backedList = mStorageControllers.backedUpData();
12251 for (StorageControllerList::const_iterator
12252 it = mStorageControllers->begin();
12253 it != mStorageControllers->end();
12254 ++it)
12255 {
12256 if ( std::find(backedList->begin(), backedList->end(), *it)
12257 == backedList->end()
12258 )
12259 {
12260 (*it)->uninit();
12261 }
12262 }
12263
12264 /* restore the list */
12265 mStorageControllers.rollback();
12266 }
12267
12268 /* rollback any changes to devices after restoring the list */
12269 if (mData->flModifications & IsModified_Storage)
12270 {
12271 for (StorageControllerList::const_iterator
12272 it = mStorageControllers->begin();
12273 it != mStorageControllers->end();
12274 ++it)
12275 {
12276 (*it)->i_rollback();
12277 }
12278 }
12279 }
12280
12281 if (!mUSBControllers.isNull())
12282 {
12283 if (mUSBControllers.isBackedUp())
12284 {
12285 /* unitialize all new devices (absent in the backed up list). */
12286 USBControllerList *backedList = mUSBControllers.backedUpData();
12287 for (USBControllerList::const_iterator
12288 it = mUSBControllers->begin();
12289 it != mUSBControllers->end();
12290 ++it)
12291 {
12292 if ( std::find(backedList->begin(), backedList->end(), *it)
12293 == backedList->end()
12294 )
12295 {
12296 (*it)->uninit();
12297 }
12298 }
12299
12300 /* restore the list */
12301 mUSBControllers.rollback();
12302 }
12303
12304 /* rollback any changes to devices after restoring the list */
12305 if (mData->flModifications & IsModified_USB)
12306 {
12307 for (USBControllerList::const_iterator
12308 it = mUSBControllers->begin();
12309 it != mUSBControllers->end();
12310 ++it)
12311 {
12312 (*it)->i_rollback();
12313 }
12314 }
12315 }
12316
12317 mUserData.rollback();
12318
12319 mHWData.rollback();
12320
12321 if (mData->flModifications & IsModified_Storage)
12322 i_rollbackMedia();
12323
12324 if (mBIOSSettings)
12325 mBIOSSettings->i_rollback();
12326
12327 if (mTrustedPlatformModule)
12328 mTrustedPlatformModule->i_rollback();
12329
12330 if (mNvramStore)
12331 mNvramStore->i_rollback();
12332
12333 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
12334 mRecordingSettings->i_rollback();
12335
12336 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
12337 mGraphicsAdapter->i_rollback();
12338
12339 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12340 mVRDEServer->i_rollback();
12341
12342 if (mAudioSettings && (mData->flModifications & IsModified_AudioSettings))
12343 mAudioSettings->i_rollback();
12344
12345 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12346 mUSBDeviceFilters->i_rollback();
12347
12348 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12349 mBandwidthControl->i_rollback();
12350
12351 if (!mHWData.isNull())
12352 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12353 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12354 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12355 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12356
12357 if (mData->flModifications & IsModified_NetworkAdapters)
12358 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12359 if ( mNetworkAdapters[slot]
12360 && mNetworkAdapters[slot]->i_isModified())
12361 {
12362 mNetworkAdapters[slot]->i_rollback();
12363 networkAdapters[slot] = mNetworkAdapters[slot];
12364 }
12365
12366 if (mData->flModifications & IsModified_SerialPorts)
12367 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12368 if ( mSerialPorts[slot]
12369 && mSerialPorts[slot]->i_isModified())
12370 {
12371 mSerialPorts[slot]->i_rollback();
12372 serialPorts[slot] = mSerialPorts[slot];
12373 }
12374
12375 if (mData->flModifications & IsModified_ParallelPorts)
12376 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12377 if ( mParallelPorts[slot]
12378 && mParallelPorts[slot]->i_isModified())
12379 {
12380 mParallelPorts[slot]->i_rollback();
12381 parallelPorts[slot] = mParallelPorts[slot];
12382 }
12383
12384 if (aNotify)
12385 {
12386 /* inform the direct session about changes */
12387
12388 ComObjPtr<Machine> that = this;
12389 uint32_t flModifications = mData->flModifications;
12390 alock.release();
12391
12392 if (flModifications & IsModified_SharedFolders)
12393 that->i_onSharedFolderChange();
12394
12395 if (flModifications & IsModified_VRDEServer)
12396 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12397 if (flModifications & IsModified_USB)
12398 that->i_onUSBControllerChange();
12399
12400 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12401 if (networkAdapters[slot])
12402 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12403 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12404 if (serialPorts[slot])
12405 that->i_onSerialPortChange(serialPorts[slot]);
12406 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12407 if (parallelPorts[slot])
12408 that->i_onParallelPortChange(parallelPorts[slot]);
12409
12410 if (flModifications & IsModified_Storage)
12411 {
12412 for (StorageControllerList::const_iterator
12413 it = mStorageControllers->begin();
12414 it != mStorageControllers->end();
12415 ++it)
12416 {
12417 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
12418 }
12419 }
12420
12421
12422#if 0
12423 if (flModifications & IsModified_BandwidthControl)
12424 that->onBandwidthControlChange();
12425#endif
12426 }
12427}
12428
12429/**
12430 * Commits all the changes to machine settings.
12431 *
12432 * Note that this operation is supposed to never fail.
12433 *
12434 * @note Locks this object and children for writing.
12435 */
12436void Machine::i_commit()
12437{
12438 AutoCaller autoCaller(this);
12439 AssertComRCReturnVoid(autoCaller.rc());
12440
12441 AutoCaller peerCaller(mPeer);
12442 AssertComRCReturnVoid(peerCaller.rc());
12443
12444 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12445
12446 /*
12447 * use safe commit to ensure Snapshot machines (that share mUserData)
12448 * will still refer to a valid memory location
12449 */
12450 mUserData.commitCopy();
12451
12452 mHWData.commit();
12453
12454 if (mMediumAttachments.isBackedUp())
12455 i_commitMedia(Global::IsOnline(mData->mMachineState));
12456
12457 mBIOSSettings->i_commit();
12458 mTrustedPlatformModule->i_commit();
12459 mNvramStore->i_commit();
12460 mRecordingSettings->i_commit();
12461 mGraphicsAdapter->i_commit();
12462 mVRDEServer->i_commit();
12463 mAudioSettings->i_commit();
12464 mUSBDeviceFilters->i_commit();
12465 mBandwidthControl->i_commit();
12466
12467 /* Since mNetworkAdapters is a list which might have been changed (resized)
12468 * without using the Backupable<> template we need to handle the copying
12469 * of the list entries manually, including the creation of peers for the
12470 * new objects. */
12471 bool commitNetworkAdapters = false;
12472 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12473 if (mPeer)
12474 {
12475 /* commit everything, even the ones which will go away */
12476 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12477 mNetworkAdapters[slot]->i_commit();
12478 /* copy over the new entries, creating a peer and uninit the original */
12479 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12480 for (size_t slot = 0; slot < newSize; slot++)
12481 {
12482 /* look if this adapter has a peer device */
12483 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12484 if (!peer)
12485 {
12486 /* no peer means the adapter is a newly created one;
12487 * create a peer owning data this data share it with */
12488 peer.createObject();
12489 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12490 }
12491 mPeer->mNetworkAdapters[slot] = peer;
12492 }
12493 /* uninit any no longer needed network adapters */
12494 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12495 mNetworkAdapters[slot]->uninit();
12496 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12497 {
12498 if (mPeer->mNetworkAdapters[slot])
12499 mPeer->mNetworkAdapters[slot]->uninit();
12500 }
12501 /* Keep the original network adapter count until this point, so that
12502 * discarding a chipset type change will not lose settings. */
12503 mNetworkAdapters.resize(newSize);
12504 mPeer->mNetworkAdapters.resize(newSize);
12505 }
12506 else
12507 {
12508 /* we have no peer (our parent is the newly created machine);
12509 * just commit changes to the network adapters */
12510 commitNetworkAdapters = true;
12511 }
12512 if (commitNetworkAdapters)
12513 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12514 mNetworkAdapters[slot]->i_commit();
12515
12516 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12517 mSerialPorts[slot]->i_commit();
12518 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12519 mParallelPorts[slot]->i_commit();
12520
12521 bool commitStorageControllers = false;
12522
12523 if (mStorageControllers.isBackedUp())
12524 {
12525 mStorageControllers.commit();
12526
12527 if (mPeer)
12528 {
12529 /* Commit all changes to new controllers (this will reshare data with
12530 * peers for those who have peers) */
12531 StorageControllerList *newList = new StorageControllerList();
12532 for (StorageControllerList::const_iterator
12533 it = mStorageControllers->begin();
12534 it != mStorageControllers->end();
12535 ++it)
12536 {
12537 (*it)->i_commit();
12538
12539 /* look if this controller has a peer device */
12540 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12541 if (!peer)
12542 {
12543 /* no peer means the device is a newly created one;
12544 * create a peer owning data this device share it with */
12545 peer.createObject();
12546 peer->init(mPeer, *it, true /* aReshare */);
12547 }
12548 else
12549 {
12550 /* remove peer from the old list */
12551 mPeer->mStorageControllers->remove(peer);
12552 }
12553 /* and add it to the new list */
12554 newList->push_back(peer);
12555 }
12556
12557 /* uninit old peer's controllers that are left */
12558 for (StorageControllerList::const_iterator
12559 it = mPeer->mStorageControllers->begin();
12560 it != mPeer->mStorageControllers->end();
12561 ++it)
12562 {
12563 (*it)->uninit();
12564 }
12565
12566 /* attach new list of controllers to our peer */
12567 mPeer->mStorageControllers.attach(newList);
12568 }
12569 else
12570 {
12571 /* we have no peer (our parent is the newly created machine);
12572 * just commit changes to devices */
12573 commitStorageControllers = true;
12574 }
12575 }
12576 else
12577 {
12578 /* the list of controllers itself is not changed,
12579 * just commit changes to controllers themselves */
12580 commitStorageControllers = true;
12581 }
12582
12583 if (commitStorageControllers)
12584 {
12585 for (StorageControllerList::const_iterator
12586 it = mStorageControllers->begin();
12587 it != mStorageControllers->end();
12588 ++it)
12589 {
12590 (*it)->i_commit();
12591 }
12592 }
12593
12594 bool commitUSBControllers = false;
12595
12596 if (mUSBControllers.isBackedUp())
12597 {
12598 mUSBControllers.commit();
12599
12600 if (mPeer)
12601 {
12602 /* Commit all changes to new controllers (this will reshare data with
12603 * peers for those who have peers) */
12604 USBControllerList *newList = new USBControllerList();
12605 for (USBControllerList::const_iterator
12606 it = mUSBControllers->begin();
12607 it != mUSBControllers->end();
12608 ++it)
12609 {
12610 (*it)->i_commit();
12611
12612 /* look if this controller has a peer device */
12613 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12614 if (!peer)
12615 {
12616 /* no peer means the device is a newly created one;
12617 * create a peer owning data this device share it with */
12618 peer.createObject();
12619 peer->init(mPeer, *it, true /* aReshare */);
12620 }
12621 else
12622 {
12623 /* remove peer from the old list */
12624 mPeer->mUSBControllers->remove(peer);
12625 }
12626 /* and add it to the new list */
12627 newList->push_back(peer);
12628 }
12629
12630 /* uninit old peer's controllers that are left */
12631 for (USBControllerList::const_iterator
12632 it = mPeer->mUSBControllers->begin();
12633 it != mPeer->mUSBControllers->end();
12634 ++it)
12635 {
12636 (*it)->uninit();
12637 }
12638
12639 /* attach new list of controllers to our peer */
12640 mPeer->mUSBControllers.attach(newList);
12641 }
12642 else
12643 {
12644 /* we have no peer (our parent is the newly created machine);
12645 * just commit changes to devices */
12646 commitUSBControllers = true;
12647 }
12648 }
12649 else
12650 {
12651 /* the list of controllers itself is not changed,
12652 * just commit changes to controllers themselves */
12653 commitUSBControllers = true;
12654 }
12655
12656 if (commitUSBControllers)
12657 {
12658 for (USBControllerList::const_iterator
12659 it = mUSBControllers->begin();
12660 it != mUSBControllers->end();
12661 ++it)
12662 {
12663 (*it)->i_commit();
12664 }
12665 }
12666
12667 if (i_isSessionMachine())
12668 {
12669 /* attach new data to the primary machine and reshare it */
12670 mPeer->mUserData.attach(mUserData);
12671 mPeer->mHWData.attach(mHWData);
12672 /* mmMediumAttachments is reshared by fixupMedia */
12673 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12674 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12675 }
12676}
12677
12678/**
12679 * Copies all the hardware data from the given machine.
12680 *
12681 * Currently, only called when the VM is being restored from a snapshot. In
12682 * particular, this implies that the VM is not running during this method's
12683 * call.
12684 *
12685 * @note This method must be called from under this object's lock.
12686 *
12687 * @note This method doesn't call #i_commit(), so all data remains backed up and
12688 * unsaved.
12689 */
12690void Machine::i_copyFrom(Machine *aThat)
12691{
12692 AssertReturnVoid(!i_isSnapshotMachine());
12693 AssertReturnVoid(aThat->i_isSnapshotMachine());
12694
12695 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12696
12697 mHWData.assignCopy(aThat->mHWData);
12698
12699 // create copies of all shared folders (mHWData after attaching a copy
12700 // contains just references to original objects)
12701 for (HWData::SharedFolderList::iterator
12702 it = mHWData->mSharedFolders.begin();
12703 it != mHWData->mSharedFolders.end();
12704 ++it)
12705 {
12706 ComObjPtr<SharedFolder> folder;
12707 folder.createObject();
12708 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12709 AssertComRC(rc);
12710 *it = folder;
12711 }
12712
12713 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12714 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12715 mNvramStore->i_copyFrom(aThat->mNvramStore);
12716 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12717 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12718 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12719 mAudioSettings->i_copyFrom(aThat->mAudioSettings);
12720 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12721 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12722
12723 /* create private copies of all controllers */
12724 mStorageControllers.backup();
12725 mStorageControllers->clear();
12726 for (StorageControllerList::const_iterator
12727 it = aThat->mStorageControllers->begin();
12728 it != aThat->mStorageControllers->end();
12729 ++it)
12730 {
12731 ComObjPtr<StorageController> ctrl;
12732 ctrl.createObject();
12733 ctrl->initCopy(this, *it);
12734 mStorageControllers->push_back(ctrl);
12735 }
12736
12737 /* create private copies of all USB controllers */
12738 mUSBControllers.backup();
12739 mUSBControllers->clear();
12740 for (USBControllerList::const_iterator
12741 it = aThat->mUSBControllers->begin();
12742 it != aThat->mUSBControllers->end();
12743 ++it)
12744 {
12745 ComObjPtr<USBController> ctrl;
12746 ctrl.createObject();
12747 ctrl->initCopy(this, *it);
12748 mUSBControllers->push_back(ctrl);
12749 }
12750
12751 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12752 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12753 {
12754 if (mNetworkAdapters[slot].isNotNull())
12755 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12756 else
12757 {
12758 unconst(mNetworkAdapters[slot]).createObject();
12759 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12760 }
12761 }
12762 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12763 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12764 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12765 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12766}
12767
12768/**
12769 * Returns whether the given storage controller is hotplug capable.
12770 *
12771 * @returns true if the controller supports hotplugging
12772 * false otherwise.
12773 * @param enmCtrlType The controller type to check for.
12774 */
12775bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12776{
12777 ComPtr<ISystemProperties> systemProperties;
12778 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12779 if (FAILED(rc))
12780 return false;
12781
12782 BOOL aHotplugCapable = FALSE;
12783 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12784
12785 return RT_BOOL(aHotplugCapable);
12786}
12787
12788#ifdef VBOX_WITH_RESOURCE_USAGE_API
12789
12790void Machine::i_getDiskList(MediaList &list)
12791{
12792 for (MediumAttachmentList::const_iterator
12793 it = mMediumAttachments->begin();
12794 it != mMediumAttachments->end();
12795 ++it)
12796 {
12797 MediumAttachment *pAttach = *it;
12798 /* just in case */
12799 AssertContinue(pAttach);
12800
12801 AutoCaller localAutoCallerA(pAttach);
12802 if (FAILED(localAutoCallerA.rc())) continue;
12803
12804 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12805
12806 if (pAttach->i_getType() == DeviceType_HardDisk)
12807 list.push_back(pAttach->i_getMedium());
12808 }
12809}
12810
12811void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12812{
12813 AssertReturnVoid(isWriteLockOnCurrentThread());
12814 AssertPtrReturnVoid(aCollector);
12815
12816 pm::CollectorHAL *hal = aCollector->getHAL();
12817 /* Create sub metrics */
12818 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12819 "Percentage of processor time spent in user mode by the VM process.");
12820 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12821 "Percentage of processor time spent in kernel mode by the VM process.");
12822 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12823 "Size of resident portion of VM process in memory.");
12824 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12825 "Actual size of all VM disks combined.");
12826 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12827 "Network receive rate.");
12828 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12829 "Network transmit rate.");
12830 /* Create and register base metrics */
12831 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12832 cpuLoadUser, cpuLoadKernel);
12833 aCollector->registerBaseMetric(cpuLoad);
12834 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12835 ramUsageUsed);
12836 aCollector->registerBaseMetric(ramUsage);
12837 MediaList disks;
12838 i_getDiskList(disks);
12839 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12840 diskUsageUsed);
12841 aCollector->registerBaseMetric(diskUsage);
12842
12843 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12844 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12845 new pm::AggregateAvg()));
12846 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12847 new pm::AggregateMin()));
12848 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12849 new pm::AggregateMax()));
12850 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12851 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12852 new pm::AggregateAvg()));
12853 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12854 new pm::AggregateMin()));
12855 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12856 new pm::AggregateMax()));
12857
12858 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12859 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12860 new pm::AggregateAvg()));
12861 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12862 new pm::AggregateMin()));
12863 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12864 new pm::AggregateMax()));
12865
12866 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12867 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12868 new pm::AggregateAvg()));
12869 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12870 new pm::AggregateMin()));
12871 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12872 new pm::AggregateMax()));
12873
12874
12875 /* Guest metrics collector */
12876 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12877 aCollector->registerGuest(mCollectorGuest);
12878 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12879
12880 /* Create sub metrics */
12881 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12882 "Percentage of processor time spent in user mode as seen by the guest.");
12883 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12884 "Percentage of processor time spent in kernel mode as seen by the guest.");
12885 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12886 "Percentage of processor time spent idling as seen by the guest.");
12887
12888 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12889 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12890 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12891 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12892 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12893 pm::SubMetric *guestMemCache = new pm::SubMetric(
12894 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12895
12896 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12897 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12898
12899 /* Create and register base metrics */
12900 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12901 machineNetRx, machineNetTx);
12902 aCollector->registerBaseMetric(machineNetRate);
12903
12904 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12905 guestLoadUser, guestLoadKernel, guestLoadIdle);
12906 aCollector->registerBaseMetric(guestCpuLoad);
12907
12908 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12909 guestMemTotal, guestMemFree,
12910 guestMemBalloon, guestMemShared,
12911 guestMemCache, guestPagedTotal);
12912 aCollector->registerBaseMetric(guestCpuMem);
12913
12914 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12915 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12916 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12917 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12918
12919 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12920 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12921 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12922 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12923
12924 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12925 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12926 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12927 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12928
12929 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12930 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12931 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12932 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12933
12934 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12935 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12936 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12937 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12938
12939 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12940 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12941 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12942 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12943
12944 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12945 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12946 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12947 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12948
12949 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12950 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12951 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12952 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12953
12954 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12955 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12956 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12957 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12958
12959 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12960 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12961 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12962 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12963
12964 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12965 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12966 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12967 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12968}
12969
12970void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12971{
12972 AssertReturnVoid(isWriteLockOnCurrentThread());
12973
12974 if (aCollector)
12975 {
12976 aCollector->unregisterMetricsFor(aMachine);
12977 aCollector->unregisterBaseMetricsFor(aMachine);
12978 }
12979}
12980
12981#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12982
12983
12984////////////////////////////////////////////////////////////////////////////////
12985
12986DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12987
12988HRESULT SessionMachine::FinalConstruct()
12989{
12990 LogFlowThisFunc(("\n"));
12991
12992 mClientToken = NULL;
12993
12994 return BaseFinalConstruct();
12995}
12996
12997void SessionMachine::FinalRelease()
12998{
12999 LogFlowThisFunc(("\n"));
13000
13001 Assert(!mClientToken);
13002 /* paranoia, should not hang around any more */
13003 if (mClientToken)
13004 {
13005 delete mClientToken;
13006 mClientToken = NULL;
13007 }
13008
13009 uninit(Uninit::Unexpected);
13010
13011 BaseFinalRelease();
13012}
13013
13014/**
13015 * @note Must be called only by Machine::LockMachine() from its own write lock.
13016 */
13017HRESULT SessionMachine::init(Machine *aMachine)
13018{
13019 LogFlowThisFuncEnter();
13020 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
13021
13022 AssertReturn(aMachine, E_INVALIDARG);
13023
13024 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
13025
13026 /* Enclose the state transition NotReady->InInit->Ready */
13027 AutoInitSpan autoInitSpan(this);
13028 AssertReturn(autoInitSpan.isOk(), E_FAIL);
13029
13030 HRESULT rc = S_OK;
13031
13032 RT_ZERO(mAuthLibCtx);
13033
13034 /* create the machine client token */
13035 try
13036 {
13037 mClientToken = new ClientToken(aMachine, this);
13038 if (!mClientToken->isReady())
13039 {
13040 delete mClientToken;
13041 mClientToken = NULL;
13042 rc = E_FAIL;
13043 }
13044 }
13045 catch (std::bad_alloc &)
13046 {
13047 rc = E_OUTOFMEMORY;
13048 }
13049 if (FAILED(rc))
13050 return rc;
13051
13052 /* memorize the peer Machine */
13053 unconst(mPeer) = aMachine;
13054 /* share the parent pointer */
13055 unconst(mParent) = aMachine->mParent;
13056
13057 /* take the pointers to data to share */
13058 mData.share(aMachine->mData);
13059 mSSData.share(aMachine->mSSData);
13060
13061 mUserData.share(aMachine->mUserData);
13062 mHWData.share(aMachine->mHWData);
13063 mMediumAttachments.share(aMachine->mMediumAttachments);
13064
13065 mStorageControllers.allocate();
13066 for (StorageControllerList::const_iterator
13067 it = aMachine->mStorageControllers->begin();
13068 it != aMachine->mStorageControllers->end();
13069 ++it)
13070 {
13071 ComObjPtr<StorageController> ctl;
13072 ctl.createObject();
13073 ctl->init(this, *it);
13074 mStorageControllers->push_back(ctl);
13075 }
13076
13077 mUSBControllers.allocate();
13078 for (USBControllerList::const_iterator
13079 it = aMachine->mUSBControllers->begin();
13080 it != aMachine->mUSBControllers->end();
13081 ++it)
13082 {
13083 ComObjPtr<USBController> ctl;
13084 ctl.createObject();
13085 ctl->init(this, *it);
13086 mUSBControllers->push_back(ctl);
13087 }
13088
13089 unconst(mBIOSSettings).createObject();
13090 mBIOSSettings->init(this, aMachine->mBIOSSettings);
13091
13092 unconst(mTrustedPlatformModule).createObject();
13093 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
13094
13095 unconst(mNvramStore).createObject();
13096 mNvramStore->init(this, aMachine->mNvramStore);
13097
13098 unconst(mRecordingSettings).createObject();
13099 mRecordingSettings->init(this, aMachine->mRecordingSettings);
13100 /* create another GraphicsAdapter object that will be mutable */
13101 unconst(mGraphicsAdapter).createObject();
13102 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
13103 /* create another VRDEServer object that will be mutable */
13104 unconst(mVRDEServer).createObject();
13105 mVRDEServer->init(this, aMachine->mVRDEServer);
13106 /* create another audio settings object that will be mutable */
13107 unconst(mAudioSettings).createObject();
13108 mAudioSettings->init(this, aMachine->mAudioSettings);
13109 /* create a list of serial ports that will be mutable */
13110 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
13111 {
13112 unconst(mSerialPorts[slot]).createObject();
13113 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
13114 }
13115 /* create a list of parallel ports that will be mutable */
13116 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
13117 {
13118 unconst(mParallelPorts[slot]).createObject();
13119 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
13120 }
13121
13122 /* create another USB device filters object that will be mutable */
13123 unconst(mUSBDeviceFilters).createObject();
13124 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
13125
13126 /* create a list of network adapters that will be mutable */
13127 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
13128 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13129 {
13130 unconst(mNetworkAdapters[slot]).createObject();
13131 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
13132 }
13133
13134 /* create another bandwidth control object that will be mutable */
13135 unconst(mBandwidthControl).createObject();
13136 mBandwidthControl->init(this, aMachine->mBandwidthControl);
13137
13138 /* default is to delete saved state on Saved -> PoweredOff transition */
13139 mRemoveSavedState = true;
13140
13141 /* Confirm a successful initialization when it's the case */
13142 autoInitSpan.setSucceeded();
13143
13144 miNATNetworksStarted = 0;
13145
13146 LogFlowThisFuncLeave();
13147 return rc;
13148}
13149
13150/**
13151 * Uninitializes this session object. If the reason is other than
13152 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
13153 * or the client watcher code.
13154 *
13155 * @param aReason uninitialization reason
13156 *
13157 * @note Locks mParent + this object for writing.
13158 */
13159void SessionMachine::uninit(Uninit::Reason aReason)
13160{
13161 LogFlowThisFuncEnter();
13162 LogFlowThisFunc(("reason=%d\n", aReason));
13163
13164 /*
13165 * Strongly reference ourselves to prevent this object deletion after
13166 * mData->mSession.mMachine.setNull() below (which can release the last
13167 * reference and call the destructor). Important: this must be done before
13168 * accessing any members (and before AutoUninitSpan that does it as well).
13169 * This self reference will be released as the very last step on return.
13170 */
13171 ComObjPtr<SessionMachine> selfRef;
13172 if (aReason != Uninit::Unexpected)
13173 selfRef = this;
13174
13175 /* Enclose the state transition Ready->InUninit->NotReady */
13176 AutoUninitSpan autoUninitSpan(this);
13177 if (autoUninitSpan.uninitDone())
13178 {
13179 LogFlowThisFunc(("Already uninitialized\n"));
13180 LogFlowThisFuncLeave();
13181 return;
13182 }
13183
13184 if (autoUninitSpan.initFailed())
13185 {
13186 /* We've been called by init() because it's failed. It's not really
13187 * necessary (nor it's safe) to perform the regular uninit sequence
13188 * below, the following is enough.
13189 */
13190 LogFlowThisFunc(("Initialization failed.\n"));
13191 /* destroy the machine client token */
13192 if (mClientToken)
13193 {
13194 delete mClientToken;
13195 mClientToken = NULL;
13196 }
13197 uninitDataAndChildObjects();
13198 mData.free();
13199 unconst(mParent) = NULL;
13200 unconst(mPeer) = NULL;
13201 LogFlowThisFuncLeave();
13202 return;
13203 }
13204
13205 MachineState_T lastState;
13206 {
13207 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
13208 lastState = mData->mMachineState;
13209 }
13210 NOREF(lastState);
13211
13212#ifdef VBOX_WITH_USB
13213 // release all captured USB devices, but do this before requesting the locks below
13214 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
13215 {
13216 /* Console::captureUSBDevices() is called in the VM process only after
13217 * setting the machine state to Starting or Restoring.
13218 * Console::detachAllUSBDevices() will be called upon successful
13219 * termination. So, we need to release USB devices only if there was
13220 * an abnormal termination of a running VM.
13221 *
13222 * This is identical to SessionMachine::DetachAllUSBDevices except
13223 * for the aAbnormal argument. */
13224 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13225 AssertComRC(rc);
13226 NOREF(rc);
13227
13228 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13229 if (service)
13230 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
13231 }
13232#endif /* VBOX_WITH_USB */
13233
13234 // we need to lock this object in uninit() because the lock is shared
13235 // with mPeer (as well as data we modify below). mParent lock is needed
13236 // by several calls to it.
13237 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
13238
13239#ifdef VBOX_WITH_RESOURCE_USAGE_API
13240 /*
13241 * It is safe to call Machine::i_unregisterMetrics() here because
13242 * PerformanceCollector::samplerCallback no longer accesses guest methods
13243 * holding the lock.
13244 */
13245 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
13246 /* The guest must be unregistered after its metrics (@bugref{5949}). */
13247 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
13248 if (mCollectorGuest)
13249 {
13250 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
13251 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
13252 mCollectorGuest = NULL;
13253 }
13254#endif
13255
13256 if (aReason == Uninit::Abnormal)
13257 {
13258 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
13259
13260 /*
13261 * Move the VM to the 'Aborted' machine state unless we are restoring a
13262 * VM that was in the 'Saved' machine state. In that case, if the VM
13263 * fails before reaching either the 'Restoring' machine state or the
13264 * 'Running' machine state then we set the machine state to
13265 * 'AbortedSaved' in order to preserve the saved state file so that the
13266 * VM can be restored in the future.
13267 */
13268 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
13269 i_setMachineState(MachineState_AbortedSaved);
13270 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
13271 i_setMachineState(MachineState_Aborted);
13272 }
13273
13274 // any machine settings modified?
13275 if (mData->flModifications)
13276 {
13277 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
13278 i_rollback(false /* aNotify */);
13279 }
13280
13281 mData->mSession.mPID = NIL_RTPROCESS;
13282
13283 if (aReason == Uninit::Unexpected)
13284 {
13285 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
13286 * client watcher thread to update the set of machines that have open
13287 * sessions. */
13288 mParent->i_updateClientWatcher();
13289 }
13290
13291 /* uninitialize all remote controls */
13292 if (mData->mSession.mRemoteControls.size())
13293 {
13294 LogFlowThisFunc(("Closing remote sessions (%d):\n",
13295 mData->mSession.mRemoteControls.size()));
13296
13297 /* Always restart a the beginning, since the iterator is invalidated
13298 * by using erase(). */
13299 for (Data::Session::RemoteControlList::iterator
13300 it = mData->mSession.mRemoteControls.begin();
13301 it != mData->mSession.mRemoteControls.end();
13302 it = mData->mSession.mRemoteControls.begin())
13303 {
13304 ComPtr<IInternalSessionControl> pControl = *it;
13305 mData->mSession.mRemoteControls.erase(it);
13306 multilock.release();
13307 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
13308 HRESULT rc = pControl->Uninitialize();
13309 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
13310 if (FAILED(rc))
13311 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
13312 multilock.acquire();
13313 }
13314 mData->mSession.mRemoteControls.clear();
13315 }
13316
13317 /* Remove all references to the NAT network service. The service will stop
13318 * if all references (also from other VMs) are removed. */
13319 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
13320 {
13321 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13322 {
13323 BOOL enabled;
13324 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13325 if ( FAILED(hrc)
13326 || !enabled)
13327 continue;
13328
13329 NetworkAttachmentType_T type;
13330 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13331 if ( SUCCEEDED(hrc)
13332 && type == NetworkAttachmentType_NATNetwork)
13333 {
13334 Bstr name;
13335 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13336 if (SUCCEEDED(hrc))
13337 {
13338 multilock.release();
13339 Utf8Str strName(name);
13340 LogRel(("VM '%s' stops using NAT network '%s'\n",
13341 mUserData->s.strName.c_str(), strName.c_str()));
13342 mParent->i_natNetworkRefDec(strName);
13343 multilock.acquire();
13344 }
13345 }
13346 }
13347 }
13348
13349 /*
13350 * An expected uninitialization can come only from #i_checkForDeath().
13351 * Otherwise it means that something's gone really wrong (for example,
13352 * the Session implementation has released the VirtualBox reference
13353 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
13354 * etc). However, it's also possible, that the client releases the IPC
13355 * semaphore correctly (i.e. before it releases the VirtualBox reference),
13356 * but the VirtualBox release event comes first to the server process.
13357 * This case is practically possible, so we should not assert on an
13358 * unexpected uninit, just log a warning.
13359 */
13360
13361 if (aReason == Uninit::Unexpected)
13362 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
13363
13364 if (aReason != Uninit::Normal)
13365 {
13366 mData->mSession.mDirectControl.setNull();
13367 }
13368 else
13369 {
13370 /* this must be null here (see #OnSessionEnd()) */
13371 Assert(mData->mSession.mDirectControl.isNull());
13372 Assert(mData->mSession.mState == SessionState_Unlocking);
13373 Assert(!mData->mSession.mProgress.isNull());
13374 }
13375 if (mData->mSession.mProgress)
13376 {
13377 if (aReason == Uninit::Normal)
13378 mData->mSession.mProgress->i_notifyComplete(S_OK);
13379 else
13380 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
13381 COM_IIDOF(ISession),
13382 getComponentName(),
13383 tr("The VM session was aborted"));
13384 mData->mSession.mProgress.setNull();
13385 }
13386
13387 if (mConsoleTaskData.mProgress)
13388 {
13389 Assert(aReason == Uninit::Abnormal);
13390 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
13391 COM_IIDOF(ISession),
13392 getComponentName(),
13393 tr("The VM session was aborted"));
13394 mConsoleTaskData.mProgress.setNull();
13395 }
13396
13397 /* remove the association between the peer machine and this session machine */
13398 Assert( (SessionMachine*)mData->mSession.mMachine == this
13399 || aReason == Uninit::Unexpected);
13400
13401 /* reset the rest of session data */
13402 mData->mSession.mLockType = LockType_Null;
13403 mData->mSession.mMachine.setNull();
13404 mData->mSession.mState = SessionState_Unlocked;
13405 mData->mSession.mName.setNull();
13406
13407 /* destroy the machine client token before leaving the exclusive lock */
13408 if (mClientToken)
13409 {
13410 delete mClientToken;
13411 mClientToken = NULL;
13412 }
13413
13414 /* fire an event */
13415 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
13416
13417 uninitDataAndChildObjects();
13418
13419 /* free the essential data structure last */
13420 mData.free();
13421
13422 /* release the exclusive lock before setting the below two to NULL */
13423 multilock.release();
13424
13425 unconst(mParent) = NULL;
13426 unconst(mPeer) = NULL;
13427
13428 AuthLibUnload(&mAuthLibCtx);
13429
13430 LogFlowThisFuncLeave();
13431}
13432
13433// util::Lockable interface
13434////////////////////////////////////////////////////////////////////////////////
13435
13436/**
13437 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13438 * with the primary Machine instance (mPeer).
13439 */
13440RWLockHandle *SessionMachine::lockHandle() const
13441{
13442 AssertReturn(mPeer != NULL, NULL);
13443 return mPeer->lockHandle();
13444}
13445
13446// IInternalMachineControl methods
13447////////////////////////////////////////////////////////////////////////////////
13448
13449/**
13450 * Passes collected guest statistics to performance collector object
13451 */
13452HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13453 ULONG aCpuKernel, ULONG aCpuIdle,
13454 ULONG aMemTotal, ULONG aMemFree,
13455 ULONG aMemBalloon, ULONG aMemShared,
13456 ULONG aMemCache, ULONG aPageTotal,
13457 ULONG aAllocVMM, ULONG aFreeVMM,
13458 ULONG aBalloonedVMM, ULONG aSharedVMM,
13459 ULONG aVmNetRx, ULONG aVmNetTx)
13460{
13461#ifdef VBOX_WITH_RESOURCE_USAGE_API
13462 if (mCollectorGuest)
13463 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13464 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13465 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13466 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13467
13468 return S_OK;
13469#else
13470 NOREF(aValidStats);
13471 NOREF(aCpuUser);
13472 NOREF(aCpuKernel);
13473 NOREF(aCpuIdle);
13474 NOREF(aMemTotal);
13475 NOREF(aMemFree);
13476 NOREF(aMemBalloon);
13477 NOREF(aMemShared);
13478 NOREF(aMemCache);
13479 NOREF(aPageTotal);
13480 NOREF(aAllocVMM);
13481 NOREF(aFreeVMM);
13482 NOREF(aBalloonedVMM);
13483 NOREF(aSharedVMM);
13484 NOREF(aVmNetRx);
13485 NOREF(aVmNetTx);
13486 return E_NOTIMPL;
13487#endif
13488}
13489
13490////////////////////////////////////////////////////////////////////////////////
13491//
13492// SessionMachine task records
13493//
13494////////////////////////////////////////////////////////////////////////////////
13495
13496/**
13497 * Task record for saving the machine state.
13498 */
13499class SessionMachine::SaveStateTask
13500 : public Machine::Task
13501{
13502public:
13503 SaveStateTask(SessionMachine *m,
13504 Progress *p,
13505 const Utf8Str &t,
13506 Reason_T enmReason,
13507 const Utf8Str &strStateFilePath)
13508 : Task(m, p, t),
13509 m_enmReason(enmReason),
13510 m_strStateFilePath(strStateFilePath)
13511 {}
13512
13513private:
13514 void handler()
13515 {
13516 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13517 }
13518
13519 Reason_T m_enmReason;
13520 Utf8Str m_strStateFilePath;
13521
13522 friend class SessionMachine;
13523};
13524
13525/**
13526 * Task thread implementation for SessionMachine::SaveState(), called from
13527 * SessionMachine::taskHandler().
13528 *
13529 * @note Locks this object for writing.
13530 *
13531 * @param task
13532 * @return
13533 */
13534void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13535{
13536 LogFlowThisFuncEnter();
13537
13538 AutoCaller autoCaller(this);
13539 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13540 if (FAILED(autoCaller.rc()))
13541 {
13542 /* we might have been uninitialized because the session was accidentally
13543 * closed by the client, so don't assert */
13544 HRESULT rc = setError(E_FAIL,
13545 tr("The session has been accidentally closed"));
13546 task.m_pProgress->i_notifyComplete(rc);
13547 LogFlowThisFuncLeave();
13548 return;
13549 }
13550
13551 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13552
13553 HRESULT rc = S_OK;
13554
13555 try
13556 {
13557 ComPtr<IInternalSessionControl> directControl;
13558 if (mData->mSession.mLockType == LockType_VM)
13559 directControl = mData->mSession.mDirectControl;
13560 if (directControl.isNull())
13561 throw setError(VBOX_E_INVALID_VM_STATE,
13562 tr("Trying to save state without a running VM"));
13563 alock.release();
13564 BOOL fSuspendedBySave;
13565 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13566 Assert(!fSuspendedBySave);
13567 alock.acquire();
13568
13569 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13570 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13571 throw E_FAIL);
13572
13573 if (SUCCEEDED(rc))
13574 {
13575 mSSData->strStateFilePath = task.m_strStateFilePath;
13576
13577 /* save all VM settings */
13578 rc = i_saveSettings(NULL, alock);
13579 // no need to check whether VirtualBox.xml needs saving also since
13580 // we can't have a name change pending at this point
13581 }
13582 else
13583 {
13584 // On failure, set the state to the state we had at the beginning.
13585 i_setMachineState(task.m_machineStateBackup);
13586 i_updateMachineStateOnClient();
13587
13588 // Delete the saved state file (might have been already created).
13589 // No need to check whether this is shared with a snapshot here
13590 // because we certainly created a fresh saved state file here.
13591 i_deleteFile(task.m_strStateFilePath, true /* fIgnoreFailures */);
13592 }
13593 }
13594 catch (HRESULT aRC) { rc = aRC; }
13595
13596 task.m_pProgress->i_notifyComplete(rc);
13597
13598 LogFlowThisFuncLeave();
13599}
13600
13601/**
13602 * @note Locks this object for writing.
13603 */
13604HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13605{
13606 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13607}
13608
13609HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13610{
13611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13612
13613 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13614 if (FAILED(rc)) return rc;
13615
13616 if ( mData->mMachineState != MachineState_Running
13617 && mData->mMachineState != MachineState_Paused
13618 )
13619 return setError(VBOX_E_INVALID_VM_STATE,
13620 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13621 Global::stringifyMachineState(mData->mMachineState));
13622
13623 ComObjPtr<Progress> pProgress;
13624 pProgress.createObject();
13625 rc = pProgress->init(i_getVirtualBox(),
13626 static_cast<IMachine *>(this) /* aInitiator */,
13627 tr("Saving the execution state of the virtual machine"),
13628 FALSE /* aCancelable */);
13629 if (FAILED(rc))
13630 return rc;
13631
13632 Utf8Str strStateFilePath;
13633 i_composeSavedStateFilename(strStateFilePath);
13634
13635 /* create and start the task on a separate thread (note that it will not
13636 * start working until we release alock) */
13637 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13638 rc = pTask->createThread();
13639 if (FAILED(rc))
13640 return rc;
13641
13642 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13643 i_setMachineState(MachineState_Saving);
13644 i_updateMachineStateOnClient();
13645
13646 pProgress.queryInterfaceTo(aProgress.asOutParam());
13647
13648 return S_OK;
13649}
13650
13651/**
13652 * @note Locks this object for writing.
13653 */
13654HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13655{
13656 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13657
13658 HRESULT rc = i_checkStateDependency(MutableStateDep);
13659 if (FAILED(rc)) return rc;
13660
13661 if ( mData->mMachineState != MachineState_PoweredOff
13662 && mData->mMachineState != MachineState_Teleported
13663 && mData->mMachineState != MachineState_Aborted
13664 )
13665 return setError(VBOX_E_INVALID_VM_STATE,
13666 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13667 Global::stringifyMachineState(mData->mMachineState));
13668
13669 com::Utf8Str stateFilePathFull;
13670 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13671 if (RT_FAILURE(vrc))
13672 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13673 tr("Invalid saved state file path '%s' (%Rrc)"),
13674 aSavedStateFile.c_str(),
13675 vrc);
13676
13677 mSSData->strStateFilePath = stateFilePathFull;
13678
13679 /* The below i_setMachineState() will detect the state transition and will
13680 * update the settings file */
13681
13682 return i_setMachineState(MachineState_Saved);
13683}
13684
13685/**
13686 * @note Locks this object for writing.
13687 */
13688HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13689{
13690 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13691
13692 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13693 if (FAILED(rc)) return rc;
13694
13695 if ( mData->mMachineState != MachineState_Saved
13696 && mData->mMachineState != MachineState_AbortedSaved)
13697 return setError(VBOX_E_INVALID_VM_STATE,
13698 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13699 Global::stringifyMachineState(mData->mMachineState));
13700
13701 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13702
13703 /*
13704 * Saved -> PoweredOff transition will be detected in the SessionMachine
13705 * and properly handled.
13706 */
13707 rc = i_setMachineState(MachineState_PoweredOff);
13708 return rc;
13709}
13710
13711
13712/**
13713 * @note Locks the same as #i_setMachineState() does.
13714 */
13715HRESULT SessionMachine::updateState(MachineState_T aState)
13716{
13717 return i_setMachineState(aState);
13718}
13719
13720/**
13721 * @note Locks this object for writing.
13722 */
13723HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13724{
13725 IProgress *pProgress(aProgress);
13726
13727 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13728
13729 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13730
13731 if (mData->mSession.mState != SessionState_Locked)
13732 return VBOX_E_INVALID_OBJECT_STATE;
13733
13734 if (!mData->mSession.mProgress.isNull())
13735 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13736
13737 /* If we didn't reference the NAT network service yet, add a reference to
13738 * force a start */
13739 if (miNATNetworksStarted < 1)
13740 {
13741 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13742 {
13743 BOOL enabled;
13744 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13745 if ( FAILED(hrc)
13746 || !enabled)
13747 continue;
13748
13749 NetworkAttachmentType_T type;
13750 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13751 if ( SUCCEEDED(hrc)
13752 && type == NetworkAttachmentType_NATNetwork)
13753 {
13754 Bstr name;
13755 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13756 if (SUCCEEDED(hrc))
13757 {
13758 Utf8Str strName(name);
13759 LogRel(("VM '%s' starts using NAT network '%s'\n",
13760 mUserData->s.strName.c_str(), strName.c_str()));
13761 mPeer->lockHandle()->unlockWrite();
13762 mParent->i_natNetworkRefInc(strName);
13763#ifdef RT_LOCK_STRICT
13764 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13765#else
13766 mPeer->lockHandle()->lockWrite();
13767#endif
13768 }
13769 }
13770 }
13771 miNATNetworksStarted++;
13772 }
13773
13774 LogFlowThisFunc(("returns S_OK.\n"));
13775 return S_OK;
13776}
13777
13778/**
13779 * @note Locks this object for writing.
13780 */
13781HRESULT SessionMachine::endPowerUp(LONG aResult)
13782{
13783 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13784
13785 if (mData->mSession.mState != SessionState_Locked)
13786 return VBOX_E_INVALID_OBJECT_STATE;
13787
13788 /* Finalize the LaunchVMProcess progress object. */
13789 if (mData->mSession.mProgress)
13790 {
13791 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13792 mData->mSession.mProgress.setNull();
13793 }
13794
13795 if (SUCCEEDED((HRESULT)aResult))
13796 {
13797#ifdef VBOX_WITH_RESOURCE_USAGE_API
13798 /* The VM has been powered up successfully, so it makes sense
13799 * now to offer the performance metrics for a running machine
13800 * object. Doing it earlier wouldn't be safe. */
13801 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13802 mData->mSession.mPID);
13803#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13804 }
13805
13806 return S_OK;
13807}
13808
13809/**
13810 * @note Locks this object for writing.
13811 */
13812HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13813{
13814 LogFlowThisFuncEnter();
13815
13816 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13817
13818 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13819 E_FAIL);
13820
13821 /* create a progress object to track operation completion */
13822 ComObjPtr<Progress> pProgress;
13823 pProgress.createObject();
13824 pProgress->init(i_getVirtualBox(),
13825 static_cast<IMachine *>(this) /* aInitiator */,
13826 tr("Stopping the virtual machine"),
13827 FALSE /* aCancelable */);
13828
13829 /* fill in the console task data */
13830 mConsoleTaskData.mLastState = mData->mMachineState;
13831 mConsoleTaskData.mProgress = pProgress;
13832
13833 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13834 i_setMachineState(MachineState_Stopping);
13835
13836 pProgress.queryInterfaceTo(aProgress.asOutParam());
13837
13838 return S_OK;
13839}
13840
13841/**
13842 * @note Locks this object for writing.
13843 */
13844HRESULT SessionMachine::endPoweringDown(LONG aResult,
13845 const com::Utf8Str &aErrMsg)
13846{
13847 HRESULT const hrcResult = (HRESULT)aResult;
13848 LogFlowThisFuncEnter();
13849
13850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13851
13852 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13853 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13854 && mConsoleTaskData.mLastState != MachineState_Null,
13855 E_FAIL);
13856
13857 /*
13858 * On failure, set the state to the state we had when BeginPoweringDown()
13859 * was called (this is expected by Console::PowerDown() and the associated
13860 * task). On success the VM process already changed the state to
13861 * MachineState_PoweredOff, so no need to do anything.
13862 */
13863 if (FAILED(hrcResult))
13864 i_setMachineState(mConsoleTaskData.mLastState);
13865
13866 /* notify the progress object about operation completion */
13867 Assert(mConsoleTaskData.mProgress);
13868 if (SUCCEEDED(hrcResult))
13869 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13870 else
13871 {
13872 if (aErrMsg.length())
13873 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13874 COM_IIDOF(ISession),
13875 getComponentName(),
13876 aErrMsg.c_str());
13877 else
13878 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13879 }
13880
13881 /* clear out the temporary saved state data */
13882 mConsoleTaskData.mLastState = MachineState_Null;
13883 mConsoleTaskData.mProgress.setNull();
13884
13885 LogFlowThisFuncLeave();
13886 return S_OK;
13887}
13888
13889
13890/**
13891 * Goes through the USB filters of the given machine to see if the given
13892 * device matches any filter or not.
13893 *
13894 * @note Locks the same as USBController::hasMatchingFilter() does.
13895 */
13896HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13897 BOOL *aMatched,
13898 ULONG *aMaskedInterfaces)
13899{
13900 LogFlowThisFunc(("\n"));
13901
13902#ifdef VBOX_WITH_USB
13903 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13904#else
13905 NOREF(aDevice);
13906 NOREF(aMaskedInterfaces);
13907 *aMatched = FALSE;
13908#endif
13909
13910 return S_OK;
13911}
13912
13913/**
13914 * @note Locks the same as Host::captureUSBDevice() does.
13915 */
13916HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13917{
13918 LogFlowThisFunc(("\n"));
13919
13920#ifdef VBOX_WITH_USB
13921 /* if captureDeviceForVM() fails, it must have set extended error info */
13922 clearError();
13923 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13924 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13925 return rc;
13926
13927 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13928 AssertReturn(service, E_FAIL);
13929 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13930#else
13931 RT_NOREF(aId, aCaptureFilename);
13932 return E_NOTIMPL;
13933#endif
13934}
13935
13936/**
13937 * @note Locks the same as Host::detachUSBDevice() does.
13938 */
13939HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13940 BOOL aDone)
13941{
13942 LogFlowThisFunc(("\n"));
13943
13944#ifdef VBOX_WITH_USB
13945 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13946 AssertReturn(service, E_FAIL);
13947 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13948#else
13949 NOREF(aId);
13950 NOREF(aDone);
13951 return E_NOTIMPL;
13952#endif
13953}
13954
13955/**
13956 * Inserts all machine filters to the USB proxy service and then calls
13957 * Host::autoCaptureUSBDevices().
13958 *
13959 * Called by Console from the VM process upon VM startup.
13960 *
13961 * @note Locks what called methods lock.
13962 */
13963HRESULT SessionMachine::autoCaptureUSBDevices()
13964{
13965 LogFlowThisFunc(("\n"));
13966
13967#ifdef VBOX_WITH_USB
13968 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13969 AssertComRC(rc);
13970 NOREF(rc);
13971
13972 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13973 AssertReturn(service, E_FAIL);
13974 return service->autoCaptureDevicesForVM(this);
13975#else
13976 return S_OK;
13977#endif
13978}
13979
13980/**
13981 * Removes all machine filters from the USB proxy service and then calls
13982 * Host::detachAllUSBDevices().
13983 *
13984 * Called by Console from the VM process upon normal VM termination or by
13985 * SessionMachine::uninit() upon abnormal VM termination (from under the
13986 * Machine/SessionMachine lock).
13987 *
13988 * @note Locks what called methods lock.
13989 */
13990HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13991{
13992 LogFlowThisFunc(("\n"));
13993
13994#ifdef VBOX_WITH_USB
13995 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13996 AssertComRC(rc);
13997 NOREF(rc);
13998
13999 USBProxyService *service = mParent->i_host()->i_usbProxyService();
14000 AssertReturn(service, E_FAIL);
14001 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
14002#else
14003 NOREF(aDone);
14004 return S_OK;
14005#endif
14006}
14007
14008/**
14009 * @note Locks this object for writing.
14010 */
14011HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
14012 ComPtr<IProgress> &aProgress)
14013{
14014 LogFlowThisFuncEnter();
14015
14016 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
14017 /*
14018 * We don't assert below because it might happen that a non-direct session
14019 * informs us it is closed right after we've been uninitialized -- it's ok.
14020 */
14021
14022 /* get IInternalSessionControl interface */
14023 ComPtr<IInternalSessionControl> control(aSession);
14024
14025 ComAssertRet(!control.isNull(), E_INVALIDARG);
14026
14027 /* Creating a Progress object requires the VirtualBox lock, and
14028 * thus locking it here is required by the lock order rules. */
14029 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
14030
14031 if (control == mData->mSession.mDirectControl)
14032 {
14033 /* The direct session is being normally closed by the client process
14034 * ----------------------------------------------------------------- */
14035
14036 /* go to the closing state (essential for all open*Session() calls and
14037 * for #i_checkForDeath()) */
14038 Assert(mData->mSession.mState == SessionState_Locked);
14039 mData->mSession.mState = SessionState_Unlocking;
14040
14041 /* set direct control to NULL to release the remote instance */
14042 mData->mSession.mDirectControl.setNull();
14043 LogFlowThisFunc(("Direct control is set to NULL\n"));
14044
14045 if (mData->mSession.mProgress)
14046 {
14047 /* finalize the progress, someone might wait if a frontend
14048 * closes the session before powering on the VM. */
14049 mData->mSession.mProgress->notifyComplete(E_FAIL,
14050 COM_IIDOF(ISession),
14051 getComponentName(),
14052 tr("The VM session was closed before any attempt to power it on"));
14053 mData->mSession.mProgress.setNull();
14054 }
14055
14056 /* Create the progress object the client will use to wait until
14057 * #i_checkForDeath() is called to uninitialize this session object after
14058 * it releases the IPC semaphore.
14059 * Note! Because we're "reusing" mProgress here, this must be a proxy
14060 * object just like for LaunchVMProcess. */
14061 Assert(mData->mSession.mProgress.isNull());
14062 ComObjPtr<ProgressProxy> progress;
14063 progress.createObject();
14064 ComPtr<IUnknown> pPeer(mPeer);
14065 progress->init(mParent, pPeer,
14066 Bstr(tr("Closing session")).raw(),
14067 FALSE /* aCancelable */);
14068 progress.queryInterfaceTo(aProgress.asOutParam());
14069 mData->mSession.mProgress = progress;
14070 }
14071 else
14072 {
14073 /* the remote session is being normally closed */
14074 bool found = false;
14075 for (Data::Session::RemoteControlList::iterator
14076 it = mData->mSession.mRemoteControls.begin();
14077 it != mData->mSession.mRemoteControls.end();
14078 ++it)
14079 {
14080 if (control == *it)
14081 {
14082 found = true;
14083 // This MUST be erase(it), not remove(*it) as the latter
14084 // triggers a very nasty use after free due to the place where
14085 // the value "lives".
14086 mData->mSession.mRemoteControls.erase(it);
14087 break;
14088 }
14089 }
14090 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
14091 E_INVALIDARG);
14092 }
14093
14094 /* signal the client watcher thread, because the client is going away */
14095 mParent->i_updateClientWatcher();
14096
14097 LogFlowThisFuncLeave();
14098 return S_OK;
14099}
14100
14101HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14102 std::vector<com::Utf8Str> &aValues,
14103 std::vector<LONG64> &aTimestamps,
14104 std::vector<com::Utf8Str> &aFlags)
14105{
14106 LogFlowThisFunc(("\n"));
14107
14108#ifdef VBOX_WITH_GUEST_PROPS
14109 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14110
14111 size_t cEntries = mHWData->mGuestProperties.size();
14112 aNames.resize(cEntries);
14113 aValues.resize(cEntries);
14114 aTimestamps.resize(cEntries);
14115 aFlags.resize(cEntries);
14116
14117 size_t i = 0;
14118 for (HWData::GuestPropertyMap::const_iterator
14119 it = mHWData->mGuestProperties.begin();
14120 it != mHWData->mGuestProperties.end();
14121 ++it, ++i)
14122 {
14123 aNames[i] = it->first;
14124 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
14125 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
14126
14127 aValues[i] = it->second.strValue;
14128 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
14129 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
14130
14131 aTimestamps[i] = it->second.mTimestamp;
14132
14133 /* If it is NULL, keep it NULL. */
14134 if (it->second.mFlags)
14135 {
14136 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
14137 GuestPropWriteFlags(it->second.mFlags, szFlags);
14138 aFlags[i] = szFlags;
14139 }
14140 else
14141 aFlags[i] = "";
14142 }
14143 return S_OK;
14144#else
14145 ReturnComNotImplemented();
14146#endif
14147}
14148
14149HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
14150 const com::Utf8Str &aValue,
14151 LONG64 aTimestamp,
14152 const com::Utf8Str &aFlags,
14153 BOOL fWasDeleted)
14154{
14155 LogFlowThisFunc(("\n"));
14156
14157#ifdef VBOX_WITH_GUEST_PROPS
14158 try
14159 {
14160 /*
14161 * Convert input up front.
14162 */
14163 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
14164 if (aFlags.length())
14165 {
14166 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
14167 AssertRCReturn(vrc, E_INVALIDARG);
14168 }
14169
14170 /*
14171 * Now grab the object lock, validate the state and do the update.
14172 */
14173
14174 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14175
14176 if (!Global::IsOnline(mData->mMachineState))
14177 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
14178
14179 i_setModified(IsModified_MachineData);
14180 mHWData.backup();
14181
14182 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
14183 if (it != mHWData->mGuestProperties.end())
14184 {
14185 if (!fWasDeleted)
14186 {
14187 it->second.strValue = aValue;
14188 it->second.mTimestamp = aTimestamp;
14189 it->second.mFlags = fFlags;
14190 }
14191 else
14192 mHWData->mGuestProperties.erase(it);
14193
14194 mData->mGuestPropertiesModified = TRUE;
14195 }
14196 else if (!fWasDeleted)
14197 {
14198 HWData::GuestProperty prop;
14199 prop.strValue = aValue;
14200 prop.mTimestamp = aTimestamp;
14201 prop.mFlags = fFlags;
14202
14203 mHWData->mGuestProperties[aName] = prop;
14204 mData->mGuestPropertiesModified = TRUE;
14205 }
14206
14207 alock.release();
14208
14209 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fWasDeleted);
14210 }
14211 catch (...)
14212 {
14213 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
14214 }
14215 return S_OK;
14216#else
14217 ReturnComNotImplemented();
14218#endif
14219}
14220
14221
14222HRESULT SessionMachine::lockMedia()
14223{
14224 AutoMultiWriteLock2 alock(this->lockHandle(),
14225 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14226
14227 AssertReturn( mData->mMachineState == MachineState_Starting
14228 || mData->mMachineState == MachineState_Restoring
14229 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
14230
14231 clearError();
14232 alock.release();
14233 return i_lockMedia();
14234}
14235
14236HRESULT SessionMachine::unlockMedia()
14237{
14238 HRESULT hrc = i_unlockMedia();
14239 return hrc;
14240}
14241
14242HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14243 ComPtr<IMediumAttachment> &aNewAttachment)
14244{
14245 // request the host lock first, since might be calling Host methods for getting host drives;
14246 // next, protect the media tree all the while we're in here, as well as our member variables
14247 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
14248 this->lockHandle(),
14249 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14250
14251 IMediumAttachment *iAttach = aAttachment;
14252 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
14253
14254 Utf8Str ctrlName;
14255 LONG lPort;
14256 LONG lDevice;
14257 bool fTempEject;
14258 {
14259 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14260
14261 /* Need to query the details first, as the IMediumAttachment reference
14262 * might be to the original settings, which we are going to change. */
14263 ctrlName = pAttach->i_getControllerName();
14264 lPort = pAttach->i_getPort();
14265 lDevice = pAttach->i_getDevice();
14266 fTempEject = pAttach->i_getTempEject();
14267 }
14268
14269 if (!fTempEject)
14270 {
14271 /* Remember previously mounted medium. The medium before taking the
14272 * backup is not necessarily the same thing. */
14273 ComObjPtr<Medium> oldmedium;
14274 oldmedium = pAttach->i_getMedium();
14275
14276 i_setModified(IsModified_Storage);
14277 mMediumAttachments.backup();
14278
14279 // The backup operation makes the pAttach reference point to the
14280 // old settings. Re-get the correct reference.
14281 pAttach = i_findAttachment(*mMediumAttachments.data(),
14282 ctrlName,
14283 lPort,
14284 lDevice);
14285
14286 {
14287 AutoCaller autoAttachCaller(this);
14288 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
14289
14290 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14291 if (!oldmedium.isNull())
14292 oldmedium->i_removeBackReference(mData->mUuid);
14293
14294 pAttach->i_updateMedium(NULL);
14295 pAttach->i_updateEjected();
14296 }
14297
14298 i_setModified(IsModified_Storage);
14299 }
14300 else
14301 {
14302 {
14303 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14304 pAttach->i_updateEjected();
14305 }
14306 }
14307
14308 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
14309
14310 return S_OK;
14311}
14312
14313HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
14314 com::Utf8Str &aResult)
14315{
14316 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14317
14318 HRESULT hr = S_OK;
14319
14320 if (!mAuthLibCtx.hAuthLibrary)
14321 {
14322 /* Load the external authentication library. */
14323 Bstr authLibrary;
14324 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
14325
14326 Utf8Str filename = authLibrary;
14327
14328 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
14329 if (RT_FAILURE(vrc))
14330 hr = setErrorBoth(E_FAIL, vrc,
14331 tr("Could not load the external authentication library '%s' (%Rrc)"),
14332 filename.c_str(), vrc);
14333 }
14334
14335 /* The auth library might need the machine lock. */
14336 alock.release();
14337
14338 if (FAILED(hr))
14339 return hr;
14340
14341 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
14342 {
14343 enum VRDEAuthParams
14344 {
14345 parmUuid = 1,
14346 parmGuestJudgement,
14347 parmUser,
14348 parmPassword,
14349 parmDomain,
14350 parmClientId
14351 };
14352
14353 AuthResult result = AuthResultAccessDenied;
14354
14355 Guid uuid(aAuthParams[parmUuid]);
14356 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
14357 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
14358
14359 result = AuthLibAuthenticate(&mAuthLibCtx,
14360 uuid.raw(), guestJudgement,
14361 aAuthParams[parmUser].c_str(),
14362 aAuthParams[parmPassword].c_str(),
14363 aAuthParams[parmDomain].c_str(),
14364 u32ClientId);
14365
14366 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
14367 size_t cbPassword = aAuthParams[parmPassword].length();
14368 if (cbPassword)
14369 {
14370 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
14371 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
14372 }
14373
14374 if (result == AuthResultAccessGranted)
14375 aResult = "granted";
14376 else
14377 aResult = "denied";
14378
14379 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
14380 aAuthParams[parmUser].c_str(), aResult.c_str()));
14381 }
14382 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
14383 {
14384 enum VRDEAuthDisconnectParams
14385 {
14386 parmUuid = 1,
14387 parmClientId
14388 };
14389
14390 Guid uuid(aAuthParams[parmUuid]);
14391 uint32_t u32ClientId = 0;
14392 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
14393 }
14394 else
14395 {
14396 hr = E_INVALIDARG;
14397 }
14398
14399 return hr;
14400}
14401
14402// public methods only for internal purposes
14403/////////////////////////////////////////////////////////////////////////////
14404
14405#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14406/**
14407 * Called from the client watcher thread to check for expected or unexpected
14408 * death of the client process that has a direct session to this machine.
14409 *
14410 * On Win32 and on OS/2, this method is called only when we've got the
14411 * mutex (i.e. the client has either died or terminated normally) so it always
14412 * returns @c true (the client is terminated, the session machine is
14413 * uninitialized).
14414 *
14415 * On other platforms, the method returns @c true if the client process has
14416 * terminated normally or abnormally and the session machine was uninitialized,
14417 * and @c false if the client process is still alive.
14418 *
14419 * @note Locks this object for writing.
14420 */
14421bool SessionMachine::i_checkForDeath()
14422{
14423 Uninit::Reason reason;
14424 bool terminated = false;
14425
14426 /* Enclose autoCaller with a block because calling uninit() from under it
14427 * will deadlock. */
14428 {
14429 AutoCaller autoCaller(this);
14430 if (!autoCaller.isOk())
14431 {
14432 /* return true if not ready, to cause the client watcher to exclude
14433 * the corresponding session from watching */
14434 LogFlowThisFunc(("Already uninitialized!\n"));
14435 return true;
14436 }
14437
14438 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14439
14440 /* Determine the reason of death: if the session state is Closing here,
14441 * everything is fine. Otherwise it means that the client did not call
14442 * OnSessionEnd() before it released the IPC semaphore. This may happen
14443 * either because the client process has abnormally terminated, or
14444 * because it simply forgot to call ISession::Close() before exiting. We
14445 * threat the latter also as an abnormal termination (see
14446 * Session::uninit() for details). */
14447 reason = mData->mSession.mState == SessionState_Unlocking ?
14448 Uninit::Normal :
14449 Uninit::Abnormal;
14450
14451 if (mClientToken)
14452 terminated = mClientToken->release();
14453 } /* AutoCaller block */
14454
14455 if (terminated)
14456 uninit(reason);
14457
14458 return terminated;
14459}
14460
14461void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14462{
14463 LogFlowThisFunc(("\n"));
14464
14465 strTokenId.setNull();
14466
14467 AutoCaller autoCaller(this);
14468 AssertComRCReturnVoid(autoCaller.rc());
14469
14470 Assert(mClientToken);
14471 if (mClientToken)
14472 mClientToken->getId(strTokenId);
14473}
14474#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14475IToken *SessionMachine::i_getToken()
14476{
14477 LogFlowThisFunc(("\n"));
14478
14479 AutoCaller autoCaller(this);
14480 AssertComRCReturn(autoCaller.rc(), NULL);
14481
14482 Assert(mClientToken);
14483 if (mClientToken)
14484 return mClientToken->getToken();
14485 else
14486 return NULL;
14487}
14488#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14489
14490Machine::ClientToken *SessionMachine::i_getClientToken()
14491{
14492 LogFlowThisFunc(("\n"));
14493
14494 AutoCaller autoCaller(this);
14495 AssertComRCReturn(autoCaller.rc(), NULL);
14496
14497 return mClientToken;
14498}
14499
14500
14501/**
14502 * @note Locks this object for reading.
14503 */
14504HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14505{
14506 LogFlowThisFunc(("\n"));
14507
14508 AutoCaller autoCaller(this);
14509 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14510
14511 ComPtr<IInternalSessionControl> directControl;
14512 {
14513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14514 if (mData->mSession.mLockType == LockType_VM)
14515 directControl = mData->mSession.mDirectControl;
14516 }
14517
14518 /* ignore notifications sent after #OnSessionEnd() is called */
14519 if (!directControl)
14520 return S_OK;
14521
14522 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14523}
14524
14525/**
14526 * @note Locks this object for reading.
14527 */
14528HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
14529 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
14530 const Utf8Str &aGuestIp, LONG aGuestPort)
14531{
14532 LogFlowThisFunc(("\n"));
14533
14534 AutoCaller autoCaller(this);
14535 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14536
14537 ComPtr<IInternalSessionControl> directControl;
14538 {
14539 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14540 if (mData->mSession.mLockType == LockType_VM)
14541 directControl = mData->mSession.mDirectControl;
14542 }
14543
14544 /* ignore notifications sent after #OnSessionEnd() is called */
14545 if (!directControl)
14546 return S_OK;
14547 /*
14548 * instead acting like callback we ask IVirtualBox deliver corresponding event
14549 */
14550
14551 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14552 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14553 return S_OK;
14554}
14555
14556/**
14557 * @note Locks this object for reading.
14558 */
14559HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14560{
14561 LogFlowThisFunc(("\n"));
14562
14563 AutoCaller autoCaller(this);
14564 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14565
14566 ComPtr<IInternalSessionControl> directControl;
14567 {
14568 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14569 if (mData->mSession.mLockType == LockType_VM)
14570 directControl = mData->mSession.mDirectControl;
14571 }
14572
14573 /* ignore notifications sent after #OnSessionEnd() is called */
14574 if (!directControl)
14575 return S_OK;
14576
14577 return directControl->OnAudioAdapterChange(audioAdapter);
14578}
14579
14580/**
14581 * @note Locks this object for reading.
14582 */
14583HRESULT SessionMachine::i_onHostAudioDeviceChange(IHostAudioDevice *aDevice, BOOL aNew, AudioDeviceState_T aState, IVirtualBoxErrorInfo *aErrInfo)
14584{
14585 LogFlowThisFunc(("\n"));
14586
14587 AutoCaller autoCaller(this);
14588 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14589
14590 ComPtr<IInternalSessionControl> directControl;
14591 {
14592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14593 if (mData->mSession.mLockType == LockType_VM)
14594 directControl = mData->mSession.mDirectControl;
14595 }
14596
14597 /* ignore notifications sent after #OnSessionEnd() is called */
14598 if (!directControl)
14599 return S_OK;
14600
14601 return directControl->OnHostAudioDeviceChange(aDevice, aNew, aState, aErrInfo);
14602}
14603
14604/**
14605 * @note Locks this object for reading.
14606 */
14607HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14608{
14609 LogFlowThisFunc(("\n"));
14610
14611 AutoCaller autoCaller(this);
14612 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14613
14614 ComPtr<IInternalSessionControl> directControl;
14615 {
14616 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14617 if (mData->mSession.mLockType == LockType_VM)
14618 directControl = mData->mSession.mDirectControl;
14619 }
14620
14621 /* ignore notifications sent after #OnSessionEnd() is called */
14622 if (!directControl)
14623 return S_OK;
14624
14625 return directControl->OnSerialPortChange(serialPort);
14626}
14627
14628/**
14629 * @note Locks this object for reading.
14630 */
14631HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14632{
14633 LogFlowThisFunc(("\n"));
14634
14635 AutoCaller autoCaller(this);
14636 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14637
14638 ComPtr<IInternalSessionControl> directControl;
14639 {
14640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14641 if (mData->mSession.mLockType == LockType_VM)
14642 directControl = mData->mSession.mDirectControl;
14643 }
14644
14645 /* ignore notifications sent after #OnSessionEnd() is called */
14646 if (!directControl)
14647 return S_OK;
14648
14649 return directControl->OnParallelPortChange(parallelPort);
14650}
14651
14652/**
14653 * @note Locks this object for reading.
14654 */
14655HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14656{
14657 LogFlowThisFunc(("\n"));
14658
14659 AutoCaller autoCaller(this);
14660 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14661
14662 ComPtr<IInternalSessionControl> directControl;
14663 {
14664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14665 if (mData->mSession.mLockType == LockType_VM)
14666 directControl = mData->mSession.mDirectControl;
14667 }
14668
14669 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14670
14671 /* ignore notifications sent after #OnSessionEnd() is called */
14672 if (!directControl)
14673 return S_OK;
14674
14675 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14676}
14677
14678/**
14679 * @note Locks this object for reading.
14680 */
14681HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14682{
14683 LogFlowThisFunc(("\n"));
14684
14685 AutoCaller autoCaller(this);
14686 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14687
14688 ComPtr<IInternalSessionControl> directControl;
14689 {
14690 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14691 if (mData->mSession.mLockType == LockType_VM)
14692 directControl = mData->mSession.mDirectControl;
14693 }
14694
14695 mParent->i_onMediumChanged(aAttachment);
14696
14697 /* ignore notifications sent after #OnSessionEnd() is called */
14698 if (!directControl)
14699 return S_OK;
14700
14701 return directControl->OnMediumChange(aAttachment, aForce);
14702}
14703
14704HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14705{
14706 LogFlowThisFunc(("\n"));
14707
14708 AutoCaller autoCaller(this);
14709 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14710
14711 ComPtr<IInternalSessionControl> directControl;
14712 {
14713 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14714 if (mData->mSession.mLockType == LockType_VM)
14715 directControl = mData->mSession.mDirectControl;
14716 }
14717
14718 /* ignore notifications sent after #OnSessionEnd() is called */
14719 if (!directControl)
14720 return S_OK;
14721
14722 return directControl->OnVMProcessPriorityChange(aPriority);
14723}
14724
14725/**
14726 * @note Locks this object for reading.
14727 */
14728HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14729{
14730 LogFlowThisFunc(("\n"));
14731
14732 AutoCaller autoCaller(this);
14733 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14734
14735 ComPtr<IInternalSessionControl> directControl;
14736 {
14737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14738 if (mData->mSession.mLockType == LockType_VM)
14739 directControl = mData->mSession.mDirectControl;
14740 }
14741
14742 /* ignore notifications sent after #OnSessionEnd() is called */
14743 if (!directControl)
14744 return S_OK;
14745
14746 return directControl->OnCPUChange(aCPU, aRemove);
14747}
14748
14749HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14750{
14751 LogFlowThisFunc(("\n"));
14752
14753 AutoCaller autoCaller(this);
14754 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14755
14756 ComPtr<IInternalSessionControl> directControl;
14757 {
14758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14759 if (mData->mSession.mLockType == LockType_VM)
14760 directControl = mData->mSession.mDirectControl;
14761 }
14762
14763 /* ignore notifications sent after #OnSessionEnd() is called */
14764 if (!directControl)
14765 return S_OK;
14766
14767 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14768}
14769
14770/**
14771 * @note Locks this object for reading.
14772 */
14773HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14774{
14775 LogFlowThisFunc(("\n"));
14776
14777 AutoCaller autoCaller(this);
14778 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14779
14780 ComPtr<IInternalSessionControl> directControl;
14781 {
14782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14783 if (mData->mSession.mLockType == LockType_VM)
14784 directControl = mData->mSession.mDirectControl;
14785 }
14786
14787 /* ignore notifications sent after #OnSessionEnd() is called */
14788 if (!directControl)
14789 return S_OK;
14790
14791 return directControl->OnVRDEServerChange(aRestart);
14792}
14793
14794/**
14795 * @note Locks this object for reading.
14796 */
14797HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14798{
14799 LogFlowThisFunc(("\n"));
14800
14801 AutoCaller autoCaller(this);
14802 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14803
14804 ComPtr<IInternalSessionControl> directControl;
14805 {
14806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14807 if (mData->mSession.mLockType == LockType_VM)
14808 directControl = mData->mSession.mDirectControl;
14809 }
14810
14811 /* ignore notifications sent after #OnSessionEnd() is called */
14812 if (!directControl)
14813 return S_OK;
14814
14815 return directControl->OnRecordingChange(aEnable);
14816}
14817
14818/**
14819 * @note Locks this object for reading.
14820 */
14821HRESULT SessionMachine::i_onUSBControllerChange()
14822{
14823 LogFlowThisFunc(("\n"));
14824
14825 AutoCaller autoCaller(this);
14826 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14827
14828 ComPtr<IInternalSessionControl> directControl;
14829 {
14830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14831 if (mData->mSession.mLockType == LockType_VM)
14832 directControl = mData->mSession.mDirectControl;
14833 }
14834
14835 /* ignore notifications sent after #OnSessionEnd() is called */
14836 if (!directControl)
14837 return S_OK;
14838
14839 return directControl->OnUSBControllerChange();
14840}
14841
14842/**
14843 * @note Locks this object for reading.
14844 */
14845HRESULT SessionMachine::i_onSharedFolderChange()
14846{
14847 LogFlowThisFunc(("\n"));
14848
14849 AutoCaller autoCaller(this);
14850 AssertComRCReturnRC(autoCaller.rc());
14851
14852 ComPtr<IInternalSessionControl> directControl;
14853 {
14854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14855 if (mData->mSession.mLockType == LockType_VM)
14856 directControl = mData->mSession.mDirectControl;
14857 }
14858
14859 /* ignore notifications sent after #OnSessionEnd() is called */
14860 if (!directControl)
14861 return S_OK;
14862
14863 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14864}
14865
14866/**
14867 * @note Locks this object for reading.
14868 */
14869HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14870{
14871 LogFlowThisFunc(("\n"));
14872
14873 AutoCaller autoCaller(this);
14874 AssertComRCReturnRC(autoCaller.rc());
14875
14876 ComPtr<IInternalSessionControl> directControl;
14877 {
14878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14879 if (mData->mSession.mLockType == LockType_VM)
14880 directControl = mData->mSession.mDirectControl;
14881 }
14882
14883 /* ignore notifications sent after #OnSessionEnd() is called */
14884 if (!directControl)
14885 return S_OK;
14886
14887 return directControl->OnClipboardModeChange(aClipboardMode);
14888}
14889
14890/**
14891 * @note Locks this object for reading.
14892 */
14893HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14894{
14895 LogFlowThisFunc(("\n"));
14896
14897 AutoCaller autoCaller(this);
14898 AssertComRCReturnRC(autoCaller.rc());
14899
14900 ComPtr<IInternalSessionControl> directControl;
14901 {
14902 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14903 if (mData->mSession.mLockType == LockType_VM)
14904 directControl = mData->mSession.mDirectControl;
14905 }
14906
14907 /* ignore notifications sent after #OnSessionEnd() is called */
14908 if (!directControl)
14909 return S_OK;
14910
14911 return directControl->OnClipboardFileTransferModeChange(aEnable);
14912}
14913
14914/**
14915 * @note Locks this object for reading.
14916 */
14917HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14918{
14919 LogFlowThisFunc(("\n"));
14920
14921 AutoCaller autoCaller(this);
14922 AssertComRCReturnRC(autoCaller.rc());
14923
14924 ComPtr<IInternalSessionControl> directControl;
14925 {
14926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14927 if (mData->mSession.mLockType == LockType_VM)
14928 directControl = mData->mSession.mDirectControl;
14929 }
14930
14931 /* ignore notifications sent after #OnSessionEnd() is called */
14932 if (!directControl)
14933 return S_OK;
14934
14935 return directControl->OnDnDModeChange(aDnDMode);
14936}
14937
14938/**
14939 * @note Locks this object for reading.
14940 */
14941HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14942{
14943 LogFlowThisFunc(("\n"));
14944
14945 AutoCaller autoCaller(this);
14946 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14947
14948 ComPtr<IInternalSessionControl> directControl;
14949 {
14950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14951 if (mData->mSession.mLockType == LockType_VM)
14952 directControl = mData->mSession.mDirectControl;
14953 }
14954
14955 /* ignore notifications sent after #OnSessionEnd() is called */
14956 if (!directControl)
14957 return S_OK;
14958
14959 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14960}
14961
14962/**
14963 * @note Locks this object for reading.
14964 */
14965HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14966{
14967 LogFlowThisFunc(("\n"));
14968
14969 AutoCaller autoCaller(this);
14970 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14971
14972 ComPtr<IInternalSessionControl> directControl;
14973 {
14974 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14975 if (mData->mSession.mLockType == LockType_VM)
14976 directControl = mData->mSession.mDirectControl;
14977 }
14978
14979 /* ignore notifications sent after #OnSessionEnd() is called */
14980 if (!directControl)
14981 return S_OK;
14982
14983 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14984}
14985
14986/**
14987 * Returns @c true if this machine's USB controller reports it has a matching
14988 * filter for the given USB device and @c false otherwise.
14989 *
14990 * @note locks this object for reading.
14991 */
14992bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14993{
14994 AutoCaller autoCaller(this);
14995 /* silently return if not ready -- this method may be called after the
14996 * direct machine session has been called */
14997 if (!autoCaller.isOk())
14998 return false;
14999
15000#ifdef VBOX_WITH_USB
15001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15002
15003 switch (mData->mMachineState)
15004 {
15005 case MachineState_Starting:
15006 case MachineState_Restoring:
15007 case MachineState_TeleportingIn:
15008 case MachineState_Paused:
15009 case MachineState_Running:
15010 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
15011 * elsewhere... */
15012 alock.release();
15013 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
15014 default: break;
15015 }
15016#else
15017 NOREF(aDevice);
15018 NOREF(aMaskedIfs);
15019#endif
15020 return false;
15021}
15022
15023/**
15024 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
15025 */
15026HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
15027 IVirtualBoxErrorInfo *aError,
15028 ULONG aMaskedIfs,
15029 const com::Utf8Str &aCaptureFilename)
15030{
15031 LogFlowThisFunc(("\n"));
15032
15033 AutoCaller autoCaller(this);
15034
15035 /* This notification may happen after the machine object has been
15036 * uninitialized (the session was closed), so don't assert. */
15037 if (FAILED(autoCaller.rc())) return autoCaller.rc();
15038
15039 ComPtr<IInternalSessionControl> directControl;
15040 {
15041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15042 if (mData->mSession.mLockType == LockType_VM)
15043 directControl = mData->mSession.mDirectControl;
15044 }
15045
15046 /* fail on notifications sent after #OnSessionEnd() is called, it is
15047 * expected by the caller */
15048 if (!directControl)
15049 return E_FAIL;
15050
15051 /* No locks should be held at this point. */
15052 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
15053 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
15054
15055 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
15056}
15057
15058/**
15059 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
15060 */
15061HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
15062 IVirtualBoxErrorInfo *aError)
15063{
15064 LogFlowThisFunc(("\n"));
15065
15066 AutoCaller autoCaller(this);
15067
15068 /* This notification may happen after the machine object has been
15069 * uninitialized (the session was closed), so don't assert. */
15070 if (FAILED(autoCaller.rc())) return autoCaller.rc();
15071
15072 ComPtr<IInternalSessionControl> directControl;
15073 {
15074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15075 if (mData->mSession.mLockType == LockType_VM)
15076 directControl = mData->mSession.mDirectControl;
15077 }
15078
15079 /* fail on notifications sent after #OnSessionEnd() is called, it is
15080 * expected by the caller */
15081 if (!directControl)
15082 return E_FAIL;
15083
15084 /* No locks should be held at this point. */
15085 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
15086 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
15087
15088 return directControl->OnUSBDeviceDetach(aId, aError);
15089}
15090
15091// protected methods
15092/////////////////////////////////////////////////////////////////////////////
15093
15094/**
15095 * Deletes the given file if it is no longer in use by either the current machine state
15096 * (if the machine is "saved") or any of the machine's snapshots.
15097 *
15098 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
15099 * but is different for each SnapshotMachine. When calling this, the order of calling this
15100 * function on the one hand and changing that variable OR the snapshots tree on the other hand
15101 * is therefore critical. I know, it's all rather messy.
15102 *
15103 * @param strStateFile
15104 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
15105 * the test for whether the saved state file is in use.
15106 */
15107void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
15108 Snapshot *pSnapshotToIgnore)
15109{
15110 // it is safe to delete this saved state file if it is not currently in use by the machine ...
15111 if ( (strStateFile.isNotEmpty())
15112 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
15113 )
15114 // ... and it must also not be shared with other snapshots
15115 if ( !mData->mFirstSnapshot
15116 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
15117 // this checks the SnapshotMachine's state file paths
15118 )
15119 i_deleteFile(strStateFile, true /* fIgnoreFailures */);
15120}
15121
15122/**
15123 * Locks the attached media.
15124 *
15125 * All attached hard disks are locked for writing and DVD/floppy are locked for
15126 * reading. Parents of attached hard disks (if any) are locked for reading.
15127 *
15128 * This method also performs accessibility check of all media it locks: if some
15129 * media is inaccessible, the method will return a failure and a bunch of
15130 * extended error info objects per each inaccessible medium.
15131 *
15132 * Note that this method is atomic: if it returns a success, all media are
15133 * locked as described above; on failure no media is locked at all (all
15134 * succeeded individual locks will be undone).
15135 *
15136 * The caller is responsible for doing the necessary state sanity checks.
15137 *
15138 * The locks made by this method must be undone by calling #unlockMedia() when
15139 * no more needed.
15140 */
15141HRESULT SessionMachine::i_lockMedia()
15142{
15143 AutoCaller autoCaller(this);
15144 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15145
15146 AutoMultiWriteLock2 alock(this->lockHandle(),
15147 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
15148
15149 /* bail out if trying to lock things with already set up locking */
15150 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
15151
15152 MultiResult mrc(S_OK);
15153
15154 /* Collect locking information for all medium objects attached to the VM. */
15155 for (MediumAttachmentList::const_iterator
15156 it = mMediumAttachments->begin();
15157 it != mMediumAttachments->end();
15158 ++it)
15159 {
15160 MediumAttachment *pAtt = *it;
15161 DeviceType_T devType = pAtt->i_getType();
15162 Medium *pMedium = pAtt->i_getMedium();
15163
15164 MediumLockList *pMediumLockList(new MediumLockList());
15165 // There can be attachments without a medium (floppy/dvd), and thus
15166 // it's impossible to create a medium lock list. It still makes sense
15167 // to have the empty medium lock list in the map in case a medium is
15168 // attached later.
15169 if (pMedium != NULL)
15170 {
15171 MediumType_T mediumType = pMedium->i_getType();
15172 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
15173 || mediumType == MediumType_Shareable;
15174 bool fIsVitalImage = (devType == DeviceType_HardDisk);
15175
15176 alock.release();
15177 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
15178 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
15179 false /* fMediumLockWriteAll */,
15180 NULL,
15181 *pMediumLockList);
15182 alock.acquire();
15183 if (FAILED(mrc))
15184 {
15185 delete pMediumLockList;
15186 mData->mSession.mLockedMedia.Clear();
15187 break;
15188 }
15189 }
15190
15191 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
15192 if (FAILED(rc))
15193 {
15194 mData->mSession.mLockedMedia.Clear();
15195 mrc = setError(rc,
15196 tr("Collecting locking information for all attached media failed"));
15197 break;
15198 }
15199 }
15200
15201 if (SUCCEEDED(mrc))
15202 {
15203 /* Now lock all media. If this fails, nothing is locked. */
15204 alock.release();
15205 HRESULT rc = mData->mSession.mLockedMedia.Lock();
15206 alock.acquire();
15207 if (FAILED(rc))
15208 {
15209 mrc = setError(rc,
15210 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
15211 }
15212 }
15213
15214 return mrc;
15215}
15216
15217/**
15218 * Undoes the locks made by by #lockMedia().
15219 */
15220HRESULT SessionMachine::i_unlockMedia()
15221{
15222 AutoCaller autoCaller(this);
15223 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15224
15225 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15226
15227 /* we may be holding important error info on the current thread;
15228 * preserve it */
15229 ErrorInfoKeeper eik;
15230
15231 HRESULT rc = mData->mSession.mLockedMedia.Clear();
15232 AssertComRC(rc);
15233 return rc;
15234}
15235
15236/**
15237 * Helper to change the machine state (reimplementation).
15238 *
15239 * @note Locks this object for writing.
15240 * @note This method must not call i_saveSettings or SaveSettings, otherwise
15241 * it can cause crashes in random places due to unexpectedly committing
15242 * the current settings. The caller is responsible for that. The call
15243 * to saveStateSettings is fine, because this method does not commit.
15244 */
15245HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
15246{
15247 LogFlowThisFuncEnter();
15248
15249 AutoCaller autoCaller(this);
15250 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15251
15252 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15253
15254 MachineState_T oldMachineState = mData->mMachineState;
15255
15256 AssertMsgReturn(oldMachineState != aMachineState,
15257 ("oldMachineState=%s, aMachineState=%s\n",
15258 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
15259 E_FAIL);
15260
15261 HRESULT rc = S_OK;
15262
15263 int stsFlags = 0;
15264 bool deleteSavedState = false;
15265
15266 /* detect some state transitions */
15267
15268 if ( ( ( oldMachineState == MachineState_Saved
15269 || oldMachineState == MachineState_AbortedSaved
15270 )
15271 && aMachineState == MachineState_Restoring
15272 )
15273 || ( ( oldMachineState == MachineState_PoweredOff
15274 || oldMachineState == MachineState_Teleported
15275 || oldMachineState == MachineState_Aborted
15276 )
15277 && ( aMachineState == MachineState_TeleportingIn
15278 || aMachineState == MachineState_Starting
15279 )
15280 )
15281 )
15282 {
15283 /* The EMT thread is about to start */
15284
15285 /* Nothing to do here for now... */
15286
15287 /// @todo NEWMEDIA don't let mDVDDrive and other children
15288 /// change anything when in the Starting/Restoring state
15289 }
15290 else if ( ( oldMachineState == MachineState_Running
15291 || oldMachineState == MachineState_Paused
15292 || oldMachineState == MachineState_Teleporting
15293 || oldMachineState == MachineState_OnlineSnapshotting
15294 || oldMachineState == MachineState_LiveSnapshotting
15295 || oldMachineState == MachineState_Stuck
15296 || oldMachineState == MachineState_Starting
15297 || oldMachineState == MachineState_Stopping
15298 || oldMachineState == MachineState_Saving
15299 || oldMachineState == MachineState_Restoring
15300 || oldMachineState == MachineState_TeleportingPausedVM
15301 || oldMachineState == MachineState_TeleportingIn
15302 )
15303 && ( aMachineState == MachineState_PoweredOff
15304 || aMachineState == MachineState_Saved
15305 || aMachineState == MachineState_Teleported
15306 || aMachineState == MachineState_Aborted
15307 || aMachineState == MachineState_AbortedSaved
15308 )
15309 )
15310 {
15311 /* The EMT thread has just stopped, unlock attached media. Note that as
15312 * opposed to locking that is done from Console, we do unlocking here
15313 * because the VM process may have aborted before having a chance to
15314 * properly unlock all media it locked. */
15315
15316 unlockMedia();
15317 }
15318
15319 if (oldMachineState == MachineState_Restoring)
15320 {
15321 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
15322 {
15323 /*
15324 * delete the saved state file once the machine has finished
15325 * restoring from it (note that Console sets the state from
15326 * Restoring to AbortedSaved if the VM couldn't restore successfully,
15327 * to give the user an ability to fix an error and retry --
15328 * we keep the saved state file in this case)
15329 */
15330 deleteSavedState = true;
15331 }
15332 }
15333 else if ( oldMachineState == MachineState_Saved
15334 && ( aMachineState == MachineState_PoweredOff
15335 || aMachineState == MachineState_Teleported
15336 )
15337 )
15338 {
15339 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
15340 deleteSavedState = true;
15341 mData->mCurrentStateModified = TRUE;
15342 stsFlags |= SaveSTS_CurStateModified;
15343 }
15344 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
15345 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
15346
15347 if ( aMachineState == MachineState_Starting
15348 || aMachineState == MachineState_Restoring
15349 || aMachineState == MachineState_TeleportingIn
15350 )
15351 {
15352 /* set the current state modified flag to indicate that the current
15353 * state is no more identical to the state in the
15354 * current snapshot */
15355 if (!mData->mCurrentSnapshot.isNull())
15356 {
15357 mData->mCurrentStateModified = TRUE;
15358 stsFlags |= SaveSTS_CurStateModified;
15359 }
15360 }
15361
15362 if (deleteSavedState)
15363 {
15364 if (mRemoveSavedState)
15365 {
15366 Assert(!mSSData->strStateFilePath.isEmpty());
15367
15368 // it is safe to delete the saved state file if ...
15369 if ( !mData->mFirstSnapshot // ... we have no snapshots or
15370 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
15371 // ... none of the snapshots share the saved state file
15372 )
15373 i_deleteFile(mSSData->strStateFilePath, true /* fIgnoreFailures */);
15374 }
15375
15376 mSSData->strStateFilePath.setNull();
15377 stsFlags |= SaveSTS_StateFilePath;
15378 }
15379
15380 /* redirect to the underlying peer machine */
15381 mPeer->i_setMachineState(aMachineState);
15382
15383 if ( oldMachineState != MachineState_RestoringSnapshot
15384 && ( aMachineState == MachineState_PoweredOff
15385 || aMachineState == MachineState_Teleported
15386 || aMachineState == MachineState_Aborted
15387 || aMachineState == MachineState_AbortedSaved
15388 || aMachineState == MachineState_Saved))
15389 {
15390 /* the machine has stopped execution
15391 * (or the saved state file was adopted) */
15392 stsFlags |= SaveSTS_StateTimeStamp;
15393 }
15394
15395 if ( ( oldMachineState == MachineState_PoweredOff
15396 || oldMachineState == MachineState_Aborted
15397 || oldMachineState == MachineState_Teleported
15398 )
15399 && aMachineState == MachineState_Saved)
15400 {
15401 /* the saved state file was adopted */
15402 Assert(!mSSData->strStateFilePath.isEmpty());
15403 stsFlags |= SaveSTS_StateFilePath;
15404 }
15405
15406#ifdef VBOX_WITH_GUEST_PROPS
15407 if ( aMachineState == MachineState_PoweredOff
15408 || aMachineState == MachineState_Aborted
15409 || aMachineState == MachineState_Teleported)
15410 {
15411 /* Make sure any transient guest properties get removed from the
15412 * property store on shutdown. */
15413 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
15414
15415 /* remove it from the settings representation */
15416 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
15417 for (settings::GuestPropertiesList::iterator
15418 it = llGuestProperties.begin();
15419 it != llGuestProperties.end();
15420 /*nothing*/)
15421 {
15422 const settings::GuestProperty &prop = *it;
15423 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
15424 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
15425 {
15426 it = llGuestProperties.erase(it);
15427 fNeedsSaving = true;
15428 }
15429 else
15430 {
15431 ++it;
15432 }
15433 }
15434
15435 /* Additionally remove it from the HWData representation. Required to
15436 * keep everything in sync, as this is what the API keeps using. */
15437 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
15438 for (HWData::GuestPropertyMap::iterator
15439 it = llHWGuestProperties.begin();
15440 it != llHWGuestProperties.end();
15441 /*nothing*/)
15442 {
15443 uint32_t fFlags = it->second.mFlags;
15444 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
15445 {
15446 /* iterator where we need to continue after the erase call
15447 * (C++03 is a fact still, and it doesn't return the iterator
15448 * which would allow continuing) */
15449 HWData::GuestPropertyMap::iterator it2 = it;
15450 ++it2;
15451 llHWGuestProperties.erase(it);
15452 it = it2;
15453 fNeedsSaving = true;
15454 }
15455 else
15456 {
15457 ++it;
15458 }
15459 }
15460
15461 if (fNeedsSaving)
15462 {
15463 mData->mCurrentStateModified = TRUE;
15464 stsFlags |= SaveSTS_CurStateModified;
15465 }
15466 }
15467#endif /* VBOX_WITH_GUEST_PROPS */
15468
15469 rc = i_saveStateSettings(stsFlags);
15470
15471 if ( ( oldMachineState != MachineState_PoweredOff
15472 && oldMachineState != MachineState_Aborted
15473 && oldMachineState != MachineState_Teleported
15474 )
15475 && ( aMachineState == MachineState_PoweredOff
15476 || aMachineState == MachineState_Aborted
15477 || aMachineState == MachineState_Teleported
15478 )
15479 )
15480 {
15481 /* we've been shut down for any reason */
15482 /* no special action so far */
15483 }
15484
15485 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, ::stringifyMachineState(mData->mMachineState) ));
15486 LogFlowThisFuncLeave();
15487 return rc;
15488}
15489
15490/**
15491 * Sends the current machine state value to the VM process.
15492 *
15493 * @note Locks this object for reading, then calls a client process.
15494 */
15495HRESULT SessionMachine::i_updateMachineStateOnClient()
15496{
15497 AutoCaller autoCaller(this);
15498 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15499
15500 ComPtr<IInternalSessionControl> directControl;
15501 {
15502 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15503 AssertReturn(!!mData, E_FAIL);
15504 if (mData->mSession.mLockType == LockType_VM)
15505 directControl = mData->mSession.mDirectControl;
15506
15507 /* directControl may be already set to NULL here in #OnSessionEnd()
15508 * called too early by the direct session process while there is still
15509 * some operation (like deleting the snapshot) in progress. The client
15510 * process in this case is waiting inside Session::close() for the
15511 * "end session" process object to complete, while #uninit() called by
15512 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15513 * operation to complete. For now, we accept this inconsistent behavior
15514 * and simply do nothing here. */
15515
15516 if (mData->mSession.mState == SessionState_Unlocking)
15517 return S_OK;
15518 }
15519
15520 /* ignore notifications sent after #OnSessionEnd() is called */
15521 if (!directControl)
15522 return S_OK;
15523
15524 return directControl->UpdateMachineState(mData->mMachineState);
15525}
15526
15527
15528/*static*/
15529HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15530{
15531 va_list args;
15532 va_start(args, pcszMsg);
15533 HRESULT rc = setErrorInternalV(aResultCode,
15534 getStaticClassIID(),
15535 getStaticComponentName(),
15536 pcszMsg, args,
15537 false /* aWarning */,
15538 true /* aLogIt */);
15539 va_end(args);
15540 return rc;
15541}
15542
15543
15544HRESULT Machine::updateState(MachineState_T aState)
15545{
15546 NOREF(aState);
15547 ReturnComNotImplemented();
15548}
15549
15550HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15551{
15552 NOREF(aProgress);
15553 ReturnComNotImplemented();
15554}
15555
15556HRESULT Machine::endPowerUp(LONG aResult)
15557{
15558 NOREF(aResult);
15559 ReturnComNotImplemented();
15560}
15561
15562HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15563{
15564 NOREF(aProgress);
15565 ReturnComNotImplemented();
15566}
15567
15568HRESULT Machine::endPoweringDown(LONG aResult,
15569 const com::Utf8Str &aErrMsg)
15570{
15571 NOREF(aResult);
15572 NOREF(aErrMsg);
15573 ReturnComNotImplemented();
15574}
15575
15576HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15577 BOOL *aMatched,
15578 ULONG *aMaskedInterfaces)
15579{
15580 NOREF(aDevice);
15581 NOREF(aMatched);
15582 NOREF(aMaskedInterfaces);
15583 ReturnComNotImplemented();
15584
15585}
15586
15587HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15588{
15589 NOREF(aId); NOREF(aCaptureFilename);
15590 ReturnComNotImplemented();
15591}
15592
15593HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15594 BOOL aDone)
15595{
15596 NOREF(aId);
15597 NOREF(aDone);
15598 ReturnComNotImplemented();
15599}
15600
15601HRESULT Machine::autoCaptureUSBDevices()
15602{
15603 ReturnComNotImplemented();
15604}
15605
15606HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15607{
15608 NOREF(aDone);
15609 ReturnComNotImplemented();
15610}
15611
15612HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15613 ComPtr<IProgress> &aProgress)
15614{
15615 NOREF(aSession);
15616 NOREF(aProgress);
15617 ReturnComNotImplemented();
15618}
15619
15620HRESULT Machine::finishOnlineMergeMedium()
15621{
15622 ReturnComNotImplemented();
15623}
15624
15625HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15626 std::vector<com::Utf8Str> &aValues,
15627 std::vector<LONG64> &aTimestamps,
15628 std::vector<com::Utf8Str> &aFlags)
15629{
15630 NOREF(aNames);
15631 NOREF(aValues);
15632 NOREF(aTimestamps);
15633 NOREF(aFlags);
15634 ReturnComNotImplemented();
15635}
15636
15637HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15638 const com::Utf8Str &aValue,
15639 LONG64 aTimestamp,
15640 const com::Utf8Str &aFlags,
15641 BOOL fWasDeleted)
15642{
15643 NOREF(aName);
15644 NOREF(aValue);
15645 NOREF(aTimestamp);
15646 NOREF(aFlags);
15647 NOREF(fWasDeleted);
15648 ReturnComNotImplemented();
15649}
15650
15651HRESULT Machine::lockMedia()
15652{
15653 ReturnComNotImplemented();
15654}
15655
15656HRESULT Machine::unlockMedia()
15657{
15658 ReturnComNotImplemented();
15659}
15660
15661HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15662 ComPtr<IMediumAttachment> &aNewAttachment)
15663{
15664 NOREF(aAttachment);
15665 NOREF(aNewAttachment);
15666 ReturnComNotImplemented();
15667}
15668
15669HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15670 ULONG aCpuUser,
15671 ULONG aCpuKernel,
15672 ULONG aCpuIdle,
15673 ULONG aMemTotal,
15674 ULONG aMemFree,
15675 ULONG aMemBalloon,
15676 ULONG aMemShared,
15677 ULONG aMemCache,
15678 ULONG aPagedTotal,
15679 ULONG aMemAllocTotal,
15680 ULONG aMemFreeTotal,
15681 ULONG aMemBalloonTotal,
15682 ULONG aMemSharedTotal,
15683 ULONG aVmNetRx,
15684 ULONG aVmNetTx)
15685{
15686 NOREF(aValidStats);
15687 NOREF(aCpuUser);
15688 NOREF(aCpuKernel);
15689 NOREF(aCpuIdle);
15690 NOREF(aMemTotal);
15691 NOREF(aMemFree);
15692 NOREF(aMemBalloon);
15693 NOREF(aMemShared);
15694 NOREF(aMemCache);
15695 NOREF(aPagedTotal);
15696 NOREF(aMemAllocTotal);
15697 NOREF(aMemFreeTotal);
15698 NOREF(aMemBalloonTotal);
15699 NOREF(aMemSharedTotal);
15700 NOREF(aVmNetRx);
15701 NOREF(aVmNetTx);
15702 ReturnComNotImplemented();
15703}
15704
15705HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15706 com::Utf8Str &aResult)
15707{
15708 NOREF(aAuthParams);
15709 NOREF(aResult);
15710 ReturnComNotImplemented();
15711}
15712
15713com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15714{
15715 com::Utf8Str strControllerName = "Unknown";
15716 switch (aBusType)
15717 {
15718 case StorageBus_IDE:
15719 {
15720 strControllerName = "IDE";
15721 break;
15722 }
15723 case StorageBus_SATA:
15724 {
15725 strControllerName = "SATA";
15726 break;
15727 }
15728 case StorageBus_SCSI:
15729 {
15730 strControllerName = "SCSI";
15731 break;
15732 }
15733 case StorageBus_Floppy:
15734 {
15735 strControllerName = "Floppy";
15736 break;
15737 }
15738 case StorageBus_SAS:
15739 {
15740 strControllerName = "SAS";
15741 break;
15742 }
15743 case StorageBus_USB:
15744 {
15745 strControllerName = "USB";
15746 break;
15747 }
15748 default:
15749 break;
15750 }
15751 return strControllerName;
15752}
15753
15754HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15755{
15756 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15757
15758 AutoCaller autoCaller(this);
15759 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15760
15761 HRESULT rc = S_OK;
15762
15763 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15764 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15765 rc = getUSBDeviceFilters(usbDeviceFilters);
15766 if (FAILED(rc)) return rc;
15767
15768 NOREF(aFlags);
15769 com::Utf8Str osTypeId;
15770 ComObjPtr<GuestOSType> osType = NULL;
15771
15772 /* Get the guest os type as a string from the VB. */
15773 rc = getOSTypeId(osTypeId);
15774 if (FAILED(rc)) return rc;
15775
15776 /* Get the os type obj that coresponds, can be used to get
15777 * the defaults for this guest OS. */
15778 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15779 if (FAILED(rc)) return rc;
15780
15781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15782
15783 /* Let the OS type select 64-bit ness. */
15784 mHWData->mLongMode = osType->i_is64Bit()
15785 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15786
15787 /* Let the OS type enable the X2APIC */
15788 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15789
15790 /* This one covers IOAPICEnabled. */
15791 mBIOSSettings->i_applyDefaults(osType);
15792
15793 /* Initialize default record settings. */
15794 mRecordingSettings->i_applyDefaults();
15795
15796 /* Initialize default BIOS settings here */
15797 /* Hardware virtualization must be ON by default */
15798 mHWData->mAPIC = true;
15799 mHWData->mHWVirtExEnabled = true;
15800
15801 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15802 if (FAILED(rc)) return rc;
15803
15804 rc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15805 if (FAILED(rc)) return rc;
15806
15807 /* Graphics stuff. */
15808 GraphicsControllerType_T graphicsController;
15809 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15810 if (FAILED(rc)) return rc;
15811
15812 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15813 if (FAILED(rc)) return rc;
15814
15815 ULONG vramSize;
15816 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15817 if (FAILED(rc)) return rc;
15818
15819 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15820 if (FAILED(rc)) return rc;
15821
15822 BOOL fAccelerate2DVideoEnabled;
15823 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15824 if (FAILED(rc)) return rc;
15825
15826 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15827 if (FAILED(rc)) return rc;
15828
15829 BOOL fAccelerate3DEnabled;
15830 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15831 if (FAILED(rc)) return rc;
15832
15833 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15834 if (FAILED(rc)) return rc;
15835
15836 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15837 if (FAILED(rc)) return rc;
15838
15839 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15840 if (FAILED(rc)) return rc;
15841
15842 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15843 if (FAILED(rc)) return rc;
15844
15845 BOOL mRTCUseUTC;
15846 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15847 if (FAILED(rc)) return rc;
15848
15849 setRTCUseUTC(mRTCUseUTC);
15850 if (FAILED(rc)) return rc;
15851
15852 /* the setter does more than just the assignment, so use it */
15853 ChipsetType_T enmChipsetType;
15854 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15855 if (FAILED(rc)) return rc;
15856
15857 rc = COMSETTER(ChipsetType)(enmChipsetType);
15858 if (FAILED(rc)) return rc;
15859
15860 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15861 if (FAILED(rc)) return rc;
15862
15863 /* Apply IOMMU defaults. */
15864 IommuType_T enmIommuType;
15865 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15866 if (FAILED(rc)) return rc;
15867
15868 rc = COMSETTER(IommuType)(enmIommuType);
15869 if (FAILED(rc)) return rc;
15870
15871 /* Apply network adapters defaults */
15872 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15873 mNetworkAdapters[slot]->i_applyDefaults(osType);
15874
15875 /* Apply serial port defaults */
15876 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15877 mSerialPorts[slot]->i_applyDefaults(osType);
15878
15879 /* Apply parallel port defaults - not OS dependent*/
15880 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15881 mParallelPorts[slot]->i_applyDefaults();
15882
15883 /* This one covers the TPM type. */
15884 mTrustedPlatformModule->i_applyDefaults(osType);
15885
15886 /* This one covers secure boot. */
15887 rc = mNvramStore->i_applyDefaults(osType);
15888 if (FAILED(rc)) return rc;
15889
15890 /* Audio stuff. */
15891 rc = mAudioSettings->i_applyDefaults(osType);
15892 if (FAILED(rc)) return rc;
15893
15894 /* Storage Controllers */
15895 StorageControllerType_T hdStorageControllerType;
15896 StorageBus_T hdStorageBusType;
15897 StorageControllerType_T dvdStorageControllerType;
15898 StorageBus_T dvdStorageBusType;
15899 BOOL recommendedFloppy;
15900 ComPtr<IStorageController> floppyController;
15901 ComPtr<IStorageController> hdController;
15902 ComPtr<IStorageController> dvdController;
15903 Utf8Str strFloppyName, strDVDName, strHDName;
15904
15905 /* GUI auto generates controller names using bus type. Do the same*/
15906 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15907
15908 /* Floppy recommended? add one. */
15909 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15910 if (FAILED(rc)) return rc;
15911 if (recommendedFloppy)
15912 {
15913 rc = addStorageController(strFloppyName,
15914 StorageBus_Floppy,
15915 floppyController);
15916 if (FAILED(rc)) return rc;
15917 }
15918
15919 /* Setup one DVD storage controller. */
15920 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15921 if (FAILED(rc)) return rc;
15922
15923 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15924 if (FAILED(rc)) return rc;
15925
15926 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15927
15928 rc = addStorageController(strDVDName,
15929 dvdStorageBusType,
15930 dvdController);
15931 if (FAILED(rc)) return rc;
15932
15933 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15934 if (FAILED(rc)) return rc;
15935
15936 /* Setup one HDD storage controller. */
15937 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15938 if (FAILED(rc)) return rc;
15939
15940 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15941 if (FAILED(rc)) return rc;
15942
15943 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15944
15945 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15946 {
15947 rc = addStorageController(strHDName,
15948 hdStorageBusType,
15949 hdController);
15950 if (FAILED(rc)) return rc;
15951
15952 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15953 if (FAILED(rc)) return rc;
15954 }
15955 else
15956 {
15957 /* The HD controller is the same as DVD: */
15958 hdController = dvdController;
15959 }
15960
15961 /* Limit the AHCI port count if it's used because windows has trouble with
15962 * too many ports and other guest (OS X in particular) may take extra long
15963 * boot: */
15964
15965 // pParent = static_cast<Medium*>(aP)
15966 IStorageController *temp = hdController;
15967 ComObjPtr<StorageController> storageController;
15968 storageController = static_cast<StorageController *>(temp);
15969
15970 // tempHDController = aHDController;
15971 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15972 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15973 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15974 storageController->COMSETTER(PortCount)(1);
15975
15976 /* USB stuff */
15977
15978 bool ohciEnabled = false;
15979
15980 ComPtr<IUSBController> usbController;
15981 BOOL recommendedUSB3;
15982 BOOL recommendedUSB;
15983 BOOL usbProxyAvailable;
15984
15985 getUSBProxyAvailable(&usbProxyAvailable);
15986 if (FAILED(rc)) return rc;
15987
15988 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15989 if (FAILED(rc)) return rc;
15990 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15991 if (FAILED(rc)) return rc;
15992
15993 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15994 {
15995#ifdef VBOX_WITH_EXTPACK
15996 /* USB 3.0 is only available if the proper ExtPack is installed. */
15997 ExtPackManager *aManager = mParent->i_getExtPackManager();
15998 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15999 {
16000 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
16001 if (FAILED(rc)) return rc;
16002
16003 /* xHci includes OHCI */
16004 ohciEnabled = true;
16005 }
16006#endif
16007 }
16008 if ( !ohciEnabled
16009 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
16010 {
16011 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16012 if (FAILED(rc)) return rc;
16013 ohciEnabled = true;
16014
16015#ifdef VBOX_WITH_EXTPACK
16016 /* USB 2.0 is only available if the proper ExtPack is installed.
16017 * Note. Configuring EHCI here and providing messages about
16018 * the missing extpack isn't exactly clean, but it is a
16019 * necessary evil to patch over legacy compatability issues
16020 * introduced by the new distribution model. */
16021 ExtPackManager *manager = mParent->i_getExtPackManager();
16022 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
16023 {
16024 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
16025 if (FAILED(rc)) return rc;
16026 }
16027#endif
16028 }
16029
16030 /* Set recommended human interface device types: */
16031 BOOL recommendedUSBHID;
16032 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
16033 if (FAILED(rc)) return rc;
16034
16035 if (recommendedUSBHID)
16036 {
16037 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
16038 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
16039 if (!ohciEnabled && !usbDeviceFilters.isNull())
16040 {
16041 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16042 if (FAILED(rc)) return rc;
16043 }
16044 }
16045
16046 BOOL recommendedUSBTablet;
16047 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
16048 if (FAILED(rc)) return rc;
16049
16050 if (recommendedUSBTablet)
16051 {
16052 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
16053 if (!ohciEnabled && !usbDeviceFilters.isNull())
16054 {
16055 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16056 if (FAILED(rc)) return rc;
16057 }
16058 }
16059
16060 /* Enable the VMMDev testing feature for bootsector VMs: */
16061 if (osTypeId == "VBoxBS_64")
16062 {
16063 rc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
16064 if (FAILED(rc))
16065 return rc;
16066 }
16067
16068 return S_OK;
16069}
16070
16071#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
16072/**
16073 * Task record for change encryption settins.
16074 */
16075class Machine::ChangeEncryptionTask
16076 : public Machine::Task
16077{
16078public:
16079 ChangeEncryptionTask(Machine *m,
16080 Progress *p,
16081 const Utf8Str &t,
16082 const com::Utf8Str &aCurrentPassword,
16083 const com::Utf8Str &aCipher,
16084 const com::Utf8Str &aNewPassword,
16085 const com::Utf8Str &aNewPasswordId,
16086 const BOOL aForce,
16087 const MediaList &llMedia)
16088 : Task(m, p, t),
16089 mstrNewPassword(aNewPassword),
16090 mstrCurrentPassword(aCurrentPassword),
16091 mstrCipher(aCipher),
16092 mstrNewPasswordId(aNewPasswordId),
16093 mForce(aForce),
16094 mllMedia(llMedia)
16095 {}
16096
16097 ~ChangeEncryptionTask()
16098 {
16099 if (mstrNewPassword.length())
16100 RTMemWipeThoroughly(mstrNewPassword.mutableRaw(), mstrNewPassword.length(), 10 /* cPasses */);
16101 if (mstrCurrentPassword.length())
16102 RTMemWipeThoroughly(mstrCurrentPassword.mutableRaw(), mstrCurrentPassword.length(), 10 /* cPasses */);
16103 if (m_pCryptoIf)
16104 {
16105 m_pMachine->i_getVirtualBox()->i_releaseCryptoIf(m_pCryptoIf);
16106 m_pCryptoIf = NULL;
16107 }
16108 }
16109
16110 Utf8Str mstrNewPassword;
16111 Utf8Str mstrCurrentPassword;
16112 Utf8Str mstrCipher;
16113 Utf8Str mstrNewPasswordId;
16114 BOOL mForce;
16115 MediaList mllMedia;
16116 PCVBOXCRYPTOIF m_pCryptoIf;
16117private:
16118 void handler()
16119 {
16120 try
16121 {
16122 m_pMachine->i_changeEncryptionHandler(*this);
16123 }
16124 catch (...)
16125 {
16126 LogRel(("Some exception in the function Machine::i_changeEncryptionHandler()\n"));
16127 }
16128 }
16129
16130 friend void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task);
16131};
16132
16133/**
16134 * Scans specified directory and fills list by files found
16135 *
16136 * @returns VBox status code.
16137 * @param lstFiles
16138 * @param strDir
16139 * @param filePattern
16140 */
16141int Machine::i_findFiles(std::list<com::Utf8Str> &lstFiles, const com::Utf8Str &strDir,
16142 const com::Utf8Str &strPattern)
16143{
16144 /* To get all entries including subdirectories. */
16145 char *pszFilePattern = RTPathJoinA(strDir.c_str(), "*");
16146 if (!pszFilePattern)
16147 return VERR_NO_STR_MEMORY;
16148
16149 PRTDIRENTRYEX pDirEntry = NULL;
16150 RTDIR hDir;
16151 size_t cbDirEntry = sizeof(RTDIRENTRYEX);
16152 int rc = RTDirOpenFiltered(&hDir, pszFilePattern, RTDIRFILTER_WINNT, 0 /*fFlags*/);
16153 if (RT_SUCCESS(rc))
16154 {
16155 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
16156 if (pDirEntry)
16157 {
16158 while ( (rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK))
16159 != VERR_NO_MORE_FILES)
16160 {
16161 char *pszFilePath = NULL;
16162
16163 if (rc == VERR_BUFFER_OVERFLOW)
16164 {
16165 /* allocate new buffer. */
16166 RTMemFree(pDirEntry);
16167 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbDirEntry);
16168 if (!pDirEntry)
16169 {
16170 rc = VERR_NO_MEMORY;
16171 break;
16172 }
16173 /* Retry. */
16174 rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
16175 if (RT_FAILURE(rc))
16176 break;
16177 }
16178 else if (RT_FAILURE(rc))
16179 break;
16180
16181 /* Exclude . and .. */
16182 if ( (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '\0')
16183 || (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '.' && pDirEntry->szName[2] == '\0'))
16184 continue;
16185 if (RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode))
16186 {
16187 char *pszSubDirPath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
16188 if (!pszSubDirPath)
16189 {
16190 rc = VERR_NO_STR_MEMORY;
16191 break;
16192 }
16193 rc = i_findFiles(lstFiles, pszSubDirPath, strPattern);
16194 RTMemFree(pszSubDirPath);
16195 if (RT_FAILURE(rc))
16196 break;
16197 continue;
16198 }
16199
16200 /* We got the new entry. */
16201 if (!RTFS_IS_FILE(pDirEntry->Info.Attr.fMode))
16202 continue;
16203
16204 if (!RTStrSimplePatternMatch(strPattern.c_str(), pDirEntry->szName))
16205 continue;
16206
16207 /* Prepend the path to the libraries. */
16208 pszFilePath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
16209 if (!pszFilePath)
16210 {
16211 rc = VERR_NO_STR_MEMORY;
16212 break;
16213 }
16214
16215 lstFiles.push_back(pszFilePath);
16216 RTStrFree(pszFilePath);
16217 }
16218
16219 RTMemFree(pDirEntry);
16220 }
16221 else
16222 rc = VERR_NO_MEMORY;
16223
16224 RTDirClose(hDir);
16225 }
16226 else
16227 {
16228 /* On Windows the above immediately signals that there are no
16229 * files matching, while on other platforms enumerating the
16230 * files below fails. Either way: stop searching. */
16231 }
16232
16233 if ( rc == VERR_NO_MORE_FILES
16234 || rc == VERR_FILE_NOT_FOUND
16235 || rc == VERR_PATH_NOT_FOUND)
16236 rc = VINF_SUCCESS;
16237 RTStrFree(pszFilePattern);
16238 return rc;
16239}
16240
16241/**
16242 * Helper to set up an I/O stream to read or write a possibly encrypted file.
16243 *
16244 * @returns VBox status code.
16245 * @param pszFilename The file to open.
16246 * @param pCryptoIf Pointer to the cryptographic interface if the file should be encrypted or contains encrypted data.
16247 * @param pszKeyStore The keystore if the file should be encrypted or contains encrypted data.
16248 * @param pszPassword The password if the file should be encrypted or contains encrypted data.
16249 * @param fOpen The open flags for the file.
16250 * @param phVfsIos Where to store the handle to the I/O stream on success.
16251 */
16252int Machine::i_createIoStreamForFile(const char *pszFilename, PCVBOXCRYPTOIF pCryptoIf,
16253 const char *pszKeyStore, const char *pszPassword,
16254 uint64_t fOpen, PRTVFSIOSTREAM phVfsIos)
16255{
16256 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
16257 int vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
16258 if (RT_SUCCESS(vrc))
16259 {
16260 if (pCryptoIf)
16261 {
16262 RTVFSFILE hVfsFileCrypto = NIL_RTVFSFILE;
16263 vrc = pCryptoIf->pfnCryptoFileFromVfsFile(hVfsFile, pszKeyStore, pszPassword, &hVfsFileCrypto);
16264 if (RT_SUCCESS(vrc))
16265 {
16266 RTVfsFileRelease(hVfsFile);
16267 hVfsFile = hVfsFileCrypto;
16268 }
16269 }
16270
16271 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
16272 RTVfsFileRelease(hVfsFile);
16273 }
16274
16275 return vrc;
16276}
16277
16278/**
16279 * Helper function processing all actions for one component (saved state files,
16280 * NVRAM files, etc). Used by Machine::i_changeEncryptionHandler only.
16281 *
16282 * @param task
16283 * @param strDirectory
16284 * @param strFilePattern
16285 * @param strMagic
16286 * @param strKeyStore
16287 * @param strKeyId
16288 * @return
16289 */
16290HRESULT Machine::i_changeEncryptionForComponent(ChangeEncryptionTask &task, const com::Utf8Str strDirectory,
16291 const com::Utf8Str strFilePattern, com::Utf8Str &strKeyStore,
16292 com::Utf8Str &strKeyId, int iCipherMode)
16293{
16294 bool fDecrypt = task.mstrCurrentPassword.isNotEmpty()
16295 && task.mstrCipher.isEmpty()
16296 && task.mstrNewPassword.isEmpty()
16297 && task.mstrNewPasswordId.isEmpty();
16298 bool fEncrypt = task.mstrCurrentPassword.isEmpty()
16299 && task.mstrCipher.isNotEmpty()
16300 && task.mstrNewPassword.isNotEmpty()
16301 && task.mstrNewPasswordId.isNotEmpty();
16302
16303 /* check if the cipher is changed which causes the reencryption*/
16304
16305 const char *pszTaskCipher = NULL;
16306 if (task.mstrCipher.isNotEmpty())
16307 pszTaskCipher = getCipherString(task.mstrCipher.c_str(), iCipherMode);
16308
16309 if (!task.mForce && !fDecrypt && !fEncrypt)
16310 {
16311 char *pszCipher = NULL;
16312 int vrc = task.m_pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(strKeyStore.c_str(),
16313 NULL /*pszPassword*/,
16314 NULL /*ppbKey*/,
16315 NULL /*pcbKey*/,
16316 &pszCipher);
16317 if (RT_SUCCESS(vrc))
16318 {
16319 task.mForce = strcmp(pszTaskCipher, pszCipher) != 0;
16320 RTMemFree(pszCipher);
16321 }
16322 else
16323 return setErrorBoth(E_FAIL, vrc, tr("Obtain cipher for '%s' files failed (%Rrc)"),
16324 strFilePattern.c_str(), vrc);
16325 }
16326
16327 /* Only the password needs to be changed */
16328 if (!task.mForce && !fDecrypt && !fEncrypt)
16329 {
16330 Assert(task.m_pCryptoIf);
16331
16332 VBOXCRYPTOCTX hCryptoCtx;
16333 int vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(strKeyStore.c_str(), task.mstrCurrentPassword.c_str(), &hCryptoCtx);
16334 if (RT_FAILURE(vrc))
16335 return setErrorBoth(E_FAIL, vrc, tr("Loading old key store for '%s' files failed, (%Rrc)"),
16336 strFilePattern.c_str(), vrc);
16337 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
16338 if (RT_FAILURE(vrc))
16339 return setErrorBoth(E_FAIL, vrc, tr("Changing the password for '%s' files failed, (%Rrc)"),
16340 strFilePattern.c_str(), vrc);
16341
16342 char *pszKeyStore = NULL;
16343 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16344 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16345 if (RT_FAILURE(vrc))
16346 return setErrorBoth(E_FAIL, vrc, tr("Saving the key store for '%s' files failed, (%Rrc)"),
16347 strFilePattern.c_str(), vrc);
16348 strKeyStore = pszKeyStore;
16349 RTMemFree(pszKeyStore);
16350 strKeyId = task.mstrNewPasswordId;
16351 return S_OK;
16352 }
16353
16354 /* Reencryption required */
16355 HRESULT rc = S_OK;
16356 int vrc = VINF_SUCCESS;
16357
16358 std::list<com::Utf8Str> lstFiles;
16359 if (SUCCEEDED(rc))
16360 {
16361 vrc = i_findFiles(lstFiles, strDirectory, strFilePattern);
16362 if (RT_FAILURE(vrc))
16363 rc = setErrorBoth(E_FAIL, vrc, tr("Getting file list for '%s' files failed, (%Rrc)"),
16364 strFilePattern.c_str(), vrc);
16365 }
16366 com::Utf8Str strNewKeyStore;
16367 if (SUCCEEDED(rc))
16368 {
16369 if (!fDecrypt)
16370 {
16371 VBOXCRYPTOCTX hCryptoCtx;
16372 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(pszTaskCipher, task.mstrNewPassword.c_str(), &hCryptoCtx);
16373 if (RT_FAILURE(vrc))
16374 return setErrorBoth(E_FAIL, vrc, tr("Create new key store for '%s' files failed, (%Rrc)"),
16375 strFilePattern.c_str(), vrc);
16376
16377 char *pszKeyStore = NULL;
16378 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16379 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16380 if (RT_FAILURE(vrc))
16381 return setErrorBoth(E_FAIL, vrc, tr("Saving the new key store for '%s' files failed, (%Rrc)"),
16382 strFilePattern.c_str(), vrc);
16383 strNewKeyStore = pszKeyStore;
16384 RTMemFree(pszKeyStore);
16385 }
16386
16387 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
16388 it != lstFiles.end();
16389 ++it)
16390 {
16391 RTVFSIOSTREAM hVfsIosOld = NIL_RTVFSIOSTREAM;
16392 RTVFSIOSTREAM hVfsIosNew = NIL_RTVFSIOSTREAM;
16393
16394 uint64_t fOpenForRead = RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE;
16395 uint64_t fOpenForWrite = RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE;
16396
16397 vrc = i_createIoStreamForFile((*it).c_str(),
16398 fEncrypt ? NULL : task.m_pCryptoIf,
16399 fEncrypt ? NULL : strKeyStore.c_str(),
16400 fEncrypt ? NULL : task.mstrCurrentPassword.c_str(),
16401 fOpenForRead, &hVfsIosOld);
16402 if (RT_SUCCESS(vrc))
16403 {
16404 vrc = i_createIoStreamForFile((*it + ".tmp").c_str(),
16405 fDecrypt ? NULL : task.m_pCryptoIf,
16406 fDecrypt ? NULL : strNewKeyStore.c_str(),
16407 fDecrypt ? NULL : task.mstrNewPassword.c_str(),
16408 fOpenForWrite, &hVfsIosNew);
16409 if (RT_FAILURE(vrc))
16410 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
16411 (*it + ".tmp").c_str(), vrc);
16412 }
16413 else
16414 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
16415 (*it).c_str(), vrc);
16416
16417 if (RT_SUCCESS(vrc))
16418 {
16419 vrc = RTVfsUtilPumpIoStreams(hVfsIosOld, hVfsIosNew, BUF_DATA_SIZE);
16420 if (RT_FAILURE(vrc))
16421 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Changing encryption of the file '%s' failed with %Rrc"),
16422 (*it).c_str(), vrc);
16423 }
16424
16425 if (hVfsIosOld != NIL_RTVFSIOSTREAM)
16426 RTVfsIoStrmRelease(hVfsIosOld);
16427 if (hVfsIosNew != NIL_RTVFSIOSTREAM)
16428 RTVfsIoStrmRelease(hVfsIosNew);
16429 }
16430 }
16431
16432 if (SUCCEEDED(rc))
16433 {
16434 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
16435 it != lstFiles.end();
16436 ++it)
16437 {
16438 vrc = RTFileRename((*it + ".tmp").c_str(), (*it).c_str(), RTPATHRENAME_FLAGS_REPLACE);
16439 if (RT_FAILURE(vrc))
16440 {
16441 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming the file '%s' failed, (%Rrc)"),
16442 (*it + ".tmp").c_str(), vrc);
16443 break;
16444 }
16445 }
16446 }
16447
16448 if (SUCCEEDED(rc))
16449 {
16450 strKeyStore = strNewKeyStore;
16451 strKeyId = task.mstrNewPasswordId;
16452 }
16453
16454 return rc;
16455}
16456
16457/**
16458 * Task thread implementation for Machine::changeEncryption(), called from
16459 * Machine::taskHandler().
16460 *
16461 * @note Locks this object for writing.
16462 *
16463 * @param task
16464 * @return
16465 */
16466void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task)
16467{
16468 LogFlowThisFuncEnter();
16469
16470 AutoCaller autoCaller(this);
16471 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
16472 if (FAILED(autoCaller.rc()))
16473 {
16474 /* we might have been uninitialized because the session was accidentally
16475 * closed by the client, so don't assert */
16476 HRESULT rc = setError(E_FAIL,
16477 tr("The session has been accidentally closed"));
16478 task.m_pProgress->i_notifyComplete(rc);
16479 LogFlowThisFuncLeave();
16480 return;
16481 }
16482
16483 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16484
16485 HRESULT rc = S_OK;
16486 com::Utf8Str strOldKeyId = mData->mstrKeyId;
16487 com::Utf8Str strOldKeyStore = mData->mstrKeyStore;
16488 try
16489 {
16490 rc = this->i_getVirtualBox()->i_retainCryptoIf(&task.m_pCryptoIf);
16491 if (FAILED(rc))
16492 throw rc;
16493
16494 if (task.mstrCurrentPassword.isEmpty())
16495 {
16496 if (mData->mstrKeyStore.isNotEmpty())
16497 throw setError(VBOX_E_PASSWORD_INCORRECT,
16498 tr("The password given for the encrypted VM is incorrect"));
16499 }
16500 else
16501 {
16502 if (mData->mstrKeyStore.isEmpty())
16503 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16504 tr("The VM is not configured for encryption"));
16505 rc = checkEncryptionPassword(task.mstrCurrentPassword);
16506 if (rc == VBOX_E_PASSWORD_INCORRECT)
16507 throw setError(VBOX_E_PASSWORD_INCORRECT,
16508 tr("The password to decrypt the VM is incorrect"));
16509 }
16510
16511 if (task.mstrCipher.isNotEmpty())
16512 {
16513 if ( task.mstrNewPassword.isEmpty()
16514 && task.mstrNewPasswordId.isEmpty()
16515 && task.mstrCurrentPassword.isNotEmpty())
16516 {
16517 /* An empty password and password ID will default to the current password. */
16518 task.mstrNewPassword = task.mstrCurrentPassword;
16519 }
16520 else if (task.mstrNewPassword.isEmpty())
16521 throw setError(VBOX_E_OBJECT_NOT_FOUND,
16522 tr("A password must be given for the VM encryption"));
16523 else if (task.mstrNewPasswordId.isEmpty())
16524 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16525 tr("A valid identifier for the password must be given"));
16526 }
16527 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
16528 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16529 tr("The password and password identifier must be empty if the output should be unencrypted"));
16530
16531 /*
16532 * Save config.
16533 * Must be first operation to prevent making encrypted copies
16534 * for old version of the config file.
16535 */
16536 int fSave = Machine::SaveS_Force;
16537 if (task.mstrNewPassword.isNotEmpty())
16538 {
16539 VBOXCRYPTOCTX hCryptoCtx;
16540
16541 int vrc = VINF_SUCCESS;
16542 if (task.mForce || task.mstrCurrentPassword.isEmpty() || task.mstrCipher.isNotEmpty())
16543 {
16544 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(getCipherString(task.mstrCipher.c_str(), CipherModeGcm),
16545 task.mstrNewPassword.c_str(), &hCryptoCtx);
16546 if (RT_FAILURE(vrc))
16547 throw setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
16548 }
16549 else
16550 {
16551 vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(mData->mstrKeyStore.c_str(),
16552 task.mstrCurrentPassword.c_str(),
16553 &hCryptoCtx);
16554 if (RT_FAILURE(vrc))
16555 throw setErrorBoth(E_FAIL, vrc, tr("Loading old key store failed, (%Rrc)"), vrc);
16556 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
16557 if (RT_FAILURE(vrc))
16558 throw setErrorBoth(E_FAIL, vrc, tr("Changing the password failed, (%Rrc)"), vrc);
16559 }
16560
16561 char *pszKeyStore;
16562 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16563 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16564 if (RT_FAILURE(vrc))
16565 throw setErrorBoth(E_FAIL, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
16566 mData->mstrKeyStore = pszKeyStore;
16567 RTStrFree(pszKeyStore);
16568 mData->mstrKeyId = task.mstrNewPasswordId;
16569 size_t cbPassword = task.mstrNewPassword.length() + 1;
16570 uint8_t *pbPassword = (uint8_t *)task.mstrNewPassword.c_str();
16571 mData->mpKeyStore->deleteSecretKey(task.mstrNewPasswordId);
16572 mData->mpKeyStore->addSecretKey(task.mstrNewPasswordId, pbPassword, cbPassword);
16573 mNvramStore->i_addPassword(task.mstrNewPasswordId, task.mstrNewPassword);
16574
16575 /*
16576 * Remove backuped config after saving because it can contain
16577 * unencrypted version of the config
16578 */
16579 fSave |= Machine::SaveS_RemoveBackup;
16580 }
16581 else
16582 {
16583 mData->mstrKeyId.setNull();
16584 mData->mstrKeyStore.setNull();
16585 }
16586
16587 Bstr bstrCurrentPassword(task.mstrCurrentPassword);
16588 Bstr bstrCipher(getCipherString(task.mstrCipher.c_str(), CipherModeXts));
16589 Bstr bstrNewPassword(task.mstrNewPassword);
16590 Bstr bstrNewPasswordId(task.mstrNewPasswordId);
16591 /* encrypt mediums */
16592 alock.release();
16593 for (MediaList::iterator it = task.mllMedia.begin();
16594 it != task.mllMedia.end();
16595 ++it)
16596 {
16597 ComPtr<IProgress> pProgress1;
16598 HRESULT hrc = (*it)->ChangeEncryption(bstrCurrentPassword.raw(), bstrCipher.raw(),
16599 bstrNewPassword.raw(), bstrNewPasswordId.raw(),
16600 pProgress1.asOutParam());
16601 if (FAILED(hrc)) throw hrc;
16602 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress1, 0 /* indefinite wait */);
16603 if (FAILED(hrc)) throw hrc;
16604 }
16605 alock.acquire();
16606
16607 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the SAV files")).raw(), 1);
16608
16609 Utf8Str strFullSnapshotFolder;
16610 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
16611
16612 /* .sav files (main and snapshots) */
16613 rc = i_changeEncryptionForComponent(task, strFullSnapshotFolder, "*.sav",
16614 mSSData->strStateKeyStore, mSSData->strStateKeyId, CipherModeGcm);
16615 if (FAILED(rc))
16616 /* the helper function already sets error object */
16617 throw rc;
16618
16619 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the NVRAM files")).raw(), 1);
16620
16621 /* .nvram files */
16622 com::Utf8Str strNVRAMKeyId;
16623 com::Utf8Str strNVRAMKeyStore;
16624 rc = mNvramStore->i_getEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16625 if (FAILED(rc))
16626 throw setError(rc, tr("Getting NVRAM encryption settings failed (%Rhrc)"), rc);
16627
16628 Utf8Str strMachineFolder;
16629 i_calculateFullPath(".", strMachineFolder);
16630
16631 rc = i_changeEncryptionForComponent(task, strMachineFolder, "*.nvram",
16632 strNVRAMKeyStore, strNVRAMKeyId, CipherModeGcm);
16633 if (FAILED(rc))
16634 /* the helper function already sets error object */
16635 throw rc;
16636
16637 rc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16638 if (FAILED(rc))
16639 throw setError(rc, tr("Setting NVRAM encryption settings failed (%Rhrc)"), rc);
16640
16641 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of log files")).raw(), 1);
16642
16643 /* .log files */
16644 com::Utf8Str strLogFolder;
16645 i_getLogFolder(strLogFolder);
16646 rc = i_changeEncryptionForComponent(task, strLogFolder, "VBox.log*",
16647 mData->mstrLogKeyStore, mData->mstrLogKeyId, CipherModeCtr);
16648 if (FAILED(rc))
16649 /* the helper function already sets error object */
16650 throw rc;
16651
16652 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the config file")).raw(), 1);
16653
16654 i_saveSettings(NULL, alock, fSave);
16655 }
16656 catch (HRESULT aRC)
16657 {
16658 rc = aRC;
16659 mData->mstrKeyId = strOldKeyId;
16660 mData->mstrKeyStore = strOldKeyStore;
16661 }
16662
16663 task.m_pProgress->i_notifyComplete(rc);
16664
16665 LogFlowThisFuncLeave();
16666}
16667#endif /*!VBOX_WITH_FULL_VM_ENCRYPTION*/
16668
16669HRESULT Machine::changeEncryption(const com::Utf8Str &aCurrentPassword,
16670 const com::Utf8Str &aCipher,
16671 const com::Utf8Str &aNewPassword,
16672 const com::Utf8Str &aNewPasswordId,
16673 BOOL aForce,
16674 ComPtr<IProgress> &aProgress)
16675{
16676 LogFlowFuncEnter();
16677
16678#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16679 RT_NOREF(aCurrentPassword, aCipher, aNewPassword, aNewPasswordId, aForce, aProgress);
16680 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16681#else
16682 /* make the VM accessible */
16683 if (!mData->mAccessible)
16684 {
16685 if ( aCurrentPassword.isEmpty()
16686 || mData->mstrKeyId.isEmpty())
16687 return setError(E_ACCESSDENIED, tr("Machine is inaccessible"));
16688
16689 HRESULT rc = addEncryptionPassword(mData->mstrKeyId, aCurrentPassword);
16690 if (FAILED(rc))
16691 return rc;
16692 }
16693
16694 AutoLimitedCaller autoCaller(this);
16695 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16696
16697 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16698
16699 /* define mediums to be change encryption */
16700
16701 MediaList llMedia;
16702 for (MediumAttachmentList::iterator
16703 it = mMediumAttachments->begin();
16704 it != mMediumAttachments->end();
16705 ++it)
16706 {
16707 ComObjPtr<MediumAttachment> &pAttach = *it;
16708 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
16709
16710 if (!pMedium.isNull())
16711 {
16712 AutoCaller mac(pMedium);
16713 if (FAILED(mac.rc())) return mac.rc();
16714 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
16715 DeviceType_T devType = pMedium->i_getDeviceType();
16716 if (devType == DeviceType_HardDisk)
16717 {
16718 /*
16719 * We need to move to last child because the Medium::changeEncryption
16720 * encrypts all chain of specified medium with its parents.
16721 * Also we perform cheking of back reference and children for
16722 * all media in the chain to raise error before we start any action.
16723 * So, we first move into root parent and then we will move to last child
16724 * keeping latter in the list for encryption.
16725 */
16726
16727 /* move to root parent */
16728 ComObjPtr<Medium> pTmpMedium = pMedium;
16729 while (pTmpMedium.isNotNull())
16730 {
16731 AutoCaller mediumAC(pTmpMedium);
16732 if (FAILED(mediumAC.rc())) return mac.rc();
16733 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16734
16735 /* Cannot encrypt media which are attached to more than one virtual machine. */
16736 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16737 if (cBackRefs > 1)
16738 return setError(VBOX_E_INVALID_OBJECT_STATE,
16739 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16740 pTmpMedium->i_getName().c_str(), cBackRefs);
16741
16742 size_t cChildren = pTmpMedium->i_getChildren().size();
16743 if (cChildren > 1)
16744 return setError(VBOX_E_INVALID_OBJECT_STATE,
16745 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16746 pTmpMedium->i_getName().c_str(), cChildren);
16747
16748 pTmpMedium = pTmpMedium->i_getParent();
16749 }
16750 /* move to last child */
16751 pTmpMedium = pMedium;
16752 while (pTmpMedium.isNotNull() && pTmpMedium->i_getChildren().size() != 0)
16753 {
16754 AutoCaller mediumAC(pTmpMedium);
16755 if (FAILED(mediumAC.rc())) return mac.rc();
16756 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16757
16758 /* Cannot encrypt media which are attached to more than one virtual machine. */
16759 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16760 if (cBackRefs > 1)
16761 return setError(VBOX_E_INVALID_OBJECT_STATE,
16762 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16763 pTmpMedium->i_getName().c_str(), cBackRefs);
16764
16765 size_t cChildren = pTmpMedium->i_getChildren().size();
16766 if (cChildren > 1)
16767 return setError(VBOX_E_INVALID_OBJECT_STATE,
16768 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16769 pTmpMedium->i_getName().c_str(), cChildren);
16770
16771 pTmpMedium = pTmpMedium->i_getChildren().front();
16772 }
16773 llMedia.push_back(pTmpMedium);
16774 }
16775 }
16776 }
16777
16778 ComObjPtr<Progress> pProgress;
16779 pProgress.createObject();
16780 HRESULT rc = pProgress->init(i_getVirtualBox(),
16781 static_cast<IMachine*>(this) /* aInitiator */,
16782 tr("Change encryption"),
16783 TRUE /* fCancellable */,
16784 (ULONG)(4 + + llMedia.size()), // cOperations
16785 tr("Change encryption of the mediuma"));
16786 if (FAILED(rc))
16787 return rc;
16788
16789 /* create and start the task on a separate thread (note that it will not
16790 * start working until we release alock) */
16791 ChangeEncryptionTask *pTask = new ChangeEncryptionTask(this, pProgress, "VM encryption",
16792 aCurrentPassword, aCipher, aNewPassword,
16793 aNewPasswordId, aForce, llMedia);
16794 rc = pTask->createThread();
16795 pTask = NULL;
16796 if (FAILED(rc))
16797 return rc;
16798
16799 pProgress.queryInterfaceTo(aProgress.asOutParam());
16800
16801 LogFlowFuncLeave();
16802
16803 return S_OK;
16804#endif
16805}
16806
16807HRESULT Machine::getEncryptionSettings(com::Utf8Str &aCipher,
16808 com::Utf8Str &aPasswordId)
16809{
16810#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16811 RT_NOREF(aCipher, aPasswordId);
16812 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16813#else
16814 AutoLimitedCaller autoCaller(this);
16815 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16816
16817 PCVBOXCRYPTOIF pCryptoIf = NULL;
16818 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16819 if (FAILED(hrc)) return hrc; /* Error is set */
16820
16821 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16822
16823 if (mData->mstrKeyStore.isNotEmpty())
16824 {
16825 char *pszCipher = NULL;
16826 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), NULL /*pszPassword*/,
16827 NULL /*ppbKey*/, NULL /*pcbKey*/, &pszCipher);
16828 if (RT_SUCCESS(vrc))
16829 {
16830 aCipher = getCipherStringWithoutMode(pszCipher);
16831 RTStrFree(pszCipher);
16832 aPasswordId = mData->mstrKeyId;
16833 }
16834 else
16835 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
16836 tr("Failed to query the encryption settings with %Rrc"),
16837 vrc);
16838 }
16839 else
16840 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16841
16842 mParent->i_releaseCryptoIf(pCryptoIf);
16843
16844 return hrc;
16845#endif
16846}
16847
16848HRESULT Machine::checkEncryptionPassword(const com::Utf8Str &aPassword)
16849{
16850#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16851 RT_NOREF(aPassword);
16852 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16853#else
16854 AutoLimitedCaller autoCaller(this);
16855 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16856
16857 PCVBOXCRYPTOIF pCryptoIf = NULL;
16858 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16859 if (FAILED(hrc)) return hrc; /* Error is set */
16860
16861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16862
16863 if (mData->mstrKeyStore.isNotEmpty())
16864 {
16865 char *pszCipher = NULL;
16866 uint8_t *pbDek = NULL;
16867 size_t cbDek = 0;
16868 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), aPassword.c_str(),
16869 &pbDek, &cbDek, &pszCipher);
16870 if (RT_SUCCESS(vrc))
16871 {
16872 RTStrFree(pszCipher);
16873 RTMemSaferFree(pbDek, cbDek);
16874 }
16875 else
16876 hrc = setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc,
16877 tr("The password supplied for the encrypted machine is incorrect"));
16878 }
16879 else
16880 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16881
16882 mParent->i_releaseCryptoIf(pCryptoIf);
16883
16884 return hrc;
16885#endif
16886}
16887
16888HRESULT Machine::addEncryptionPassword(const com::Utf8Str &aId,
16889 const com::Utf8Str &aPassword)
16890{
16891#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16892 RT_NOREF(aId, aPassword);
16893 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16894#else
16895 AutoLimitedCaller autoCaller(this);
16896 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16897
16898 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16899
16900 size_t cbPassword = aPassword.length() + 1;
16901 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
16902
16903 mData->mpKeyStore->addSecretKey(aId, pbPassword, cbPassword);
16904
16905 if ( mData->mAccessible
16906 && mData->mSession.mState == SessionState_Locked
16907 && mData->mSession.mLockType == LockType_VM
16908 && mData->mSession.mDirectControl != NULL)
16909 {
16910 /* get the console from the direct session */
16911 ComPtr<IConsole> console;
16912 HRESULT rc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16913 ComAssertComRC(rc);
16914 /* send passsword to console */
16915 console->AddEncryptionPassword(Bstr(aId).raw(),
16916 Bstr(aPassword).raw(),
16917 TRUE);
16918 }
16919
16920 if (mData->mstrKeyId == aId)
16921 {
16922 HRESULT hrc = checkEncryptionPassword(aPassword);
16923 if (FAILED(hrc))
16924 return hrc;
16925
16926 if (SUCCEEDED(hrc))
16927 {
16928 /*
16929 * Encryption is used and password is correct,
16930 * Reinit the machine if required.
16931 */
16932 BOOL fAccessible;
16933 alock.release();
16934 getAccessible(&fAccessible);
16935 alock.acquire();
16936 }
16937 }
16938
16939 /*
16940 * Add the password into the NvramStore only after
16941 * the machine becomes accessible and the NvramStore
16942 * contains key id and key store.
16943 */
16944 if (mNvramStore.isNotNull())
16945 mNvramStore->i_addPassword(aId, aPassword);
16946
16947 return S_OK;
16948#endif
16949}
16950
16951HRESULT Machine::addEncryptionPasswords(const std::vector<com::Utf8Str> &aIds,
16952 const std::vector<com::Utf8Str> &aPasswords)
16953{
16954#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16955 RT_NOREF(aIds, aPasswords);
16956 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16957#else
16958 if (aIds.size() != aPasswords.size())
16959 return setError(E_INVALIDARG, tr("Id and passwords arrays must have the same size"));
16960
16961 HRESULT hrc = S_OK;
16962 for (size_t i = 0; i < aIds.size() && SUCCEEDED(hrc); ++i)
16963 hrc = addEncryptionPassword(aIds[i], aPasswords[i]);
16964
16965 return hrc;
16966#endif
16967}
16968
16969HRESULT Machine::removeEncryptionPassword(AutoCaller &autoCaller, const com::Utf8Str &aId)
16970{
16971#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16972 RT_NOREF(autoCaller, aId);
16973 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16974#else
16975 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16976
16977 if ( mData->mAccessible
16978 && mData->mSession.mState == SessionState_Locked
16979 && mData->mSession.mLockType == LockType_VM
16980 && mData->mSession.mDirectControl != NULL)
16981 {
16982 /* get the console from the direct session */
16983 ComPtr<IConsole> console;
16984 HRESULT rc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16985 ComAssertComRC(rc);
16986 /* send passsword to console */
16987 console->RemoveEncryptionPassword(Bstr(aId).raw());
16988 }
16989
16990 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty() && mData->mstrKeyId == aId)
16991 {
16992 if (Global::IsOnlineOrTransient(mData->mMachineState))
16993 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
16994 alock.release();
16995 autoCaller.release();
16996 /* return because all passwords are purged when machine becomes inaccessible; */
16997 return i_setInaccessible();
16998 }
16999
17000 if (mNvramStore.isNotNull())
17001 mNvramStore->i_removePassword(aId);
17002 mData->mpKeyStore->deleteSecretKey(aId);
17003 return S_OK;
17004#endif
17005}
17006
17007HRESULT Machine::clearAllEncryptionPasswords(AutoCaller &autoCaller)
17008{
17009#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
17010 RT_NOREF(autoCaller);
17011 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
17012#else
17013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
17014
17015 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty())
17016 {
17017 if (Global::IsOnlineOrTransient(mData->mMachineState))
17018 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
17019 alock.release();
17020 autoCaller.release();
17021 /* return because all passwords are purged when machine becomes inaccessible; */
17022 return i_setInaccessible();
17023 }
17024
17025 mNvramStore->i_removeAllPasswords();
17026 mData->mpKeyStore->deleteAllSecretKeys(false, true);
17027 return S_OK;
17028#endif
17029}
17030
17031#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
17032HRESULT Machine::i_setInaccessible()
17033{
17034 if (!mData->mAccessible)
17035 return S_OK;
17036
17037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
17038 VirtualBox *pParent = mParent;
17039 com::Utf8Str strConfigFile = mData->m_strConfigFile;
17040 Guid id(i_getId());
17041
17042 alock.release();
17043
17044 uninit();
17045 HRESULT rc = initFromSettings(pParent, strConfigFile, &id, com::Utf8Str());
17046
17047 alock.acquire();
17048 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
17049 return rc;
17050}
17051#endif
17052
17053/* This isn't handled entirely by the wrapper generator yet. */
17054#ifdef VBOX_WITH_XPCOM
17055NS_DECL_CLASSINFO(SessionMachine)
17056NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
17057
17058NS_DECL_CLASSINFO(SnapshotMachine)
17059NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
17060#endif
Note: See TracBrowser for help on using the repository browser.

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