VirtualBox

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

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

Recording: Settings handling fixes / overhaul. This adds the ability to handle per-screen settings, which can be different from the first screen (screen 0). Also fixed a couple of bugs regarding snapshot handling and persistence (committing, rolling back, ++) in that area. FE/VBoxManage now can also list the per-screen settings. Added some further @todos. bugref:9286

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 599.6 KB
Line 
1/* $Id: MachineImpl.cpp 95639 2022-07-14 08:30:45Z 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 recording 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 recording settings object */
8635 unconst(mRecordingSettings).createObject();
8636 mRecordingSettings->init(this);
8637
8638 /* create associated trusted platform module object */
8639 unconst(mTrustedPlatformModule).createObject();
8640 mTrustedPlatformModule->init(this);
8641
8642 /* create associated NVRAM store object */
8643 unconst(mNvramStore).createObject();
8644 mNvramStore->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 (mRecordingSettings)
8774 {
8775 mRecordingSettings->uninit();
8776 unconst(mRecordingSettings).setNull();
8777 }
8778
8779 if (mTrustedPlatformModule)
8780 {
8781 mTrustedPlatformModule->uninit();
8782 unconst(mTrustedPlatformModule).setNull();
8783 }
8784
8785 if (mNvramStore)
8786 {
8787 mNvramStore->uninit();
8788 unconst(mNvramStore).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 config.recordingSettings);
9086 if (FAILED(rc)) return rc;
9087
9088 /*
9089 * NOTE: the assignment below must be the last thing to do,
9090 * otherwise it will be not possible to change the settings
9091 * somewhere in the code above because all setters will be
9092 * blocked by i_checkStateDependency(MutableStateDep).
9093 */
9094
9095 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
9096 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
9097 {
9098 /* no need to use i_setMachineState() during init() */
9099 mData->mMachineState = MachineState_AbortedSaved;
9100 }
9101 else if (config.fAborted)
9102 {
9103 mSSData->strStateFilePath.setNull();
9104
9105 /* no need to use i_setMachineState() during init() */
9106 mData->mMachineState = MachineState_Aborted;
9107 }
9108 else if (!mSSData->strStateFilePath.isEmpty())
9109 {
9110 /* no need to use i_setMachineState() during init() */
9111 mData->mMachineState = MachineState_Saved;
9112 }
9113
9114 // after loading settings, we are no longer different from the XML on disk
9115 mData->flModifications = 0;
9116
9117 return S_OK;
9118}
9119
9120/**
9121 * Loads all snapshots starting from the given settings.
9122 *
9123 * @param data snapshot settings.
9124 * @param aCurSnapshotId Current snapshot ID from the settings file.
9125 */
9126HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
9127 const Guid &aCurSnapshotId)
9128{
9129 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
9130 AssertReturn(!i_isSessionMachine(), E_FAIL);
9131
9132 HRESULT rc = S_OK;
9133
9134 std::list<const settings::Snapshot *> llSettingsTodo;
9135 llSettingsTodo.push_back(&data);
9136 std::list<Snapshot *> llParentsTodo;
9137 llParentsTodo.push_back(NULL);
9138
9139 while (llSettingsTodo.size() > 0)
9140 {
9141 const settings::Snapshot *current = llSettingsTodo.front();
9142 llSettingsTodo.pop_front();
9143 Snapshot *pParent = llParentsTodo.front();
9144 llParentsTodo.pop_front();
9145
9146 Utf8Str strStateFile;
9147 if (!current->strStateFile.isEmpty())
9148 {
9149 /* optional */
9150 strStateFile = current->strStateFile;
9151 int vrc = i_calculateFullPath(strStateFile, strStateFile);
9152 if (RT_FAILURE(vrc))
9153 {
9154 setErrorBoth(E_FAIL, vrc,
9155 tr("Invalid saved state file path '%s' (%Rrc)"),
9156 strStateFile.c_str(), vrc);
9157 }
9158 }
9159
9160 /* create a snapshot machine object */
9161 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9162 pSnapshotMachine.createObject();
9163 rc = pSnapshotMachine->initFromSettings(this,
9164 current->hardware,
9165 &current->debugging,
9166 &current->autostart,
9167 current->recordingSettings,
9168 current->uuid.ref(),
9169 strStateFile);
9170 if (FAILED(rc)) break;
9171
9172 /* create a snapshot object */
9173 ComObjPtr<Snapshot> pSnapshot;
9174 pSnapshot.createObject();
9175 /* initialize the snapshot */
9176 rc = pSnapshot->init(mParent, // VirtualBox object
9177 current->uuid,
9178 current->strName,
9179 current->strDescription,
9180 current->timestamp,
9181 pSnapshotMachine,
9182 pParent);
9183 if (FAILED(rc)) break;
9184
9185 /* memorize the first snapshot if necessary */
9186 if (!mData->mFirstSnapshot)
9187 {
9188 Assert(pParent == NULL);
9189 mData->mFirstSnapshot = pSnapshot;
9190 }
9191
9192 /* memorize the current snapshot when appropriate */
9193 if ( !mData->mCurrentSnapshot
9194 && pSnapshot->i_getId() == aCurSnapshotId
9195 )
9196 mData->mCurrentSnapshot = pSnapshot;
9197
9198 /* create all children */
9199 std::list<settings::Snapshot>::const_iterator itBegin = current->llChildSnapshots.begin();
9200 std::list<settings::Snapshot>::const_iterator itEnd = current->llChildSnapshots.end();
9201 for (std::list<settings::Snapshot>::const_iterator it = itBegin; it != itEnd; ++it)
9202 {
9203 llSettingsTodo.push_back(&*it);
9204 llParentsTodo.push_back(pSnapshot);
9205 }
9206 }
9207
9208 return rc;
9209}
9210
9211/**
9212 * Loads settings into mHWData.
9213 *
9214 * @param puuidRegistry Registry ID.
9215 * @param puuidSnapshot Snapshot ID
9216 * @param data Reference to the hardware settings.
9217 * @param pDbg Pointer to the debugging settings.
9218 * @param pAutostart Pointer to the autostart settings
9219 * @param recording Reference to recording settings.
9220 */
9221HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
9222 const Guid *puuidSnapshot,
9223 const settings::Hardware &data,
9224 const settings::Debugging *pDbg,
9225 const settings::Autostart *pAutostart,
9226 const settings::RecordingSettings &recording)
9227{
9228 AssertReturn(!i_isSessionMachine(), E_FAIL);
9229
9230 HRESULT rc = S_OK;
9231
9232 try
9233 {
9234 ComObjPtr<GuestOSType> pGuestOSType;
9235 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9236
9237 /* The hardware version attribute (optional). */
9238 mHWData->mHWVersion = data.strVersion;
9239 mHWData->mHardwareUUID = data.uuid;
9240
9241 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9242 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9243 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9244 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9245 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9246 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9247 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
9248 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
9249 mHWData->mPAEEnabled = data.fPAE;
9250 mHWData->mLongMode = data.enmLongMode;
9251 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9252 mHWData->mAPIC = data.fAPIC;
9253 mHWData->mX2APIC = data.fX2APIC;
9254 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9255 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9256 mHWData->mSpecCtrl = data.fSpecCtrl;
9257 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
9258 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
9259 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
9260 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
9261 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
9262 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9263 mHWData->mCPUCount = data.cCPUs;
9264 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9265 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9266 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9267 mHWData->mCpuProfile = data.strCpuProfile;
9268
9269 // cpu
9270 if (mHWData->mCPUHotPlugEnabled)
9271 {
9272 for (settings::CpuList::const_iterator
9273 it = data.llCpus.begin();
9274 it != data.llCpus.end();
9275 ++it)
9276 {
9277 const settings::Cpu &cpu = *it;
9278
9279 mHWData->mCPUAttached[cpu.ulId] = true;
9280 }
9281 }
9282
9283 // cpuid leafs
9284 for (settings::CpuIdLeafsList::const_iterator
9285 it = data.llCpuIdLeafs.begin();
9286 it != data.llCpuIdLeafs.end();
9287 ++it)
9288 {
9289 const settings::CpuIdLeaf &rLeaf= *it;
9290 if ( rLeaf.idx < UINT32_C(0x20)
9291 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9292 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9293 mHWData->mCpuIdLeafList.push_back(rLeaf);
9294 /* else: just ignore */
9295 }
9296
9297 mHWData->mMemorySize = data.ulMemorySizeMB;
9298 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9299
9300 // boot order
9301 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9302 {
9303 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9304 if (it == data.mapBootOrder.end())
9305 mHWData->mBootOrder[i] = DeviceType_Null;
9306 else
9307 mHWData->mBootOrder[i] = it->second;
9308 }
9309
9310 mHWData->mFirmwareType = data.firmwareType;
9311 mHWData->mPointingHIDType = data.pointingHIDType;
9312 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9313 mHWData->mChipsetType = data.chipsetType;
9314 mHWData->mIommuType = data.iommuType;
9315 mHWData->mParavirtProvider = data.paravirtProvider;
9316 mHWData->mParavirtDebug = data.strParavirtDebug;
9317 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9318 mHWData->mHPETEnabled = data.fHPETEnabled;
9319
9320 /* GraphicsAdapter */
9321 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
9322 if (FAILED(rc)) return rc;
9323
9324 /* VRDEServer */
9325 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9326 if (FAILED(rc)) return rc;
9327
9328 /* BIOS */
9329 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9330 if (FAILED(rc)) return rc;
9331
9332 /* Recording */
9333 rc = mRecordingSettings->i_loadSettings(recording);
9334 if (FAILED(rc)) return rc;
9335
9336 /* Trusted Platform Module */
9337 rc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
9338 if (FAILED(rc)) return rc;
9339
9340 rc = mNvramStore->i_loadSettings(data.nvramSettings);
9341 if (FAILED(rc)) return rc;
9342
9343 // Bandwidth control (must come before network adapters)
9344 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9345 if (FAILED(rc)) return rc;
9346
9347 /* USB controllers */
9348 for (settings::USBControllerList::const_iterator
9349 it = data.usbSettings.llUSBControllers.begin();
9350 it != data.usbSettings.llUSBControllers.end();
9351 ++it)
9352 {
9353 const settings::USBController &settingsCtrl = *it;
9354 ComObjPtr<USBController> newCtrl;
9355
9356 newCtrl.createObject();
9357 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9358 mUSBControllers->push_back(newCtrl);
9359 }
9360
9361 /* USB device filters */
9362 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9363 if (FAILED(rc)) return rc;
9364
9365 // network adapters (establish array size first and apply defaults, to
9366 // ensure reading the same settings as we saved, since the list skips
9367 // adapters having defaults)
9368 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9369 size_t oldCount = mNetworkAdapters.size();
9370 if (newCount > oldCount)
9371 {
9372 mNetworkAdapters.resize(newCount);
9373 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9374 {
9375 unconst(mNetworkAdapters[slot]).createObject();
9376 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9377 }
9378 }
9379 else if (newCount < oldCount)
9380 mNetworkAdapters.resize(newCount);
9381 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9382 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9383 for (settings::NetworkAdaptersList::const_iterator
9384 it = data.llNetworkAdapters.begin();
9385 it != data.llNetworkAdapters.end();
9386 ++it)
9387 {
9388 const settings::NetworkAdapter &nic = *it;
9389
9390 /* slot uniqueness is guaranteed by XML Schema */
9391 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9392 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9393 if (FAILED(rc)) return rc;
9394 }
9395
9396 // serial ports (establish defaults first, to ensure reading the same
9397 // settings as we saved, since the list skips ports having defaults)
9398 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9399 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9400 for (settings::SerialPortsList::const_iterator
9401 it = data.llSerialPorts.begin();
9402 it != data.llSerialPorts.end();
9403 ++it)
9404 {
9405 const settings::SerialPort &s = *it;
9406
9407 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9408 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9409 if (FAILED(rc)) return rc;
9410 }
9411
9412 // parallel ports (establish defaults first, to ensure reading the same
9413 // settings as we saved, since the list skips ports having defaults)
9414 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9415 mParallelPorts[i]->i_applyDefaults();
9416 for (settings::ParallelPortsList::const_iterator
9417 it = data.llParallelPorts.begin();
9418 it != data.llParallelPorts.end();
9419 ++it)
9420 {
9421 const settings::ParallelPort &p = *it;
9422
9423 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9424 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9425 if (FAILED(rc)) return rc;
9426 }
9427
9428 /* Audio settings */
9429 rc = mAudioSettings->i_loadSettings(data.audioAdapter);
9430 if (FAILED(rc)) return rc;
9431
9432 /* storage controllers */
9433 rc = i_loadStorageControllers(data.storage,
9434 puuidRegistry,
9435 puuidSnapshot);
9436 if (FAILED(rc)) return rc;
9437
9438 /* Shared folders */
9439 for (settings::SharedFoldersList::const_iterator
9440 it = data.llSharedFolders.begin();
9441 it != data.llSharedFolders.end();
9442 ++it)
9443 {
9444 const settings::SharedFolder &sf = *it;
9445
9446 ComObjPtr<SharedFolder> sharedFolder;
9447 /* Check for double entries. Not allowed! */
9448 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9449 if (SUCCEEDED(rc))
9450 return setError(VBOX_E_OBJECT_IN_USE,
9451 tr("Shared folder named '%s' already exists"),
9452 sf.strName.c_str());
9453
9454 /* Create the new shared folder. Don't break on error. This will be
9455 * reported when the machine starts. */
9456 sharedFolder.createObject();
9457 rc = sharedFolder->init(i_getMachine(),
9458 sf.strName,
9459 sf.strHostPath,
9460 RT_BOOL(sf.fWritable),
9461 RT_BOOL(sf.fAutoMount),
9462 sf.strAutoMountPoint,
9463 false /* fFailOnError */);
9464 if (FAILED(rc)) return rc;
9465 mHWData->mSharedFolders.push_back(sharedFolder);
9466 }
9467
9468 // Clipboard
9469 mHWData->mClipboardMode = data.clipboardMode;
9470 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9471
9472 // drag'n'drop
9473 mHWData->mDnDMode = data.dndMode;
9474
9475 // guest settings
9476 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9477
9478 // IO settings
9479 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9480 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9481
9482 // Host PCI devices
9483 for (settings::HostPCIDeviceAttachmentList::const_iterator
9484 it = data.pciAttachments.begin();
9485 it != data.pciAttachments.end();
9486 ++it)
9487 {
9488 const settings::HostPCIDeviceAttachment &hpda = *it;
9489 ComObjPtr<PCIDeviceAttachment> pda;
9490
9491 pda.createObject();
9492 pda->i_loadSettings(this, hpda);
9493 mHWData->mPCIDeviceAssignments.push_back(pda);
9494 }
9495
9496 /*
9497 * (The following isn't really real hardware, but it lives in HWData
9498 * for reasons of convenience.)
9499 */
9500
9501#ifdef VBOX_WITH_GUEST_PROPS
9502 /* Guest properties (optional) */
9503
9504 /* Only load transient guest properties for configs which have saved
9505 * state, because there shouldn't be any for powered off VMs. The same
9506 * logic applies for snapshots, as offline snapshots shouldn't have
9507 * any such properties. They confuse the code in various places.
9508 * Note: can't rely on the machine state, as it isn't set yet. */
9509 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9510 /* apologies for the hacky unconst() usage, but this needs hacking
9511 * actually inconsistent settings into consistency, otherwise there
9512 * will be some corner cases where the inconsistency survives
9513 * surprisingly long without getting fixed, especially for snapshots
9514 * as there are no config changes. */
9515 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9516 for (settings::GuestPropertiesList::iterator
9517 it = llGuestProperties.begin();
9518 it != llGuestProperties.end();
9519 /*nothing*/)
9520 {
9521 const settings::GuestProperty &prop = *it;
9522 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9523 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9524 if ( fSkipTransientGuestProperties
9525 && ( fFlags & GUEST_PROP_F_TRANSIENT
9526 || fFlags & GUEST_PROP_F_TRANSRESET))
9527 {
9528 it = llGuestProperties.erase(it);
9529 continue;
9530 }
9531 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9532 mHWData->mGuestProperties[prop.strName] = property;
9533 ++it;
9534 }
9535#endif /* VBOX_WITH_GUEST_PROPS defined */
9536
9537 rc = i_loadDebugging(pDbg);
9538 if (FAILED(rc))
9539 return rc;
9540
9541 mHWData->mAutostart = *pAutostart;
9542
9543 /* default frontend */
9544 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9545 }
9546 catch (std::bad_alloc &)
9547 {
9548 return E_OUTOFMEMORY;
9549 }
9550
9551 AssertComRC(rc);
9552 return rc;
9553}
9554
9555/**
9556 * Called from i_loadHardware() to load the debugging settings of the
9557 * machine.
9558 *
9559 * @param pDbg Pointer to the settings.
9560 */
9561HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9562{
9563 mHWData->mDebugging = *pDbg;
9564 /* no more processing currently required, this will probably change. */
9565 return S_OK;
9566}
9567
9568/**
9569 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9570 *
9571 * @param data storage settings.
9572 * @param puuidRegistry media registry ID to set media to or NULL;
9573 * see Machine::i_loadMachineDataFromSettings()
9574 * @param puuidSnapshot snapshot ID
9575 * @return
9576 */
9577HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9578 const Guid *puuidRegistry,
9579 const Guid *puuidSnapshot)
9580{
9581 AssertReturn(!i_isSessionMachine(), E_FAIL);
9582
9583 HRESULT rc = S_OK;
9584
9585 for (settings::StorageControllersList::const_iterator
9586 it = data.llStorageControllers.begin();
9587 it != data.llStorageControllers.end();
9588 ++it)
9589 {
9590 const settings::StorageController &ctlData = *it;
9591
9592 ComObjPtr<StorageController> pCtl;
9593 /* Try to find one with the name first. */
9594 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9595 if (SUCCEEDED(rc))
9596 return setError(VBOX_E_OBJECT_IN_USE,
9597 tr("Storage controller named '%s' already exists"),
9598 ctlData.strName.c_str());
9599
9600 pCtl.createObject();
9601 rc = pCtl->init(this,
9602 ctlData.strName,
9603 ctlData.storageBus,
9604 ctlData.ulInstance,
9605 ctlData.fBootable);
9606 if (FAILED(rc)) return rc;
9607
9608 mStorageControllers->push_back(pCtl);
9609
9610 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9611 if (FAILED(rc)) return rc;
9612
9613 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9614 if (FAILED(rc)) return rc;
9615
9616 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9617 if (FAILED(rc)) return rc;
9618
9619 /* Load the attached devices now. */
9620 rc = i_loadStorageDevices(pCtl,
9621 ctlData,
9622 puuidRegistry,
9623 puuidSnapshot);
9624 if (FAILED(rc)) return rc;
9625 }
9626
9627 return S_OK;
9628}
9629
9630/**
9631 * Called from i_loadStorageControllers for a controller's devices.
9632 *
9633 * @param aStorageController
9634 * @param data
9635 * @param puuidRegistry media registry ID to set media to or NULL; see
9636 * Machine::i_loadMachineDataFromSettings()
9637 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9638 * @return
9639 */
9640HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9641 const settings::StorageController &data,
9642 const Guid *puuidRegistry,
9643 const Guid *puuidSnapshot)
9644{
9645 HRESULT rc = S_OK;
9646
9647 /* paranoia: detect duplicate attachments */
9648 for (settings::AttachedDevicesList::const_iterator
9649 it = data.llAttachedDevices.begin();
9650 it != data.llAttachedDevices.end();
9651 ++it)
9652 {
9653 const settings::AttachedDevice &ad = *it;
9654
9655 for (settings::AttachedDevicesList::const_iterator it2 = it;
9656 it2 != data.llAttachedDevices.end();
9657 ++it2)
9658 {
9659 if (it == it2)
9660 continue;
9661
9662 const settings::AttachedDevice &ad2 = *it2;
9663
9664 if ( ad.lPort == ad2.lPort
9665 && ad.lDevice == ad2.lDevice)
9666 {
9667 return setError(E_FAIL,
9668 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9669 aStorageController->i_getName().c_str(),
9670 ad.lPort,
9671 ad.lDevice,
9672 mUserData->s.strName.c_str());
9673 }
9674 }
9675 }
9676
9677 for (settings::AttachedDevicesList::const_iterator
9678 it = data.llAttachedDevices.begin();
9679 it != data.llAttachedDevices.end();
9680 ++it)
9681 {
9682 const settings::AttachedDevice &dev = *it;
9683 ComObjPtr<Medium> medium;
9684
9685 switch (dev.deviceType)
9686 {
9687 case DeviceType_Floppy:
9688 case DeviceType_DVD:
9689 if (dev.strHostDriveSrc.isNotEmpty())
9690 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9691 false /* fRefresh */, medium);
9692 else
9693 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9694 dev.uuid,
9695 false /* fRefresh */,
9696 false /* aSetError */,
9697 medium);
9698 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9699 // This is not an error. The host drive or UUID might have vanished, so just go
9700 // ahead without this removeable medium attachment
9701 rc = S_OK;
9702 break;
9703
9704 case DeviceType_HardDisk:
9705 {
9706 /* find a hard disk by UUID */
9707 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9708 if (FAILED(rc))
9709 {
9710 if (i_isSnapshotMachine())
9711 {
9712 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9713 // so the user knows that the bad disk is in a snapshot somewhere
9714 com::ErrorInfo info;
9715 return setError(E_FAIL,
9716 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9717 puuidSnapshot->raw(),
9718 info.getText().raw());
9719 }
9720 else
9721 return rc;
9722 }
9723
9724 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9725
9726 if (medium->i_getType() == MediumType_Immutable)
9727 {
9728 if (i_isSnapshotMachine())
9729 return setError(E_FAIL,
9730 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9731 "of the virtual machine '%s' ('%s')"),
9732 medium->i_getLocationFull().c_str(),
9733 dev.uuid.raw(),
9734 puuidSnapshot->raw(),
9735 mUserData->s.strName.c_str(),
9736 mData->m_strConfigFileFull.c_str());
9737
9738 return setError(E_FAIL,
9739 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9740 medium->i_getLocationFull().c_str(),
9741 dev.uuid.raw(),
9742 mUserData->s.strName.c_str(),
9743 mData->m_strConfigFileFull.c_str());
9744 }
9745
9746 if (medium->i_getType() == MediumType_MultiAttach)
9747 {
9748 if (i_isSnapshotMachine())
9749 return setError(E_FAIL,
9750 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9751 "of the virtual machine '%s' ('%s')"),
9752 medium->i_getLocationFull().c_str(),
9753 dev.uuid.raw(),
9754 puuidSnapshot->raw(),
9755 mUserData->s.strName.c_str(),
9756 mData->m_strConfigFileFull.c_str());
9757
9758 return setError(E_FAIL,
9759 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9760 medium->i_getLocationFull().c_str(),
9761 dev.uuid.raw(),
9762 mUserData->s.strName.c_str(),
9763 mData->m_strConfigFileFull.c_str());
9764 }
9765
9766 if ( !i_isSnapshotMachine()
9767 && medium->i_getChildren().size() != 0
9768 )
9769 return setError(E_FAIL,
9770 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9771 "because it has %d differencing child hard disks"),
9772 medium->i_getLocationFull().c_str(),
9773 dev.uuid.raw(),
9774 mUserData->s.strName.c_str(),
9775 mData->m_strConfigFileFull.c_str(),
9776 medium->i_getChildren().size());
9777
9778 if (i_findAttachment(*mMediumAttachments.data(),
9779 medium))
9780 return setError(E_FAIL,
9781 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9782 medium->i_getLocationFull().c_str(),
9783 dev.uuid.raw(),
9784 mUserData->s.strName.c_str(),
9785 mData->m_strConfigFileFull.c_str());
9786
9787 break;
9788 }
9789
9790 default:
9791 return setError(E_FAIL,
9792 tr("Controller '%s' port %u unit %u has device with unknown type (%d) - virtual machine '%s' ('%s')"),
9793 data.strName.c_str(), dev.lPort, dev.lDevice, dev.deviceType,
9794 mUserData->s.strName.c_str(), mData->m_strConfigFileFull.c_str());
9795 }
9796
9797 if (FAILED(rc))
9798 break;
9799
9800 /* Bandwidth groups are loaded at this point. */
9801 ComObjPtr<BandwidthGroup> pBwGroup;
9802
9803 if (!dev.strBwGroup.isEmpty())
9804 {
9805 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9806 if (FAILED(rc))
9807 return setError(E_FAIL,
9808 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9809 medium->i_getLocationFull().c_str(),
9810 dev.strBwGroup.c_str(),
9811 mUserData->s.strName.c_str(),
9812 mData->m_strConfigFileFull.c_str());
9813 pBwGroup->i_reference();
9814 }
9815
9816 const Utf8Str controllerName = aStorageController->i_getName();
9817 ComObjPtr<MediumAttachment> pAttachment;
9818 pAttachment.createObject();
9819 rc = pAttachment->init(this,
9820 medium,
9821 controllerName,
9822 dev.lPort,
9823 dev.lDevice,
9824 dev.deviceType,
9825 false,
9826 dev.fPassThrough,
9827 dev.fTempEject,
9828 dev.fNonRotational,
9829 dev.fDiscard,
9830 dev.fHotPluggable,
9831 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9832 if (FAILED(rc)) break;
9833
9834 /* associate the medium with this machine and snapshot */
9835 if (!medium.isNull())
9836 {
9837 AutoCaller medCaller(medium);
9838 if (FAILED(medCaller.rc())) return medCaller.rc();
9839 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9840
9841 if (i_isSnapshotMachine())
9842 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9843 else
9844 rc = medium->i_addBackReference(mData->mUuid);
9845 /* If the medium->addBackReference fails it sets an appropriate
9846 * error message, so no need to do any guesswork here. */
9847
9848 if (puuidRegistry)
9849 // caller wants registry ID to be set on all attached media (OVF import case)
9850 medium->i_addRegistry(*puuidRegistry);
9851 }
9852
9853 if (FAILED(rc))
9854 break;
9855
9856 /* back up mMediumAttachments to let registeredInit() properly rollback
9857 * on failure (= limited accessibility) */
9858 i_setModified(IsModified_Storage);
9859 mMediumAttachments.backup();
9860 mMediumAttachments->push_back(pAttachment);
9861 }
9862
9863 return rc;
9864}
9865
9866/**
9867 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9868 *
9869 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9870 * @param aSnapshot where to return the found snapshot
9871 * @param aSetError true to set extended error info on failure
9872 */
9873HRESULT Machine::i_findSnapshotById(const Guid &aId,
9874 ComObjPtr<Snapshot> &aSnapshot,
9875 bool aSetError /* = false */)
9876{
9877 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9878
9879 if (!mData->mFirstSnapshot)
9880 {
9881 if (aSetError)
9882 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9883 return E_FAIL;
9884 }
9885
9886 if (aId.isZero())
9887 aSnapshot = mData->mFirstSnapshot;
9888 else
9889 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9890
9891 if (!aSnapshot)
9892 {
9893 if (aSetError)
9894 return setError(E_FAIL,
9895 tr("Could not find a snapshot with UUID {%s}"),
9896 aId.toString().c_str());
9897 return E_FAIL;
9898 }
9899
9900 return S_OK;
9901}
9902
9903/**
9904 * Returns the snapshot with the given name or fails of no such snapshot.
9905 *
9906 * @param strName snapshot name to find
9907 * @param aSnapshot where to return the found snapshot
9908 * @param aSetError true to set extended error info on failure
9909 */
9910HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9911 ComObjPtr<Snapshot> &aSnapshot,
9912 bool aSetError /* = false */)
9913{
9914 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9915
9916 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9917
9918 if (!mData->mFirstSnapshot)
9919 {
9920 if (aSetError)
9921 return setError(VBOX_E_OBJECT_NOT_FOUND,
9922 tr("This machine does not have any snapshots"));
9923 return VBOX_E_OBJECT_NOT_FOUND;
9924 }
9925
9926 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9927
9928 if (!aSnapshot)
9929 {
9930 if (aSetError)
9931 return setError(VBOX_E_OBJECT_NOT_FOUND,
9932 tr("Could not find a snapshot named '%s'"), strName.c_str());
9933 return VBOX_E_OBJECT_NOT_FOUND;
9934 }
9935
9936 return S_OK;
9937}
9938
9939/**
9940 * Returns a storage controller object with the given name.
9941 *
9942 * @param aName storage controller name to find
9943 * @param aStorageController where to return the found storage controller
9944 * @param aSetError true to set extended error info on failure
9945 */
9946HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9947 ComObjPtr<StorageController> &aStorageController,
9948 bool aSetError /* = false */)
9949{
9950 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9951
9952 for (StorageControllerList::const_iterator
9953 it = mStorageControllers->begin();
9954 it != mStorageControllers->end();
9955 ++it)
9956 {
9957 if ((*it)->i_getName() == aName)
9958 {
9959 aStorageController = (*it);
9960 return S_OK;
9961 }
9962 }
9963
9964 if (aSetError)
9965 return setError(VBOX_E_OBJECT_NOT_FOUND,
9966 tr("Could not find a storage controller named '%s'"),
9967 aName.c_str());
9968 return VBOX_E_OBJECT_NOT_FOUND;
9969}
9970
9971/**
9972 * Returns a USB controller object with the given name.
9973 *
9974 * @param aName USB controller name to find
9975 * @param aUSBController where to return the found USB controller
9976 * @param aSetError true to set extended error info on failure
9977 */
9978HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9979 ComObjPtr<USBController> &aUSBController,
9980 bool aSetError /* = false */)
9981{
9982 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9983
9984 for (USBControllerList::const_iterator
9985 it = mUSBControllers->begin();
9986 it != mUSBControllers->end();
9987 ++it)
9988 {
9989 if ((*it)->i_getName() == aName)
9990 {
9991 aUSBController = (*it);
9992 return S_OK;
9993 }
9994 }
9995
9996 if (aSetError)
9997 return setError(VBOX_E_OBJECT_NOT_FOUND,
9998 tr("Could not find a storage controller named '%s'"),
9999 aName.c_str());
10000 return VBOX_E_OBJECT_NOT_FOUND;
10001}
10002
10003/**
10004 * Returns the number of USB controller instance of the given type.
10005 *
10006 * @param enmType USB controller type.
10007 */
10008ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
10009{
10010 ULONG cCtrls = 0;
10011
10012 for (USBControllerList::const_iterator
10013 it = mUSBControllers->begin();
10014 it != mUSBControllers->end();
10015 ++it)
10016 {
10017 if ((*it)->i_getControllerType() == enmType)
10018 cCtrls++;
10019 }
10020
10021 return cCtrls;
10022}
10023
10024HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
10025 MediumAttachmentList &atts)
10026{
10027 AutoCaller autoCaller(this);
10028 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10029
10030 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10031
10032 for (MediumAttachmentList::const_iterator
10033 it = mMediumAttachments->begin();
10034 it != mMediumAttachments->end();
10035 ++it)
10036 {
10037 const ComObjPtr<MediumAttachment> &pAtt = *it;
10038 // should never happen, but deal with NULL pointers in the list.
10039 AssertContinue(!pAtt.isNull());
10040
10041 // getControllerName() needs caller+read lock
10042 AutoCaller autoAttCaller(pAtt);
10043 if (FAILED(autoAttCaller.rc()))
10044 {
10045 atts.clear();
10046 return autoAttCaller.rc();
10047 }
10048 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
10049
10050 if (pAtt->i_getControllerName() == aName)
10051 atts.push_back(pAtt);
10052 }
10053
10054 return S_OK;
10055}
10056
10057
10058/**
10059 * Helper for #i_saveSettings. Cares about renaming the settings directory and
10060 * file if the machine name was changed and about creating a new settings file
10061 * if this is a new machine.
10062 *
10063 * @note Must be never called directly but only from #saveSettings().
10064 */
10065HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
10066 bool *pfSettingsFileIsNew)
10067{
10068 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10069
10070 HRESULT rc = S_OK;
10071
10072 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
10073 /// @todo need to handle primary group change, too
10074
10075 /* attempt to rename the settings file if machine name is changed */
10076 if ( mUserData->s.fNameSync
10077 && mUserData.isBackedUp()
10078 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
10079 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
10080 )
10081 {
10082 bool dirRenamed = false;
10083 bool fileRenamed = false;
10084
10085 Utf8Str configFile, newConfigFile;
10086 Utf8Str configFilePrev, newConfigFilePrev;
10087 Utf8Str NVRAMFile, newNVRAMFile;
10088 Utf8Str configDir, newConfigDir;
10089
10090 do
10091 {
10092 int vrc = VINF_SUCCESS;
10093
10094 Utf8Str name = mUserData.backedUpData()->s.strName;
10095 Utf8Str newName = mUserData->s.strName;
10096 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
10097 if (group == "/")
10098 group.setNull();
10099 Utf8Str newGroup = mUserData->s.llGroups.front();
10100 if (newGroup == "/")
10101 newGroup.setNull();
10102
10103 configFile = mData->m_strConfigFileFull;
10104
10105 /* first, rename the directory if it matches the group and machine name */
10106 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
10107 /** @todo hack, make somehow use of ComposeMachineFilename */
10108 if (mUserData->s.fDirectoryIncludesUUID)
10109 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10110 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
10111 /** @todo hack, make somehow use of ComposeMachineFilename */
10112 if (mUserData->s.fDirectoryIncludesUUID)
10113 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10114 configDir = configFile;
10115 configDir.stripFilename();
10116 newConfigDir = configDir;
10117 if ( configDir.length() >= groupPlusName.length()
10118 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
10119 groupPlusName.c_str()))
10120 {
10121 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10122 Utf8Str newConfigBaseDir(newConfigDir);
10123 newConfigDir.append(newGroupPlusName);
10124 /* consistency: use \ if appropriate on the platform */
10125 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10126 /* new dir and old dir cannot be equal here because of 'if'
10127 * above and because name != newName */
10128 Assert(configDir != newConfigDir);
10129 if (!fSettingsFileIsNew)
10130 {
10131 /* perform real rename only if the machine is not new */
10132 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10133 if ( vrc == VERR_FILE_NOT_FOUND
10134 || vrc == VERR_PATH_NOT_FOUND)
10135 {
10136 /* create the parent directory, then retry renaming */
10137 Utf8Str parent(newConfigDir);
10138 parent.stripFilename();
10139 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10140 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10141 }
10142 if (RT_FAILURE(vrc))
10143 {
10144 rc = setErrorBoth(E_FAIL, vrc,
10145 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10146 configDir.c_str(),
10147 newConfigDir.c_str(),
10148 vrc);
10149 break;
10150 }
10151 /* delete subdirectories which are no longer needed */
10152 Utf8Str dir(configDir);
10153 dir.stripFilename();
10154 while (dir != newConfigBaseDir && dir != ".")
10155 {
10156 vrc = RTDirRemove(dir.c_str());
10157 if (RT_FAILURE(vrc))
10158 break;
10159 dir.stripFilename();
10160 }
10161 dirRenamed = true;
10162 }
10163 }
10164
10165 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10166
10167 /* then try to rename the settings file itself */
10168 if (newConfigFile != configFile)
10169 {
10170 /* get the path to old settings file in renamed directory */
10171 Assert(mData->m_strConfigFileFull == configFile);
10172 configFile.printf("%s%c%s",
10173 newConfigDir.c_str(),
10174 RTPATH_DELIMITER,
10175 RTPathFilename(mData->m_strConfigFileFull.c_str()));
10176 if (!fSettingsFileIsNew)
10177 {
10178 /* perform real rename only if the machine is not new */
10179 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10180 if (RT_FAILURE(vrc))
10181 {
10182 rc = setErrorBoth(E_FAIL, vrc,
10183 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10184 configFile.c_str(),
10185 newConfigFile.c_str(),
10186 vrc);
10187 break;
10188 }
10189 fileRenamed = true;
10190 configFilePrev = configFile;
10191 configFilePrev += "-prev";
10192 newConfigFilePrev = newConfigFile;
10193 newConfigFilePrev += "-prev";
10194 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10195 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
10196 if (NVRAMFile.isNotEmpty())
10197 {
10198 // in the NVRAM file path, replace the old directory with the new directory
10199 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
10200 {
10201 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
10202 NVRAMFile = newConfigDir + strNVRAMFile;
10203 }
10204 newNVRAMFile = newConfigFile;
10205 newNVRAMFile.stripSuffix();
10206 newNVRAMFile += ".nvram";
10207 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
10208 }
10209 }
10210 }
10211
10212 // update m_strConfigFileFull amd mConfigFile
10213 mData->m_strConfigFileFull = newConfigFile;
10214 // compute the relative path too
10215 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10216
10217 // store the old and new so that VirtualBox::i_saveSettings() can update
10218 // the media registry
10219 if ( mData->mRegistered
10220 && (configDir != newConfigDir || configFile != newConfigFile))
10221 {
10222 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10223
10224 if (pfNeedsGlobalSaveSettings)
10225 *pfNeedsGlobalSaveSettings = true;
10226 }
10227
10228 // in the saved state file path, replace the old directory with the new directory
10229 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10230 {
10231 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
10232 mSSData->strStateFilePath = newConfigDir + strStateFileName;
10233 }
10234 if (newNVRAMFile.isNotEmpty())
10235 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
10236
10237 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
10238 if (mData->mFirstSnapshot)
10239 {
10240 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10241 newConfigDir.c_str());
10242 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
10243 newConfigDir.c_str());
10244 }
10245 }
10246 while (0);
10247
10248 if (FAILED(rc))
10249 {
10250 /* silently try to rename everything back */
10251 if (fileRenamed)
10252 {
10253 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10254 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10255 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
10256 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
10257 }
10258 if (dirRenamed)
10259 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10260 }
10261
10262 if (FAILED(rc)) return rc;
10263 }
10264
10265 if (fSettingsFileIsNew)
10266 {
10267 /* create a virgin config file */
10268 int vrc = VINF_SUCCESS;
10269
10270 /* ensure the settings directory exists */
10271 Utf8Str path(mData->m_strConfigFileFull);
10272 path.stripFilename();
10273 if (!RTDirExists(path.c_str()))
10274 {
10275 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10276 if (RT_FAILURE(vrc))
10277 {
10278 return setErrorBoth(E_FAIL, vrc,
10279 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10280 path.c_str(),
10281 vrc);
10282 }
10283 }
10284
10285 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10286 path = mData->m_strConfigFileFull;
10287 RTFILE f = NIL_RTFILE;
10288 vrc = RTFileOpen(&f, path.c_str(),
10289 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10290 if (RT_FAILURE(vrc))
10291 return setErrorBoth(E_FAIL, vrc,
10292 tr("Could not create the settings file '%s' (%Rrc)"),
10293 path.c_str(),
10294 vrc);
10295 RTFileClose(f);
10296 }
10297 if (pfSettingsFileIsNew)
10298 *pfSettingsFileIsNew = fSettingsFileIsNew;
10299
10300 return rc;
10301}
10302
10303/**
10304 * Saves and commits machine data, user data and hardware data.
10305 *
10306 * Note that on failure, the data remains uncommitted.
10307 *
10308 * @a aFlags may combine the following flags:
10309 *
10310 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10311 * Used when saving settings after an operation that makes them 100%
10312 * correspond to the settings from the current snapshot.
10313 * - SaveS_Force: settings will be saved without doing a deep compare of the
10314 * settings structures. This is used when this is called because snapshots
10315 * have changed to avoid the overhead of the deep compare.
10316 *
10317 * @note Must be called from under this object's write lock. Locks children for
10318 * writing.
10319 *
10320 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10321 * initialized to false and that will be set to true by this function if
10322 * the caller must invoke VirtualBox::i_saveSettings() because the global
10323 * settings have changed. This will happen if a machine rename has been
10324 * saved and the global machine and media registries will therefore need
10325 * updating.
10326 * @param alock Reference to the lock for this machine object.
10327 * @param aFlags Flags.
10328 */
10329HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10330 AutoWriteLock &alock,
10331 int aFlags /*= 0*/)
10332{
10333 LogFlowThisFuncEnter();
10334
10335 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10336
10337 /* make sure child objects are unable to modify the settings while we are
10338 * saving them */
10339 i_ensureNoStateDependencies(alock);
10340
10341 AssertReturn(!i_isSnapshotMachine(),
10342 E_FAIL);
10343
10344 if (!mData->mAccessible)
10345 return setError(VBOX_E_INVALID_VM_STATE,
10346 tr("The machine is not accessible, so cannot save settings"));
10347
10348 HRESULT rc = S_OK;
10349 PCVBOXCRYPTOIF pCryptoIf = NULL;
10350 const char *pszPassword = NULL;
10351 SecretKey *pKey = NULL;
10352
10353#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10354 if (mData->mstrKeyId.isNotEmpty())
10355 {
10356 /* VM is going to be encrypted. */
10357 alock.release(); /** @todo Revise the locking. */
10358 rc = mParent->i_retainCryptoIf(&pCryptoIf);
10359 alock.acquire();
10360 if (FAILED(rc)) return rc; /* Error is set. */
10361
10362 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
10363 if (RT_SUCCESS(vrc))
10364 pszPassword = (const char *)pKey->getKeyBuffer();
10365 else
10366 {
10367 mParent->i_releaseCryptoIf(pCryptoIf);
10368 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
10369 tr("Failed to retain VM encryption password using ID '%s' with %Rrc"),
10370 mData->mstrKeyId.c_str(), vrc);
10371 }
10372 }
10373#else
10374 RT_NOREF(pKey);
10375#endif
10376
10377 bool fNeedsWrite = false;
10378 bool fSettingsFileIsNew = false;
10379
10380 /* First, prepare to save settings. It will care about renaming the
10381 * settings directory and file if the machine name was changed and about
10382 * creating a new settings file if this is a new machine. */
10383 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings,
10384 &fSettingsFileIsNew);
10385 if (FAILED(rc))
10386 {
10387#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10388 if (pCryptoIf)
10389 {
10390 alock.release(); /** @todo Revise the locking. */
10391 mParent->i_releaseCryptoIf(pCryptoIf);
10392 alock.acquire();
10393 }
10394 if (pKey)
10395 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10396#endif
10397 return rc;
10398 }
10399
10400 // keep a pointer to the current settings structures
10401 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10402 settings::MachineConfigFile *pNewConfig = NULL;
10403
10404 try
10405 {
10406 // make a fresh one to have everyone write stuff into
10407 pNewConfig = new settings::MachineConfigFile(NULL);
10408 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10409#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10410 pNewConfig->strKeyId = mData->mstrKeyId;
10411 pNewConfig->strKeyStore = mData->mstrKeyStore;
10412#endif
10413
10414 // now go and copy all the settings data from COM to the settings structures
10415 // (this calls i_saveSettings() on all the COM objects in the machine)
10416 i_copyMachineDataToSettings(*pNewConfig);
10417
10418 if (aFlags & SaveS_ResetCurStateModified)
10419 {
10420 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10421 mData->mCurrentStateModified = FALSE;
10422 fNeedsWrite = true; // always, no need to compare
10423 }
10424 else if (aFlags & SaveS_Force)
10425 {
10426 fNeedsWrite = true; // always, no need to compare
10427 }
10428 else
10429 {
10430 if (!mData->mCurrentStateModified)
10431 {
10432 // do a deep compare of the settings that we just saved with the settings
10433 // previously stored in the config file; this invokes MachineConfigFile::operator==
10434 // which does a deep compare of all the settings, which is expensive but less expensive
10435 // than writing out XML in vain
10436 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10437
10438 // could still be modified if any settings changed
10439 mData->mCurrentStateModified = fAnySettingsChanged;
10440
10441 fNeedsWrite = fAnySettingsChanged;
10442 }
10443 else
10444 fNeedsWrite = true;
10445 }
10446
10447 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10448
10449 if (fNeedsWrite)
10450 {
10451 // now spit it all out!
10452 pNewConfig->write(mData->m_strConfigFileFull, pCryptoIf, pszPassword);
10453 if (aFlags & SaveS_RemoveBackup)
10454 i_deleteFile(mData->m_strConfigFileFull + "-prev", true /* fIgnoreFailures */);
10455 }
10456
10457 mData->pMachineConfigFile = pNewConfig;
10458 delete pOldConfig;
10459 i_commit();
10460
10461 // after saving settings, we are no longer different from the XML on disk
10462 mData->flModifications = 0;
10463 }
10464 catch (HRESULT err)
10465 {
10466 // we assume that error info is set by the thrower
10467 rc = err;
10468
10469 // delete any newly created settings file
10470 if (fSettingsFileIsNew)
10471 i_deleteFile(mData->m_strConfigFileFull, true /* fIgnoreFailures */);
10472
10473 // restore old config
10474 delete pNewConfig;
10475 mData->pMachineConfigFile = pOldConfig;
10476 }
10477 catch (...)
10478 {
10479 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10480 }
10481
10482#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10483 if (pCryptoIf)
10484 {
10485 alock.release(); /** @todo Revise the locking. */
10486 mParent->i_releaseCryptoIf(pCryptoIf);
10487 alock.acquire();
10488 }
10489 if (pKey)
10490 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10491#endif
10492
10493 if (fNeedsWrite)
10494 {
10495 /* Fire the data change event, even on failure (since we've already
10496 * committed all data). This is done only for SessionMachines because
10497 * mutable Machine instances are always not registered (i.e. private
10498 * to the client process that creates them) and thus don't need to
10499 * inform callbacks. */
10500 if (i_isSessionMachine())
10501 mParent->i_onMachineDataChanged(mData->mUuid);
10502 }
10503
10504 LogFlowThisFunc(("rc=%08X\n", rc));
10505 LogFlowThisFuncLeave();
10506 return rc;
10507}
10508
10509/**
10510 * Implementation for saving the machine settings into the given
10511 * settings::MachineConfigFile instance. This copies machine extradata
10512 * from the previous machine config file in the instance data, if any.
10513 *
10514 * This gets called from two locations:
10515 *
10516 * -- Machine::i_saveSettings(), during the regular XML writing;
10517 *
10518 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10519 * exported to OVF and we write the VirtualBox proprietary XML
10520 * into a <vbox:Machine> tag.
10521 *
10522 * This routine fills all the fields in there, including snapshots, *except*
10523 * for the following:
10524 *
10525 * -- fCurrentStateModified. There is some special logic associated with that.
10526 *
10527 * The caller can then call MachineConfigFile::write() or do something else
10528 * with it.
10529 *
10530 * Caller must hold the machine lock!
10531 *
10532 * This throws XML errors and HRESULT, so the caller must have a catch block!
10533 */
10534void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10535{
10536 // deep copy extradata, being extra careful with self assignment (the STL
10537 // map assignment on Mac OS X clang based Xcode isn't checking)
10538 if (&config != mData->pMachineConfigFile)
10539 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10540
10541 config.uuid = mData->mUuid;
10542
10543 // copy name, description, OS type, teleport, UTC etc.
10544 config.machineUserData = mUserData->s;
10545
10546#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10547 config.strStateKeyId = mSSData->strStateKeyId;
10548 config.strStateKeyStore = mSSData->strStateKeyStore;
10549 config.strLogKeyId = mData->mstrLogKeyId;
10550 config.strLogKeyStore = mData->mstrLogKeyStore;
10551#endif
10552
10553 if ( mData->mMachineState == MachineState_Saved
10554 || mData->mMachineState == MachineState_AbortedSaved
10555 || mData->mMachineState == MachineState_Restoring
10556 // when doing certain snapshot operations we may or may not have
10557 // a saved state in the current state, so keep everything as is
10558 || ( ( mData->mMachineState == MachineState_Snapshotting
10559 || mData->mMachineState == MachineState_DeletingSnapshot
10560 || mData->mMachineState == MachineState_RestoringSnapshot)
10561 && (!mSSData->strStateFilePath.isEmpty())
10562 )
10563 )
10564 {
10565 Assert(!mSSData->strStateFilePath.isEmpty());
10566 /* try to make the file name relative to the settings file dir */
10567 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10568 }
10569 else
10570 {
10571 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10572 config.strStateFile.setNull();
10573 }
10574
10575 if (mData->mCurrentSnapshot)
10576 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10577 else
10578 config.uuidCurrentSnapshot.clear();
10579
10580 config.timeLastStateChange = mData->mLastStateChange;
10581 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10582 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10583
10584 HRESULT hrc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart, config.recordingSettings);
10585 if (FAILED(hrc)) throw hrc;
10586
10587 // save machine's media registry if this is VirtualBox 4.0 or later
10588 if (config.canHaveOwnMediaRegistry())
10589 {
10590 // determine machine folder
10591 Utf8Str strMachineFolder = i_getSettingsFileFull();
10592 strMachineFolder.stripFilename();
10593 mParent->i_saveMediaRegistry(config.mediaRegistry,
10594 i_getId(), // only media with registry ID == machine UUID
10595 strMachineFolder);
10596 // this throws HRESULT
10597 }
10598
10599 // save snapshots
10600 hrc = i_saveAllSnapshots(config);
10601 if (FAILED(hrc)) throw hrc;
10602}
10603
10604/**
10605 * Saves all snapshots of the machine into the given machine config file. Called
10606 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10607 * @param config
10608 * @return
10609 */
10610HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10611{
10612 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10613
10614 HRESULT rc = S_OK;
10615
10616 try
10617 {
10618 config.llFirstSnapshot.clear();
10619
10620 if (mData->mFirstSnapshot)
10621 {
10622 // the settings use a list for "the first snapshot"
10623 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10624
10625 // get reference to the snapshot on the list and work on that
10626 // element straight in the list to avoid excessive copying later
10627 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10628 if (FAILED(rc)) throw rc;
10629 }
10630
10631// if (mType == IsSessionMachine)
10632// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10633
10634 }
10635 catch (HRESULT err)
10636 {
10637 /* we assume that error info is set by the thrower */
10638 rc = err;
10639 }
10640 catch (...)
10641 {
10642 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10643 }
10644
10645 return rc;
10646}
10647
10648/**
10649 * Saves the VM hardware configuration. It is assumed that the
10650 * given node is empty.
10651 *
10652 * @param data Reference to the settings object for the hardware config.
10653 * @param pDbg Pointer to the settings object for the debugging config
10654 * which happens to live in mHWData.
10655 * @param pAutostart Pointer to the settings object for the autostart config
10656 * which happens to live in mHWData.
10657 * @param recording Reference to reecording settings.
10658 */
10659HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10660 settings::Autostart *pAutostart, settings::RecordingSettings &recording)
10661{
10662 HRESULT rc = S_OK;
10663
10664 try
10665 {
10666 /* The hardware version attribute (optional).
10667 Automatically upgrade from 1 to current default hardware version
10668 when there is no saved state. (ugly!) */
10669 if ( mHWData->mHWVersion == "1"
10670 && mSSData->strStateFilePath.isEmpty()
10671 )
10672 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10673
10674 data.strVersion = mHWData->mHWVersion;
10675 data.uuid = mHWData->mHardwareUUID;
10676
10677 // CPU
10678 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10679 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10680 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10681 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10682 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10683 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10684 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10685 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10686 data.fPAE = !!mHWData->mPAEEnabled;
10687 data.enmLongMode = mHWData->mLongMode;
10688 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10689 data.fAPIC = !!mHWData->mAPIC;
10690 data.fX2APIC = !!mHWData->mX2APIC;
10691 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10692 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10693 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10694 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10695 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10696 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10697 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10698 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10699 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10700 data.cCPUs = mHWData->mCPUCount;
10701 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10702 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10703 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10704 data.strCpuProfile = mHWData->mCpuProfile;
10705
10706 data.llCpus.clear();
10707 if (data.fCpuHotPlug)
10708 {
10709 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10710 {
10711 if (mHWData->mCPUAttached[idx])
10712 {
10713 settings::Cpu cpu;
10714 cpu.ulId = idx;
10715 data.llCpus.push_back(cpu);
10716 }
10717 }
10718 }
10719
10720 /* Standard and Extended CPUID leafs. */
10721 data.llCpuIdLeafs.clear();
10722 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10723
10724 // memory
10725 data.ulMemorySizeMB = mHWData->mMemorySize;
10726 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10727
10728 // firmware
10729 data.firmwareType = mHWData->mFirmwareType;
10730
10731 // HID
10732 data.pointingHIDType = mHWData->mPointingHIDType;
10733 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10734
10735 // chipset
10736 data.chipsetType = mHWData->mChipsetType;
10737
10738 // iommu
10739 data.iommuType = mHWData->mIommuType;
10740
10741 // paravirt
10742 data.paravirtProvider = mHWData->mParavirtProvider;
10743 data.strParavirtDebug = mHWData->mParavirtDebug;
10744
10745 // emulated USB card reader
10746 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10747
10748 // HPET
10749 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10750
10751 // boot order
10752 data.mapBootOrder.clear();
10753 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10754 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10755
10756 /* VRDEServer settings (optional) */
10757 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10758 if (FAILED(rc)) throw rc;
10759
10760 /* BIOS settings (required) */
10761 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10762 if (FAILED(rc)) throw rc;
10763
10764 /* Recording settings. */
10765 rc = mRecordingSettings->i_saveSettings(recording);
10766 if (FAILED(rc)) throw rc;
10767
10768 /* Trusted Platform Module settings (required) */
10769 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10770 if (FAILED(rc)) throw rc;
10771
10772 /* NVRAM settings (required) */
10773 rc = mNvramStore->i_saveSettings(data.nvramSettings);
10774 if (FAILED(rc)) throw rc;
10775
10776 /* GraphicsAdapter settings (required) */
10777 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10778 if (FAILED(rc)) throw rc;
10779
10780 /* USB Controller (required) */
10781 data.usbSettings.llUSBControllers.clear();
10782 for (USBControllerList::const_iterator
10783 it = mUSBControllers->begin();
10784 it != mUSBControllers->end();
10785 ++it)
10786 {
10787 ComObjPtr<USBController> ctrl = *it;
10788 settings::USBController settingsCtrl;
10789
10790 settingsCtrl.strName = ctrl->i_getName();
10791 settingsCtrl.enmType = ctrl->i_getControllerType();
10792
10793 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10794 }
10795
10796 /* USB device filters (required) */
10797 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10798 if (FAILED(rc)) throw rc;
10799
10800 /* Network adapters (required) */
10801 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10802 data.llNetworkAdapters.clear();
10803 /* Write out only the nominal number of network adapters for this
10804 * chipset type. Since Machine::commit() hasn't been called there
10805 * may be extra NIC settings in the vector. */
10806 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10807 {
10808 settings::NetworkAdapter nic;
10809 nic.ulSlot = (uint32_t)slot;
10810 /* paranoia check... must not be NULL, but must not crash either. */
10811 if (mNetworkAdapters[slot])
10812 {
10813 if (mNetworkAdapters[slot]->i_hasDefaults())
10814 continue;
10815
10816 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10817 if (FAILED(rc)) throw rc;
10818
10819 data.llNetworkAdapters.push_back(nic);
10820 }
10821 }
10822
10823 /* Serial ports */
10824 data.llSerialPorts.clear();
10825 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10826 {
10827 if (mSerialPorts[slot]->i_hasDefaults())
10828 continue;
10829
10830 settings::SerialPort s;
10831 s.ulSlot = slot;
10832 rc = mSerialPorts[slot]->i_saveSettings(s);
10833 if (FAILED(rc)) return rc;
10834
10835 data.llSerialPorts.push_back(s);
10836 }
10837
10838 /* Parallel ports */
10839 data.llParallelPorts.clear();
10840 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10841 {
10842 if (mParallelPorts[slot]->i_hasDefaults())
10843 continue;
10844
10845 settings::ParallelPort p;
10846 p.ulSlot = slot;
10847 rc = mParallelPorts[slot]->i_saveSettings(p);
10848 if (FAILED(rc)) return rc;
10849
10850 data.llParallelPorts.push_back(p);
10851 }
10852
10853 /* Audio settings */
10854 rc = mAudioSettings->i_saveSettings(data.audioAdapter);
10855 if (FAILED(rc)) return rc;
10856
10857 rc = i_saveStorageControllers(data.storage);
10858 if (FAILED(rc)) return rc;
10859
10860 /* Shared folders */
10861 data.llSharedFolders.clear();
10862 for (HWData::SharedFolderList::const_iterator
10863 it = mHWData->mSharedFolders.begin();
10864 it != mHWData->mSharedFolders.end();
10865 ++it)
10866 {
10867 SharedFolder *pSF = *it;
10868 AutoCaller sfCaller(pSF);
10869 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10870 settings::SharedFolder sf;
10871 sf.strName = pSF->i_getName();
10872 sf.strHostPath = pSF->i_getHostPath();
10873 sf.fWritable = !!pSF->i_isWritable();
10874 sf.fAutoMount = !!pSF->i_isAutoMounted();
10875 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10876
10877 data.llSharedFolders.push_back(sf);
10878 }
10879
10880 // clipboard
10881 data.clipboardMode = mHWData->mClipboardMode;
10882 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10883
10884 // drag'n'drop
10885 data.dndMode = mHWData->mDnDMode;
10886
10887 /* Guest */
10888 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10889
10890 // IO settings
10891 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10892 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10893
10894 /* BandwidthControl (required) */
10895 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10896 if (FAILED(rc)) throw rc;
10897
10898 /* Host PCI devices */
10899 data.pciAttachments.clear();
10900 for (HWData::PCIDeviceAssignmentList::const_iterator
10901 it = mHWData->mPCIDeviceAssignments.begin();
10902 it != mHWData->mPCIDeviceAssignments.end();
10903 ++it)
10904 {
10905 ComObjPtr<PCIDeviceAttachment> pda = *it;
10906 settings::HostPCIDeviceAttachment hpda;
10907
10908 rc = pda->i_saveSettings(hpda);
10909 if (FAILED(rc)) throw rc;
10910
10911 data.pciAttachments.push_back(hpda);
10912 }
10913
10914 // guest properties
10915 data.llGuestProperties.clear();
10916#ifdef VBOX_WITH_GUEST_PROPS
10917 for (HWData::GuestPropertyMap::const_iterator
10918 it = mHWData->mGuestProperties.begin();
10919 it != mHWData->mGuestProperties.end();
10920 ++it)
10921 {
10922 HWData::GuestProperty property = it->second;
10923
10924 /* Remove transient guest properties at shutdown unless we
10925 * are saving state. Note that restoring snapshot intentionally
10926 * keeps them, they will be removed if appropriate once the final
10927 * machine state is set (as crashes etc. need to work). */
10928 if ( ( mData->mMachineState == MachineState_PoweredOff
10929 || mData->mMachineState == MachineState_Aborted
10930 || mData->mMachineState == MachineState_Teleported)
10931 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10932 continue;
10933 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10934 prop.strName = it->first;
10935 prop.strValue = property.strValue;
10936 prop.timestamp = (uint64_t)property.mTimestamp;
10937 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10938 GuestPropWriteFlags(property.mFlags, szFlags);
10939 prop.strFlags = szFlags;
10940
10941 data.llGuestProperties.push_back(prop);
10942 }
10943
10944 /* I presume this doesn't require a backup(). */
10945 mData->mGuestPropertiesModified = FALSE;
10946#endif /* VBOX_WITH_GUEST_PROPS defined */
10947
10948 *pDbg = mHWData->mDebugging;
10949 *pAutostart = mHWData->mAutostart;
10950
10951 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10952 }
10953 catch (std::bad_alloc &)
10954 {
10955 return E_OUTOFMEMORY;
10956 }
10957
10958 AssertComRC(rc);
10959 return rc;
10960}
10961
10962/**
10963 * Saves the storage controller configuration.
10964 *
10965 * @param data storage settings.
10966 */
10967HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10968{
10969 data.llStorageControllers.clear();
10970
10971 for (StorageControllerList::const_iterator
10972 it = mStorageControllers->begin();
10973 it != mStorageControllers->end();
10974 ++it)
10975 {
10976 HRESULT rc;
10977 ComObjPtr<StorageController> pCtl = *it;
10978
10979 settings::StorageController ctl;
10980 ctl.strName = pCtl->i_getName();
10981 ctl.controllerType = pCtl->i_getControllerType();
10982 ctl.storageBus = pCtl->i_getStorageBus();
10983 ctl.ulInstance = pCtl->i_getInstance();
10984 ctl.fBootable = pCtl->i_getBootable();
10985
10986 /* Save the port count. */
10987 ULONG portCount;
10988 rc = pCtl->COMGETTER(PortCount)(&portCount);
10989 ComAssertComRCRet(rc, rc);
10990 ctl.ulPortCount = portCount;
10991
10992 /* Save fUseHostIOCache */
10993 BOOL fUseHostIOCache;
10994 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10995 ComAssertComRCRet(rc, rc);
10996 ctl.fUseHostIOCache = !!fUseHostIOCache;
10997
10998 /* save the devices now. */
10999 rc = i_saveStorageDevices(pCtl, ctl);
11000 ComAssertComRCRet(rc, rc);
11001
11002 data.llStorageControllers.push_back(ctl);
11003 }
11004
11005 return S_OK;
11006}
11007
11008/**
11009 * Saves the hard disk configuration.
11010 */
11011HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
11012 settings::StorageController &data)
11013{
11014 MediumAttachmentList atts;
11015
11016 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
11017 if (FAILED(rc)) return rc;
11018
11019 data.llAttachedDevices.clear();
11020 for (MediumAttachmentList::const_iterator
11021 it = atts.begin();
11022 it != atts.end();
11023 ++it)
11024 {
11025 settings::AttachedDevice dev;
11026 IMediumAttachment *iA = *it;
11027 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
11028 Medium *pMedium = pAttach->i_getMedium();
11029
11030 dev.deviceType = pAttach->i_getType();
11031 dev.lPort = pAttach->i_getPort();
11032 dev.lDevice = pAttach->i_getDevice();
11033 dev.fPassThrough = pAttach->i_getPassthrough();
11034 dev.fHotPluggable = pAttach->i_getHotPluggable();
11035 if (pMedium)
11036 {
11037 if (pMedium->i_isHostDrive())
11038 dev.strHostDriveSrc = pMedium->i_getLocationFull();
11039 else
11040 dev.uuid = pMedium->i_getId();
11041 dev.fTempEject = pAttach->i_getTempEject();
11042 dev.fNonRotational = pAttach->i_getNonRotational();
11043 dev.fDiscard = pAttach->i_getDiscard();
11044 }
11045
11046 dev.strBwGroup = pAttach->i_getBandwidthGroup();
11047
11048 data.llAttachedDevices.push_back(dev);
11049 }
11050
11051 return S_OK;
11052}
11053
11054/**
11055 * Saves machine state settings as defined by aFlags
11056 * (SaveSTS_* values).
11057 *
11058 * @param aFlags Combination of SaveSTS_* flags.
11059 *
11060 * @note Locks objects for writing.
11061 */
11062HRESULT Machine::i_saveStateSettings(int aFlags)
11063{
11064 if (aFlags == 0)
11065 return S_OK;
11066
11067 AutoCaller autoCaller(this);
11068 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11069
11070 /* This object's write lock is also necessary to serialize file access
11071 * (prevent concurrent reads and writes) */
11072 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11073
11074 HRESULT rc = S_OK;
11075
11076 Assert(mData->pMachineConfigFile);
11077
11078 try
11079 {
11080 if (aFlags & SaveSTS_CurStateModified)
11081 mData->pMachineConfigFile->fCurrentStateModified = true;
11082
11083 if (aFlags & SaveSTS_StateFilePath)
11084 {
11085 if (!mSSData->strStateFilePath.isEmpty())
11086 /* try to make the file name relative to the settings file dir */
11087 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
11088 else
11089 mData->pMachineConfigFile->strStateFile.setNull();
11090 }
11091
11092 if (aFlags & SaveSTS_StateTimeStamp)
11093 {
11094 Assert( mData->mMachineState != MachineState_Aborted
11095 || mSSData->strStateFilePath.isEmpty());
11096
11097 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
11098
11099 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
11100 || mData->mMachineState == MachineState_AbortedSaved);
11101/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
11102 }
11103
11104 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
11105 }
11106 catch (...)
11107 {
11108 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
11109 }
11110
11111 return rc;
11112}
11113
11114/**
11115 * Ensures that the given medium is added to a media registry. If this machine
11116 * was created with 4.0 or later, then the machine registry is used. Otherwise
11117 * the global VirtualBox media registry is used.
11118 *
11119 * Caller must NOT hold machine lock, media tree or any medium locks!
11120 *
11121 * @param pMedium
11122 */
11123void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
11124{
11125 /* Paranoia checks: do not hold machine or media tree locks. */
11126 AssertReturnVoid(!isWriteLockOnCurrentThread());
11127 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
11128
11129 ComObjPtr<Medium> pBase;
11130 {
11131 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11132 pBase = pMedium->i_getBase();
11133 }
11134
11135 /* Paranoia checks: do not hold medium locks. */
11136 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
11137 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
11138
11139 // decide which medium registry to use now that the medium is attached:
11140 Guid uuid;
11141 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
11142 if (fCanHaveOwnMediaRegistry)
11143 // machine XML is VirtualBox 4.0 or higher:
11144 uuid = i_getId(); // machine UUID
11145 else
11146 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
11147
11148 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
11149 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11150 if (pMedium->i_addRegistry(uuid))
11151 mParent->i_markRegistryModified(uuid);
11152
11153 /* For more complex hard disk structures it can happen that the base
11154 * medium isn't yet associated with any medium registry. Do that now. */
11155 if (pMedium != pBase)
11156 {
11157 /* Tree lock needed by Medium::addRegistryAll. */
11158 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11159 if (fCanHaveOwnMediaRegistry && pBase->i_removeRegistryAll(mParent->i_getGlobalRegistryId()))
11160 {
11161 treeLock.release();
11162 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11163 treeLock.acquire();
11164 }
11165 if (pBase->i_addRegistryAll(uuid))
11166 {
11167 treeLock.release();
11168 mParent->i_markRegistryModified(uuid);
11169 }
11170 }
11171}
11172
11173/**
11174 * Physically deletes a file belonging to a machine.
11175 *
11176 * @returns HRESULT
11177 * @retval VBOX_E_FILE_ERROR on failure.
11178 * @param strFile File to delete.
11179 * @param fIgnoreFailures Whether to ignore deletion failures. Defaults to \c false.
11180 * VERR_FILE_NOT_FOUND and VERR_PATH_NOT_FOUND always will be ignored.
11181 * @param strWhat File hint which will be used when setting an error. Optional.
11182 * @param prc Where to return IPRT's error code on failure. Optional and can be NULL.
11183 */
11184HRESULT Machine::i_deleteFile(const Utf8Str &strFile, bool fIgnoreFailures /* = false */,
11185 const Utf8Str &strWhat /* = "" */, int *prc /* = NULL */)
11186{
11187 AssertReturn(strFile.isNotEmpty(), E_INVALIDARG);
11188
11189 HRESULT hrc = S_OK;
11190
11191 LogFunc(("Deleting file '%s'\n", strFile.c_str()));
11192
11193 int vrc = RTFileDelete(strFile.c_str());
11194 if (RT_FAILURE(vrc))
11195 {
11196 if ( !fIgnoreFailures
11197 /* Don't (externally) bitch about stuff which doesn't exist. */
11198 && ( vrc != VERR_FILE_NOT_FOUND
11199 && vrc != VERR_PATH_NOT_FOUND
11200 )
11201 )
11202 {
11203 LogRel(("Deleting file '%s' failed: %Rrc\n", strFile.c_str(), vrc));
11204
11205 Utf8StrFmt strError("Error deleting %s '%s' (%Rrc)",
11206 strWhat.isEmpty() ? tr("file") : strWhat.c_str(), strFile.c_str(), vrc);
11207 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, strError.c_str(),
11208 strFile.c_str(), vrc);
11209 }
11210
11211 if (prc)
11212 *prc = vrc;
11213 }
11214
11215 return hrc;
11216}
11217
11218/**
11219 * Creates differencing hard disks for all normal hard disks attached to this
11220 * machine and a new set of attachments to refer to created disks.
11221 *
11222 * Used when taking a snapshot or when deleting the current state. Gets called
11223 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
11224 *
11225 * This method assumes that mMediumAttachments contains the original hard disk
11226 * attachments it needs to create diffs for. On success, these attachments will
11227 * be replaced with the created diffs.
11228 *
11229 * Attachments with non-normal hard disks are left as is.
11230 *
11231 * If @a aOnline is @c false then the original hard disks that require implicit
11232 * diffs will be locked for reading. Otherwise it is assumed that they are
11233 * already locked for writing (when the VM was started). Note that in the latter
11234 * case it is responsibility of the caller to lock the newly created diffs for
11235 * writing if this method succeeds.
11236 *
11237 * @param aProgress Progress object to run (must contain at least as
11238 * many operations left as the number of hard disks
11239 * attached).
11240 * @param aWeight Weight of this operation.
11241 * @param aOnline Whether the VM was online prior to this operation.
11242 *
11243 * @note The progress object is not marked as completed, neither on success nor
11244 * on failure. This is a responsibility of the caller.
11245 *
11246 * @note Locks this object and the media tree for writing.
11247 */
11248HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
11249 ULONG aWeight,
11250 bool aOnline)
11251{
11252 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11253
11254 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
11255 AssertReturn(!!pProgressControl, E_INVALIDARG);
11256
11257 AutoCaller autoCaller(this);
11258 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11259
11260 AutoMultiWriteLock2 alock(this->lockHandle(),
11261 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11262
11263 /* must be in a protective state because we release the lock below */
11264 AssertReturn( mData->mMachineState == MachineState_Snapshotting
11265 || mData->mMachineState == MachineState_OnlineSnapshotting
11266 || mData->mMachineState == MachineState_LiveSnapshotting
11267 || mData->mMachineState == MachineState_RestoringSnapshot
11268 || mData->mMachineState == MachineState_DeletingSnapshot
11269 , E_FAIL);
11270
11271 HRESULT rc = S_OK;
11272
11273 // use appropriate locked media map (online or offline)
11274 MediumLockListMap lockedMediaOffline;
11275 MediumLockListMap *lockedMediaMap;
11276 if (aOnline)
11277 lockedMediaMap = &mData->mSession.mLockedMedia;
11278 else
11279 lockedMediaMap = &lockedMediaOffline;
11280
11281 try
11282 {
11283 if (!aOnline)
11284 {
11285 /* lock all attached hard disks early to detect "in use"
11286 * situations before creating actual diffs */
11287 for (MediumAttachmentList::const_iterator
11288 it = mMediumAttachments->begin();
11289 it != mMediumAttachments->end();
11290 ++it)
11291 {
11292 MediumAttachment *pAtt = *it;
11293 if (pAtt->i_getType() == DeviceType_HardDisk)
11294 {
11295 Medium *pMedium = pAtt->i_getMedium();
11296 Assert(pMedium);
11297
11298 MediumLockList *pMediumLockList(new MediumLockList());
11299 alock.release();
11300 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11301 NULL /* pToLockWrite */,
11302 false /* fMediumLockWriteAll */,
11303 NULL,
11304 *pMediumLockList);
11305 alock.acquire();
11306 if (FAILED(rc))
11307 {
11308 delete pMediumLockList;
11309 throw rc;
11310 }
11311 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11312 if (FAILED(rc))
11313 {
11314 throw setError(rc,
11315 tr("Collecting locking information for all attached media failed"));
11316 }
11317 }
11318 }
11319
11320 /* Now lock all media. If this fails, nothing is locked. */
11321 alock.release();
11322 rc = lockedMediaMap->Lock();
11323 alock.acquire();
11324 if (FAILED(rc))
11325 {
11326 throw setError(rc,
11327 tr("Locking of attached media failed"));
11328 }
11329 }
11330
11331 /* remember the current list (note that we don't use backup() since
11332 * mMediumAttachments may be already backed up) */
11333 MediumAttachmentList atts = *mMediumAttachments.data();
11334
11335 /* start from scratch */
11336 mMediumAttachments->clear();
11337
11338 /* go through remembered attachments and create diffs for normal hard
11339 * disks and attach them */
11340 for (MediumAttachmentList::const_iterator
11341 it = atts.begin();
11342 it != atts.end();
11343 ++it)
11344 {
11345 MediumAttachment *pAtt = *it;
11346
11347 DeviceType_T devType = pAtt->i_getType();
11348 Medium *pMedium = pAtt->i_getMedium();
11349
11350 if ( devType != DeviceType_HardDisk
11351 || pMedium == NULL
11352 || pMedium->i_getType() != MediumType_Normal)
11353 {
11354 /* copy the attachment as is */
11355
11356 /** @todo the progress object created in SessionMachine::TakeSnaphot
11357 * only expects operations for hard disks. Later other
11358 * device types need to show up in the progress as well. */
11359 if (devType == DeviceType_HardDisk)
11360 {
11361 if (pMedium == NULL)
11362 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11363 aWeight); // weight
11364 else
11365 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11366 pMedium->i_getBase()->i_getName().c_str()).raw(),
11367 aWeight); // weight
11368 }
11369
11370 mMediumAttachments->push_back(pAtt);
11371 continue;
11372 }
11373
11374 /* need a diff */
11375 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11376 pMedium->i_getBase()->i_getName().c_str()).raw(),
11377 aWeight); // weight
11378
11379 Utf8Str strFullSnapshotFolder;
11380 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11381
11382 ComObjPtr<Medium> diff;
11383 diff.createObject();
11384 // store the diff in the same registry as the parent
11385 // (this cannot fail here because we can't create implicit diffs for
11386 // unregistered images)
11387 Guid uuidRegistryParent;
11388 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11389 Assert(fInRegistry); NOREF(fInRegistry);
11390 rc = diff->init(mParent,
11391 pMedium->i_getPreferredDiffFormat(),
11392 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11393 uuidRegistryParent,
11394 DeviceType_HardDisk);
11395 if (FAILED(rc)) throw rc;
11396
11397 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11398 * the push_back? Looks like we're going to release medium with the
11399 * wrong kind of lock (general issue with if we fail anywhere at all)
11400 * and an orphaned VDI in the snapshots folder. */
11401
11402 /* update the appropriate lock list */
11403 MediumLockList *pMediumLockList;
11404 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11405 AssertComRCThrowRC(rc);
11406 if (aOnline)
11407 {
11408 alock.release();
11409 /* The currently attached medium will be read-only, change
11410 * the lock type to read. */
11411 rc = pMediumLockList->Update(pMedium, false);
11412 alock.acquire();
11413 AssertComRCThrowRC(rc);
11414 }
11415
11416 /* release the locks before the potentially lengthy operation */
11417 alock.release();
11418 rc = pMedium->i_createDiffStorage(diff,
11419 pMedium->i_getPreferredDiffVariant(),
11420 pMediumLockList,
11421 NULL /* aProgress */,
11422 true /* aWait */,
11423 false /* aNotify */);
11424 alock.acquire();
11425 if (FAILED(rc)) throw rc;
11426
11427 /* actual lock list update is done in Machine::i_commitMedia */
11428
11429 rc = diff->i_addBackReference(mData->mUuid);
11430 AssertComRCThrowRC(rc);
11431
11432 /* add a new attachment */
11433 ComObjPtr<MediumAttachment> attachment;
11434 attachment.createObject();
11435 rc = attachment->init(this,
11436 diff,
11437 pAtt->i_getControllerName(),
11438 pAtt->i_getPort(),
11439 pAtt->i_getDevice(),
11440 DeviceType_HardDisk,
11441 true /* aImplicit */,
11442 false /* aPassthrough */,
11443 false /* aTempEject */,
11444 pAtt->i_getNonRotational(),
11445 pAtt->i_getDiscard(),
11446 pAtt->i_getHotPluggable(),
11447 pAtt->i_getBandwidthGroup());
11448 if (FAILED(rc)) throw rc;
11449
11450 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11451 AssertComRCThrowRC(rc);
11452 mMediumAttachments->push_back(attachment);
11453 }
11454 }
11455 catch (HRESULT aRC) { rc = aRC; }
11456
11457 /* unlock all hard disks we locked when there is no VM */
11458 if (!aOnline)
11459 {
11460 ErrorInfoKeeper eik;
11461
11462 HRESULT rc1 = lockedMediaMap->Clear();
11463 AssertComRC(rc1);
11464 }
11465
11466 return rc;
11467}
11468
11469/**
11470 * Deletes implicit differencing hard disks created either by
11471 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11472 * mMediumAttachments.
11473 *
11474 * Note that to delete hard disks created by #attachDevice() this method is
11475 * called from #i_rollbackMedia() when the changes are rolled back.
11476 *
11477 * @note Locks this object and the media tree for writing.
11478 */
11479HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11480{
11481 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11482
11483 AutoCaller autoCaller(this);
11484 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11485
11486 AutoMultiWriteLock2 alock(this->lockHandle(),
11487 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11488
11489 /* We absolutely must have backed up state. */
11490 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11491
11492 /* Check if there are any implicitly created diff images. */
11493 bool fImplicitDiffs = false;
11494 for (MediumAttachmentList::const_iterator
11495 it = mMediumAttachments->begin();
11496 it != mMediumAttachments->end();
11497 ++it)
11498 {
11499 const ComObjPtr<MediumAttachment> &pAtt = *it;
11500 if (pAtt->i_isImplicit())
11501 {
11502 fImplicitDiffs = true;
11503 break;
11504 }
11505 }
11506 /* If there is nothing to do, leave early. This saves lots of image locking
11507 * effort. It also avoids a MachineStateChanged event without real reason.
11508 * This is important e.g. when loading a VM config, because there should be
11509 * no events. Otherwise API clients can become thoroughly confused for
11510 * inaccessible VMs (the code for loading VM configs uses this method for
11511 * cleanup if the config makes no sense), as they take such events as an
11512 * indication that the VM is alive, and they would force the VM config to
11513 * be reread, leading to an endless loop. */
11514 if (!fImplicitDiffs)
11515 return S_OK;
11516
11517 HRESULT rc = S_OK;
11518 MachineState_T oldState = mData->mMachineState;
11519
11520 /* will release the lock before the potentially lengthy operation,
11521 * so protect with the special state (unless already protected) */
11522 if ( oldState != MachineState_Snapshotting
11523 && oldState != MachineState_OnlineSnapshotting
11524 && oldState != MachineState_LiveSnapshotting
11525 && oldState != MachineState_RestoringSnapshot
11526 && oldState != MachineState_DeletingSnapshot
11527 && oldState != MachineState_DeletingSnapshotOnline
11528 && oldState != MachineState_DeletingSnapshotPaused
11529 )
11530 i_setMachineState(MachineState_SettingUp);
11531
11532 // use appropriate locked media map (online or offline)
11533 MediumLockListMap lockedMediaOffline;
11534 MediumLockListMap *lockedMediaMap;
11535 if (aOnline)
11536 lockedMediaMap = &mData->mSession.mLockedMedia;
11537 else
11538 lockedMediaMap = &lockedMediaOffline;
11539
11540 try
11541 {
11542 if (!aOnline)
11543 {
11544 /* lock all attached hard disks early to detect "in use"
11545 * situations before deleting actual diffs */
11546 for (MediumAttachmentList::const_iterator
11547 it = mMediumAttachments->begin();
11548 it != mMediumAttachments->end();
11549 ++it)
11550 {
11551 MediumAttachment *pAtt = *it;
11552 if (pAtt->i_getType() == DeviceType_HardDisk)
11553 {
11554 Medium *pMedium = pAtt->i_getMedium();
11555 Assert(pMedium);
11556
11557 MediumLockList *pMediumLockList(new MediumLockList());
11558 alock.release();
11559 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11560 NULL /* pToLockWrite */,
11561 false /* fMediumLockWriteAll */,
11562 NULL,
11563 *pMediumLockList);
11564 alock.acquire();
11565
11566 if (FAILED(rc))
11567 {
11568 delete pMediumLockList;
11569 throw rc;
11570 }
11571
11572 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11573 if (FAILED(rc))
11574 throw rc;
11575 }
11576 }
11577
11578 if (FAILED(rc))
11579 throw rc;
11580 } // end of offline
11581
11582 /* Lock lists are now up to date and include implicitly created media */
11583
11584 /* Go through remembered attachments and delete all implicitly created
11585 * diffs and fix up the attachment information */
11586 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11587 MediumAttachmentList implicitAtts;
11588 for (MediumAttachmentList::const_iterator
11589 it = mMediumAttachments->begin();
11590 it != mMediumAttachments->end();
11591 ++it)
11592 {
11593 ComObjPtr<MediumAttachment> pAtt = *it;
11594 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11595 if (pMedium.isNull())
11596 continue;
11597
11598 // Implicit attachments go on the list for deletion and back references are removed.
11599 if (pAtt->i_isImplicit())
11600 {
11601 /* Deassociate and mark for deletion */
11602 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11603 rc = pMedium->i_removeBackReference(mData->mUuid);
11604 if (FAILED(rc))
11605 throw rc;
11606 implicitAtts.push_back(pAtt);
11607 continue;
11608 }
11609
11610 /* Was this medium attached before? */
11611 if (!i_findAttachment(oldAtts, pMedium))
11612 {
11613 /* no: de-associate */
11614 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11615 rc = pMedium->i_removeBackReference(mData->mUuid);
11616 if (FAILED(rc))
11617 throw rc;
11618 continue;
11619 }
11620 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11621 }
11622
11623 /* If there are implicit attachments to delete, throw away the lock
11624 * map contents (which will unlock all media) since the medium
11625 * attachments will be rolled back. Below we need to completely
11626 * recreate the lock map anyway since it is infinitely complex to
11627 * do this incrementally (would need reconstructing each attachment
11628 * change, which would be extremely hairy). */
11629 if (implicitAtts.size() != 0)
11630 {
11631 ErrorInfoKeeper eik;
11632
11633 HRESULT rc1 = lockedMediaMap->Clear();
11634 AssertComRC(rc1);
11635 }
11636
11637 /* rollback hard disk changes */
11638 mMediumAttachments.rollback();
11639
11640 MultiResult mrc(S_OK);
11641
11642 // Delete unused implicit diffs.
11643 if (implicitAtts.size() != 0)
11644 {
11645 alock.release();
11646
11647 for (MediumAttachmentList::const_iterator
11648 it = implicitAtts.begin();
11649 it != implicitAtts.end();
11650 ++it)
11651 {
11652 // Remove medium associated with this attachment.
11653 ComObjPtr<MediumAttachment> pAtt = *it;
11654 Assert(pAtt);
11655 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11656 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11657 Assert(pMedium);
11658
11659 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11660 // continue on delete failure, just collect error messages
11661 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11662 pMedium->i_getLocationFull().c_str() ));
11663 mrc = rc;
11664 }
11665 // Clear the list of deleted implicit attachments now, while not
11666 // holding the lock, as it will ultimately trigger Medium::uninit()
11667 // calls which assume that the media tree lock isn't held.
11668 implicitAtts.clear();
11669
11670 alock.acquire();
11671
11672 /* if there is a VM recreate media lock map as mentioned above,
11673 * otherwise it is a waste of time and we leave things unlocked */
11674 if (aOnline)
11675 {
11676 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11677 /* must never be NULL, but better safe than sorry */
11678 if (!pMachine.isNull())
11679 {
11680 alock.release();
11681 rc = mData->mSession.mMachine->i_lockMedia();
11682 alock.acquire();
11683 if (FAILED(rc))
11684 throw rc;
11685 }
11686 }
11687 }
11688 }
11689 catch (HRESULT aRC) {rc = aRC;}
11690
11691 if (mData->mMachineState == MachineState_SettingUp)
11692 i_setMachineState(oldState);
11693
11694 /* unlock all hard disks we locked when there is no VM */
11695 if (!aOnline)
11696 {
11697 ErrorInfoKeeper eik;
11698
11699 HRESULT rc1 = lockedMediaMap->Clear();
11700 AssertComRC(rc1);
11701 }
11702
11703 return rc;
11704}
11705
11706
11707/**
11708 * Looks through the given list of media attachments for one with the given parameters
11709 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11710 * can be searched as well if needed.
11711 *
11712 * @param ll
11713 * @param aControllerName
11714 * @param aControllerPort
11715 * @param aDevice
11716 * @return
11717 */
11718MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11719 const Utf8Str &aControllerName,
11720 LONG aControllerPort,
11721 LONG aDevice)
11722{
11723 for (MediumAttachmentList::const_iterator
11724 it = ll.begin();
11725 it != ll.end();
11726 ++it)
11727 {
11728 MediumAttachment *pAttach = *it;
11729 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11730 return pAttach;
11731 }
11732
11733 return NULL;
11734}
11735
11736/**
11737 * Looks through the given list of media attachments for one with the given parameters
11738 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11739 * can be searched as well if needed.
11740 *
11741 * @param ll
11742 * @param pMedium
11743 * @return
11744 */
11745MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11746 ComObjPtr<Medium> pMedium)
11747{
11748 for (MediumAttachmentList::const_iterator
11749 it = ll.begin();
11750 it != ll.end();
11751 ++it)
11752 {
11753 MediumAttachment *pAttach = *it;
11754 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11755 if (pMediumThis == pMedium)
11756 return pAttach;
11757 }
11758
11759 return NULL;
11760}
11761
11762/**
11763 * Looks through the given list of media attachments for one with the given parameters
11764 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11765 * can be searched as well if needed.
11766 *
11767 * @param ll
11768 * @param id
11769 * @return
11770 */
11771MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11772 Guid &id)
11773{
11774 for (MediumAttachmentList::const_iterator
11775 it = ll.begin();
11776 it != ll.end();
11777 ++it)
11778 {
11779 MediumAttachment *pAttach = *it;
11780 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11781 if (pMediumThis->i_getId() == id)
11782 return pAttach;
11783 }
11784
11785 return NULL;
11786}
11787
11788/**
11789 * Main implementation for Machine::DetachDevice. This also gets called
11790 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11791 *
11792 * @param pAttach Medium attachment to detach.
11793 * @param writeLock Machine write lock which the caller must have locked once.
11794 * This may be released temporarily in here.
11795 * @param pSnapshot If NULL, then the detachment is for the current machine.
11796 * Otherwise this is for a SnapshotMachine, and this must be
11797 * its snapshot.
11798 * @return
11799 */
11800HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11801 AutoWriteLock &writeLock,
11802 Snapshot *pSnapshot)
11803{
11804 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11805 DeviceType_T mediumType = pAttach->i_getType();
11806
11807 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11808
11809 if (pAttach->i_isImplicit())
11810 {
11811 /* attempt to implicitly delete the implicitly created diff */
11812
11813 /// @todo move the implicit flag from MediumAttachment to Medium
11814 /// and forbid any hard disk operation when it is implicit. Or maybe
11815 /// a special media state for it to make it even more simple.
11816
11817 Assert(mMediumAttachments.isBackedUp());
11818
11819 /* will release the lock before the potentially lengthy operation, so
11820 * protect with the special state */
11821 MachineState_T oldState = mData->mMachineState;
11822 i_setMachineState(MachineState_SettingUp);
11823
11824 writeLock.release();
11825
11826 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11827 true /*aWait*/,
11828 false /*aNotify*/);
11829
11830 writeLock.acquire();
11831
11832 i_setMachineState(oldState);
11833
11834 if (FAILED(rc)) return rc;
11835 }
11836
11837 i_setModified(IsModified_Storage);
11838 mMediumAttachments.backup();
11839 mMediumAttachments->remove(pAttach);
11840
11841 if (!oldmedium.isNull())
11842 {
11843 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11844 if (pSnapshot)
11845 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11846 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11847 else if (mediumType != DeviceType_HardDisk)
11848 oldmedium->i_removeBackReference(mData->mUuid);
11849 }
11850
11851 return S_OK;
11852}
11853
11854/**
11855 * Goes thru all media of the given list and
11856 *
11857 * 1) calls i_detachDevice() on each of them for this machine and
11858 * 2) adds all Medium objects found in the process to the given list,
11859 * depending on cleanupMode.
11860 *
11861 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11862 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11863 * media to the list.
11864 *
11865 * This gets called from Machine::Unregister, both for the actual Machine and
11866 * the SnapshotMachine objects that might be found in the snapshots.
11867 *
11868 * Requires caller and locking. The machine lock must be passed in because it
11869 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11870 *
11871 * @param writeLock Machine lock from top-level caller; this gets passed to
11872 * i_detachDevice.
11873 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11874 * object if called for a SnapshotMachine.
11875 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11876 * added to llMedia; if Full, then all media get added;
11877 * otherwise no media get added.
11878 * @param llMedia Caller's list to receive Medium objects which got detached so
11879 * caller can close() them, depending on cleanupMode.
11880 * @return
11881 */
11882HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11883 Snapshot *pSnapshot,
11884 CleanupMode_T cleanupMode,
11885 MediaList &llMedia)
11886{
11887 Assert(isWriteLockOnCurrentThread());
11888
11889 HRESULT rc;
11890
11891 // make a temporary list because i_detachDevice invalidates iterators into
11892 // mMediumAttachments
11893 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11894
11895 for (MediumAttachmentList::iterator
11896 it = llAttachments2.begin();
11897 it != llAttachments2.end();
11898 ++it)
11899 {
11900 ComObjPtr<MediumAttachment> &pAttach = *it;
11901 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11902
11903 if (!pMedium.isNull())
11904 {
11905 AutoCaller mac(pMedium);
11906 if (FAILED(mac.rc())) return mac.rc();
11907 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11908 DeviceType_T devType = pMedium->i_getDeviceType();
11909 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11910 && devType == DeviceType_HardDisk)
11911 || (cleanupMode == CleanupMode_Full)
11912 )
11913 {
11914 llMedia.push_back(pMedium);
11915 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11916 /* Not allowed to keep this lock as below we need the parent
11917 * medium lock, and the lock order is parent to child. */
11918 lock.release();
11919 /*
11920 * Search for medias which are not attached to any machine, but
11921 * in the chain to an attached disk. Mediums are only consided
11922 * if they are:
11923 * - have only one child
11924 * - no references to any machines
11925 * - are of normal medium type
11926 */
11927 while (!pParent.isNull())
11928 {
11929 AutoCaller mac1(pParent);
11930 if (FAILED(mac1.rc())) return mac1.rc();
11931 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11932 if (pParent->i_getChildren().size() == 1)
11933 {
11934 if ( pParent->i_getMachineBackRefCount() == 0
11935 && pParent->i_getType() == MediumType_Normal
11936 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11937 llMedia.push_back(pParent);
11938 }
11939 else
11940 break;
11941 pParent = pParent->i_getParent();
11942 }
11943 }
11944 }
11945
11946 // real machine: then we need to use the proper method
11947 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11948
11949 if (FAILED(rc))
11950 return rc;
11951 }
11952
11953 return S_OK;
11954}
11955
11956/**
11957 * Perform deferred hard disk detachments.
11958 *
11959 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11960 * changed (not backed up).
11961 *
11962 * If @a aOnline is @c true then this method will also unlock the old hard
11963 * disks for which the new implicit diffs were created and will lock these new
11964 * diffs for writing.
11965 *
11966 * @param aOnline Whether the VM was online prior to this operation.
11967 *
11968 * @note Locks this object for writing!
11969 */
11970void Machine::i_commitMedia(bool aOnline /*= false*/)
11971{
11972 AutoCaller autoCaller(this);
11973 AssertComRCReturnVoid(autoCaller.rc());
11974
11975 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11976
11977 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11978
11979 HRESULT rc = S_OK;
11980
11981 /* no attach/detach operations -- nothing to do */
11982 if (!mMediumAttachments.isBackedUp())
11983 return;
11984
11985 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11986 bool fMediaNeedsLocking = false;
11987
11988 /* enumerate new attachments */
11989 for (MediumAttachmentList::const_iterator
11990 it = mMediumAttachments->begin();
11991 it != mMediumAttachments->end();
11992 ++it)
11993 {
11994 MediumAttachment *pAttach = *it;
11995
11996 pAttach->i_commit();
11997
11998 Medium *pMedium = pAttach->i_getMedium();
11999 bool fImplicit = pAttach->i_isImplicit();
12000
12001 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
12002 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
12003 fImplicit));
12004
12005 /** @todo convert all this Machine-based voodoo to MediumAttachment
12006 * based commit logic. */
12007 if (fImplicit)
12008 {
12009 /* convert implicit attachment to normal */
12010 pAttach->i_setImplicit(false);
12011
12012 if ( aOnline
12013 && pMedium
12014 && pAttach->i_getType() == DeviceType_HardDisk
12015 )
12016 {
12017 /* update the appropriate lock list */
12018 MediumLockList *pMediumLockList;
12019 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
12020 AssertComRC(rc);
12021 if (pMediumLockList)
12022 {
12023 /* unlock if there's a need to change the locking */
12024 if (!fMediaNeedsLocking)
12025 {
12026 rc = mData->mSession.mLockedMedia.Unlock();
12027 AssertComRC(rc);
12028 fMediaNeedsLocking = true;
12029 }
12030 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
12031 AssertComRC(rc);
12032 rc = pMediumLockList->Append(pMedium, true);
12033 AssertComRC(rc);
12034 }
12035 }
12036
12037 continue;
12038 }
12039
12040 if (pMedium)
12041 {
12042 /* was this medium attached before? */
12043 for (MediumAttachmentList::iterator
12044 oldIt = oldAtts.begin();
12045 oldIt != oldAtts.end();
12046 ++oldIt)
12047 {
12048 MediumAttachment *pOldAttach = *oldIt;
12049 if (pOldAttach->i_getMedium() == pMedium)
12050 {
12051 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
12052
12053 /* yes: remove from old to avoid de-association */
12054 oldAtts.erase(oldIt);
12055 break;
12056 }
12057 }
12058 }
12059 }
12060
12061 /* enumerate remaining old attachments and de-associate from the
12062 * current machine state */
12063 for (MediumAttachmentList::const_iterator
12064 it = oldAtts.begin();
12065 it != oldAtts.end();
12066 ++it)
12067 {
12068 MediumAttachment *pAttach = *it;
12069 Medium *pMedium = pAttach->i_getMedium();
12070
12071 /* Detach only hard disks, since DVD/floppy media is detached
12072 * instantly in MountMedium. */
12073 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
12074 {
12075 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
12076
12077 /* now de-associate from the current machine state */
12078 rc = pMedium->i_removeBackReference(mData->mUuid);
12079 AssertComRC(rc);
12080
12081 if (aOnline)
12082 {
12083 /* unlock since medium is not used anymore */
12084 MediumLockList *pMediumLockList;
12085 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
12086 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
12087 {
12088 /* this happens for online snapshots, there the attachment
12089 * is changing, but only to a diff image created under
12090 * the old one, so there is no separate lock list */
12091 Assert(!pMediumLockList);
12092 }
12093 else
12094 {
12095 AssertComRC(rc);
12096 if (pMediumLockList)
12097 {
12098 rc = mData->mSession.mLockedMedia.Remove(pAttach);
12099 AssertComRC(rc);
12100 }
12101 }
12102 }
12103 }
12104 }
12105
12106 /* take media locks again so that the locking state is consistent */
12107 if (fMediaNeedsLocking)
12108 {
12109 Assert(aOnline);
12110 rc = mData->mSession.mLockedMedia.Lock();
12111 AssertComRC(rc);
12112 }
12113
12114 /* commit the hard disk changes */
12115 mMediumAttachments.commit();
12116
12117 if (i_isSessionMachine())
12118 {
12119 /*
12120 * Update the parent machine to point to the new owner.
12121 * This is necessary because the stored parent will point to the
12122 * session machine otherwise and cause crashes or errors later
12123 * when the session machine gets invalid.
12124 */
12125 /** @todo Change the MediumAttachment class to behave like any other
12126 * class in this regard by creating peer MediumAttachment
12127 * objects for session machines and share the data with the peer
12128 * machine.
12129 */
12130 for (MediumAttachmentList::const_iterator
12131 it = mMediumAttachments->begin();
12132 it != mMediumAttachments->end();
12133 ++it)
12134 (*it)->i_updateParentMachine(mPeer);
12135
12136 /* attach new data to the primary machine and reshare it */
12137 mPeer->mMediumAttachments.attach(mMediumAttachments);
12138 }
12139
12140 return;
12141}
12142
12143/**
12144 * Perform deferred deletion of implicitly created diffs.
12145 *
12146 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
12147 * changed (not backed up).
12148 *
12149 * @note Locks this object for writing!
12150 */
12151void Machine::i_rollbackMedia()
12152{
12153 AutoCaller autoCaller(this);
12154 AssertComRCReturnVoid(autoCaller.rc());
12155
12156 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12157 LogFlowThisFunc(("Entering rollbackMedia\n"));
12158
12159 HRESULT rc = S_OK;
12160
12161 /* no attach/detach operations -- nothing to do */
12162 if (!mMediumAttachments.isBackedUp())
12163 return;
12164
12165 /* enumerate new attachments */
12166 for (MediumAttachmentList::const_iterator
12167 it = mMediumAttachments->begin();
12168 it != mMediumAttachments->end();
12169 ++it)
12170 {
12171 MediumAttachment *pAttach = *it;
12172 /* Fix up the backrefs for DVD/floppy media. */
12173 if (pAttach->i_getType() != DeviceType_HardDisk)
12174 {
12175 Medium *pMedium = pAttach->i_getMedium();
12176 if (pMedium)
12177 {
12178 rc = pMedium->i_removeBackReference(mData->mUuid);
12179 AssertComRC(rc);
12180 }
12181 }
12182
12183 (*it)->i_rollback();
12184
12185 pAttach = *it;
12186 /* Fix up the backrefs for DVD/floppy media. */
12187 if (pAttach->i_getType() != DeviceType_HardDisk)
12188 {
12189 Medium *pMedium = pAttach->i_getMedium();
12190 if (pMedium)
12191 {
12192 rc = pMedium->i_addBackReference(mData->mUuid);
12193 AssertComRC(rc);
12194 }
12195 }
12196 }
12197
12198 /** @todo convert all this Machine-based voodoo to MediumAttachment
12199 * based rollback logic. */
12200 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
12201
12202 return;
12203}
12204
12205/**
12206 * Returns true if the settings file is located in the directory named exactly
12207 * as the machine; this means, among other things, that the machine directory
12208 * should be auto-renamed.
12209 *
12210 * @param aSettingsDir if not NULL, the full machine settings file directory
12211 * name will be assigned there.
12212 *
12213 * @note Doesn't lock anything.
12214 * @note Not thread safe (must be called from this object's lock).
12215 */
12216bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
12217{
12218 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12219 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
12220 if (aSettingsDir)
12221 *aSettingsDir = strMachineDirName;
12222 strMachineDirName.stripPath(); // vmname
12223 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12224 strConfigFileOnly.stripPath() // vmname.vbox
12225 .stripSuffix(); // vmname
12226 /** @todo hack, make somehow use of ComposeMachineFilename */
12227 if (mUserData->s.fDirectoryIncludesUUID)
12228 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
12229
12230 AssertReturn(!strMachineDirName.isEmpty(), false);
12231 AssertReturn(!strConfigFileOnly.isEmpty(), false);
12232
12233 return strMachineDirName == strConfigFileOnly;
12234}
12235
12236/**
12237 * Discards all changes to machine settings.
12238 *
12239 * @param aNotify Whether to notify the direct session about changes or not.
12240 *
12241 * @note Locks objects for writing!
12242 */
12243void Machine::i_rollback(bool aNotify)
12244{
12245 AutoCaller autoCaller(this);
12246 AssertComRCReturn(autoCaller.rc(), (void)0);
12247
12248 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12249
12250 if (!mStorageControllers.isNull())
12251 {
12252 if (mStorageControllers.isBackedUp())
12253 {
12254 /* unitialize all new devices (absent in the backed up list). */
12255 StorageControllerList *backedList = mStorageControllers.backedUpData();
12256 for (StorageControllerList::const_iterator
12257 it = mStorageControllers->begin();
12258 it != mStorageControllers->end();
12259 ++it)
12260 {
12261 if ( std::find(backedList->begin(), backedList->end(), *it)
12262 == backedList->end()
12263 )
12264 {
12265 (*it)->uninit();
12266 }
12267 }
12268
12269 /* restore the list */
12270 mStorageControllers.rollback();
12271 }
12272
12273 /* rollback any changes to devices after restoring the list */
12274 if (mData->flModifications & IsModified_Storage)
12275 {
12276 for (StorageControllerList::const_iterator
12277 it = mStorageControllers->begin();
12278 it != mStorageControllers->end();
12279 ++it)
12280 {
12281 (*it)->i_rollback();
12282 }
12283 }
12284 }
12285
12286 if (!mUSBControllers.isNull())
12287 {
12288 if (mUSBControllers.isBackedUp())
12289 {
12290 /* unitialize all new devices (absent in the backed up list). */
12291 USBControllerList *backedList = mUSBControllers.backedUpData();
12292 for (USBControllerList::const_iterator
12293 it = mUSBControllers->begin();
12294 it != mUSBControllers->end();
12295 ++it)
12296 {
12297 if ( std::find(backedList->begin(), backedList->end(), *it)
12298 == backedList->end()
12299 )
12300 {
12301 (*it)->uninit();
12302 }
12303 }
12304
12305 /* restore the list */
12306 mUSBControllers.rollback();
12307 }
12308
12309 /* rollback any changes to devices after restoring the list */
12310 if (mData->flModifications & IsModified_USB)
12311 {
12312 for (USBControllerList::const_iterator
12313 it = mUSBControllers->begin();
12314 it != mUSBControllers->end();
12315 ++it)
12316 {
12317 (*it)->i_rollback();
12318 }
12319 }
12320 }
12321
12322 mUserData.rollback();
12323
12324 mHWData.rollback();
12325
12326 if (mData->flModifications & IsModified_Storage)
12327 i_rollbackMedia();
12328
12329 if (mBIOSSettings)
12330 mBIOSSettings->i_rollback();
12331
12332 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
12333 mRecordingSettings->i_rollback();
12334
12335 if (mTrustedPlatformModule)
12336 mTrustedPlatformModule->i_rollback();
12337
12338 if (mNvramStore)
12339 mNvramStore->i_rollback();
12340
12341 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
12342 mGraphicsAdapter->i_rollback();
12343
12344 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12345 mVRDEServer->i_rollback();
12346
12347 if (mAudioSettings && (mData->flModifications & IsModified_AudioSettings))
12348 mAudioSettings->i_rollback();
12349
12350 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12351 mUSBDeviceFilters->i_rollback();
12352
12353 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12354 mBandwidthControl->i_rollback();
12355
12356 if (!mHWData.isNull())
12357 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12358 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12359 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12360 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12361
12362 if (mData->flModifications & IsModified_NetworkAdapters)
12363 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12364 if ( mNetworkAdapters[slot]
12365 && mNetworkAdapters[slot]->i_isModified())
12366 {
12367 mNetworkAdapters[slot]->i_rollback();
12368 networkAdapters[slot] = mNetworkAdapters[slot];
12369 }
12370
12371 if (mData->flModifications & IsModified_SerialPorts)
12372 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12373 if ( mSerialPorts[slot]
12374 && mSerialPorts[slot]->i_isModified())
12375 {
12376 mSerialPorts[slot]->i_rollback();
12377 serialPorts[slot] = mSerialPorts[slot];
12378 }
12379
12380 if (mData->flModifications & IsModified_ParallelPorts)
12381 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12382 if ( mParallelPorts[slot]
12383 && mParallelPorts[slot]->i_isModified())
12384 {
12385 mParallelPorts[slot]->i_rollback();
12386 parallelPorts[slot] = mParallelPorts[slot];
12387 }
12388
12389 if (aNotify)
12390 {
12391 /* inform the direct session about changes */
12392
12393 ComObjPtr<Machine> that = this;
12394 uint32_t flModifications = mData->flModifications;
12395 alock.release();
12396
12397 if (flModifications & IsModified_SharedFolders)
12398 that->i_onSharedFolderChange();
12399
12400 if (flModifications & IsModified_VRDEServer)
12401 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12402 if (flModifications & IsModified_USB)
12403 that->i_onUSBControllerChange();
12404
12405 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12406 if (networkAdapters[slot])
12407 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12408 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12409 if (serialPorts[slot])
12410 that->i_onSerialPortChange(serialPorts[slot]);
12411 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12412 if (parallelPorts[slot])
12413 that->i_onParallelPortChange(parallelPorts[slot]);
12414
12415 if (flModifications & IsModified_Storage)
12416 {
12417 for (StorageControllerList::const_iterator
12418 it = mStorageControllers->begin();
12419 it != mStorageControllers->end();
12420 ++it)
12421 {
12422 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
12423 }
12424 }
12425
12426
12427#if 0
12428 if (flModifications & IsModified_BandwidthControl)
12429 that->onBandwidthControlChange();
12430#endif
12431 }
12432}
12433
12434/**
12435 * Commits all the changes to machine settings.
12436 *
12437 * Note that this operation is supposed to never fail.
12438 *
12439 * @note Locks this object and children for writing.
12440 */
12441void Machine::i_commit()
12442{
12443 AutoCaller autoCaller(this);
12444 AssertComRCReturnVoid(autoCaller.rc());
12445
12446 AutoCaller peerCaller(mPeer);
12447 AssertComRCReturnVoid(peerCaller.rc());
12448
12449 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12450
12451 /*
12452 * use safe commit to ensure Snapshot machines (that share mUserData)
12453 * will still refer to a valid memory location
12454 */
12455 mUserData.commitCopy();
12456
12457 mHWData.commit();
12458
12459 if (mMediumAttachments.isBackedUp())
12460 i_commitMedia(Global::IsOnline(mData->mMachineState));
12461
12462 mBIOSSettings->i_commit();
12463 mRecordingSettings->i_commit();
12464 mTrustedPlatformModule->i_commit();
12465 mNvramStore->i_commit();
12466 mGraphicsAdapter->i_commit();
12467 mVRDEServer->i_commit();
12468 mAudioSettings->i_commit();
12469 mUSBDeviceFilters->i_commit();
12470 mBandwidthControl->i_commit();
12471
12472 /* Since mNetworkAdapters is a list which might have been changed (resized)
12473 * without using the Backupable<> template we need to handle the copying
12474 * of the list entries manually, including the creation of peers for the
12475 * new objects. */
12476 bool commitNetworkAdapters = false;
12477 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12478 if (mPeer)
12479 {
12480 /* commit everything, even the ones which will go away */
12481 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12482 mNetworkAdapters[slot]->i_commit();
12483 /* copy over the new entries, creating a peer and uninit the original */
12484 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12485 for (size_t slot = 0; slot < newSize; slot++)
12486 {
12487 /* look if this adapter has a peer device */
12488 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12489 if (!peer)
12490 {
12491 /* no peer means the adapter is a newly created one;
12492 * create a peer owning data this data share it with */
12493 peer.createObject();
12494 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12495 }
12496 mPeer->mNetworkAdapters[slot] = peer;
12497 }
12498 /* uninit any no longer needed network adapters */
12499 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12500 mNetworkAdapters[slot]->uninit();
12501 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12502 {
12503 if (mPeer->mNetworkAdapters[slot])
12504 mPeer->mNetworkAdapters[slot]->uninit();
12505 }
12506 /* Keep the original network adapter count until this point, so that
12507 * discarding a chipset type change will not lose settings. */
12508 mNetworkAdapters.resize(newSize);
12509 mPeer->mNetworkAdapters.resize(newSize);
12510 }
12511 else
12512 {
12513 /* we have no peer (our parent is the newly created machine);
12514 * just commit changes to the network adapters */
12515 commitNetworkAdapters = true;
12516 }
12517 if (commitNetworkAdapters)
12518 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12519 mNetworkAdapters[slot]->i_commit();
12520
12521 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12522 mSerialPorts[slot]->i_commit();
12523 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12524 mParallelPorts[slot]->i_commit();
12525
12526 bool commitStorageControllers = false;
12527
12528 if (mStorageControllers.isBackedUp())
12529 {
12530 mStorageControllers.commit();
12531
12532 if (mPeer)
12533 {
12534 /* Commit all changes to new controllers (this will reshare data with
12535 * peers for those who have peers) */
12536 StorageControllerList *newList = new StorageControllerList();
12537 for (StorageControllerList::const_iterator
12538 it = mStorageControllers->begin();
12539 it != mStorageControllers->end();
12540 ++it)
12541 {
12542 (*it)->i_commit();
12543
12544 /* look if this controller has a peer device */
12545 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12546 if (!peer)
12547 {
12548 /* no peer means the device is a newly created one;
12549 * create a peer owning data this device share it with */
12550 peer.createObject();
12551 peer->init(mPeer, *it, true /* aReshare */);
12552 }
12553 else
12554 {
12555 /* remove peer from the old list */
12556 mPeer->mStorageControllers->remove(peer);
12557 }
12558 /* and add it to the new list */
12559 newList->push_back(peer);
12560 }
12561
12562 /* uninit old peer's controllers that are left */
12563 for (StorageControllerList::const_iterator
12564 it = mPeer->mStorageControllers->begin();
12565 it != mPeer->mStorageControllers->end();
12566 ++it)
12567 {
12568 (*it)->uninit();
12569 }
12570
12571 /* attach new list of controllers to our peer */
12572 mPeer->mStorageControllers.attach(newList);
12573 }
12574 else
12575 {
12576 /* we have no peer (our parent is the newly created machine);
12577 * just commit changes to devices */
12578 commitStorageControllers = true;
12579 }
12580 }
12581 else
12582 {
12583 /* the list of controllers itself is not changed,
12584 * just commit changes to controllers themselves */
12585 commitStorageControllers = true;
12586 }
12587
12588 if (commitStorageControllers)
12589 {
12590 for (StorageControllerList::const_iterator
12591 it = mStorageControllers->begin();
12592 it != mStorageControllers->end();
12593 ++it)
12594 {
12595 (*it)->i_commit();
12596 }
12597 }
12598
12599 bool commitUSBControllers = false;
12600
12601 if (mUSBControllers.isBackedUp())
12602 {
12603 mUSBControllers.commit();
12604
12605 if (mPeer)
12606 {
12607 /* Commit all changes to new controllers (this will reshare data with
12608 * peers for those who have peers) */
12609 USBControllerList *newList = new USBControllerList();
12610 for (USBControllerList::const_iterator
12611 it = mUSBControllers->begin();
12612 it != mUSBControllers->end();
12613 ++it)
12614 {
12615 (*it)->i_commit();
12616
12617 /* look if this controller has a peer device */
12618 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12619 if (!peer)
12620 {
12621 /* no peer means the device is a newly created one;
12622 * create a peer owning data this device share it with */
12623 peer.createObject();
12624 peer->init(mPeer, *it, true /* aReshare */);
12625 }
12626 else
12627 {
12628 /* remove peer from the old list */
12629 mPeer->mUSBControllers->remove(peer);
12630 }
12631 /* and add it to the new list */
12632 newList->push_back(peer);
12633 }
12634
12635 /* uninit old peer's controllers that are left */
12636 for (USBControllerList::const_iterator
12637 it = mPeer->mUSBControllers->begin();
12638 it != mPeer->mUSBControllers->end();
12639 ++it)
12640 {
12641 (*it)->uninit();
12642 }
12643
12644 /* attach new list of controllers to our peer */
12645 mPeer->mUSBControllers.attach(newList);
12646 }
12647 else
12648 {
12649 /* we have no peer (our parent is the newly created machine);
12650 * just commit changes to devices */
12651 commitUSBControllers = true;
12652 }
12653 }
12654 else
12655 {
12656 /* the list of controllers itself is not changed,
12657 * just commit changes to controllers themselves */
12658 commitUSBControllers = true;
12659 }
12660
12661 if (commitUSBControllers)
12662 {
12663 for (USBControllerList::const_iterator
12664 it = mUSBControllers->begin();
12665 it != mUSBControllers->end();
12666 ++it)
12667 {
12668 (*it)->i_commit();
12669 }
12670 }
12671
12672 if (i_isSessionMachine())
12673 {
12674 /* attach new data to the primary machine and reshare it */
12675 mPeer->mUserData.attach(mUserData);
12676 mPeer->mHWData.attach(mHWData);
12677 /* mmMediumAttachments is reshared by fixupMedia */
12678 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12679 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12680 }
12681}
12682
12683/**
12684 * Copies all the hardware data from the given machine.
12685 *
12686 * Currently, only called when the VM is being restored from a snapshot. In
12687 * particular, this implies that the VM is not running during this method's
12688 * call.
12689 *
12690 * @note This method must be called from under this object's lock.
12691 *
12692 * @note This method doesn't call #i_commit(), so all data remains backed up and
12693 * unsaved.
12694 */
12695void Machine::i_copyFrom(Machine *aThat)
12696{
12697 AssertReturnVoid(!i_isSnapshotMachine());
12698 AssertReturnVoid(aThat->i_isSnapshotMachine());
12699
12700 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12701
12702 mHWData.assignCopy(aThat->mHWData);
12703
12704 // create copies of all shared folders (mHWData after attaching a copy
12705 // contains just references to original objects)
12706 for (HWData::SharedFolderList::iterator
12707 it = mHWData->mSharedFolders.begin();
12708 it != mHWData->mSharedFolders.end();
12709 ++it)
12710 {
12711 ComObjPtr<SharedFolder> folder;
12712 folder.createObject();
12713 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12714 AssertComRC(rc);
12715 *it = folder;
12716 }
12717
12718 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12719 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12720 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12721 mNvramStore->i_copyFrom(aThat->mNvramStore);
12722 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12723 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12724 mAudioSettings->i_copyFrom(aThat->mAudioSettings);
12725 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12726 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12727
12728 /* create private copies of all controllers */
12729 mStorageControllers.backup();
12730 mStorageControllers->clear();
12731 for (StorageControllerList::const_iterator
12732 it = aThat->mStorageControllers->begin();
12733 it != aThat->mStorageControllers->end();
12734 ++it)
12735 {
12736 ComObjPtr<StorageController> ctrl;
12737 ctrl.createObject();
12738 ctrl->initCopy(this, *it);
12739 mStorageControllers->push_back(ctrl);
12740 }
12741
12742 /* create private copies of all USB controllers */
12743 mUSBControllers.backup();
12744 mUSBControllers->clear();
12745 for (USBControllerList::const_iterator
12746 it = aThat->mUSBControllers->begin();
12747 it != aThat->mUSBControllers->end();
12748 ++it)
12749 {
12750 ComObjPtr<USBController> ctrl;
12751 ctrl.createObject();
12752 ctrl->initCopy(this, *it);
12753 mUSBControllers->push_back(ctrl);
12754 }
12755
12756 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12757 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12758 {
12759 if (mNetworkAdapters[slot].isNotNull())
12760 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12761 else
12762 {
12763 unconst(mNetworkAdapters[slot]).createObject();
12764 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12765 }
12766 }
12767 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12768 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12769 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12770 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12771}
12772
12773/**
12774 * Returns whether the given storage controller is hotplug capable.
12775 *
12776 * @returns true if the controller supports hotplugging
12777 * false otherwise.
12778 * @param enmCtrlType The controller type to check for.
12779 */
12780bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12781{
12782 ComPtr<ISystemProperties> systemProperties;
12783 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12784 if (FAILED(rc))
12785 return false;
12786
12787 BOOL aHotplugCapable = FALSE;
12788 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12789
12790 return RT_BOOL(aHotplugCapable);
12791}
12792
12793#ifdef VBOX_WITH_RESOURCE_USAGE_API
12794
12795void Machine::i_getDiskList(MediaList &list)
12796{
12797 for (MediumAttachmentList::const_iterator
12798 it = mMediumAttachments->begin();
12799 it != mMediumAttachments->end();
12800 ++it)
12801 {
12802 MediumAttachment *pAttach = *it;
12803 /* just in case */
12804 AssertContinue(pAttach);
12805
12806 AutoCaller localAutoCallerA(pAttach);
12807 if (FAILED(localAutoCallerA.rc())) continue;
12808
12809 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12810
12811 if (pAttach->i_getType() == DeviceType_HardDisk)
12812 list.push_back(pAttach->i_getMedium());
12813 }
12814}
12815
12816void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12817{
12818 AssertReturnVoid(isWriteLockOnCurrentThread());
12819 AssertPtrReturnVoid(aCollector);
12820
12821 pm::CollectorHAL *hal = aCollector->getHAL();
12822 /* Create sub metrics */
12823 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12824 "Percentage of processor time spent in user mode by the VM process.");
12825 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12826 "Percentage of processor time spent in kernel mode by the VM process.");
12827 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12828 "Size of resident portion of VM process in memory.");
12829 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12830 "Actual size of all VM disks combined.");
12831 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12832 "Network receive rate.");
12833 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12834 "Network transmit rate.");
12835 /* Create and register base metrics */
12836 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12837 cpuLoadUser, cpuLoadKernel);
12838 aCollector->registerBaseMetric(cpuLoad);
12839 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12840 ramUsageUsed);
12841 aCollector->registerBaseMetric(ramUsage);
12842 MediaList disks;
12843 i_getDiskList(disks);
12844 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12845 diskUsageUsed);
12846 aCollector->registerBaseMetric(diskUsage);
12847
12848 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12849 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12850 new pm::AggregateAvg()));
12851 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12852 new pm::AggregateMin()));
12853 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12854 new pm::AggregateMax()));
12855 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12856 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12857 new pm::AggregateAvg()));
12858 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12859 new pm::AggregateMin()));
12860 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12861 new pm::AggregateMax()));
12862
12863 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12864 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12865 new pm::AggregateAvg()));
12866 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12867 new pm::AggregateMin()));
12868 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12869 new pm::AggregateMax()));
12870
12871 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12872 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12873 new pm::AggregateAvg()));
12874 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12875 new pm::AggregateMin()));
12876 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12877 new pm::AggregateMax()));
12878
12879
12880 /* Guest metrics collector */
12881 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12882 aCollector->registerGuest(mCollectorGuest);
12883 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12884
12885 /* Create sub metrics */
12886 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12887 "Percentage of processor time spent in user mode as seen by the guest.");
12888 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12889 "Percentage of processor time spent in kernel mode as seen by the guest.");
12890 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12891 "Percentage of processor time spent idling as seen by the guest.");
12892
12893 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12894 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12895 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12896 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12897 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12898 pm::SubMetric *guestMemCache = new pm::SubMetric(
12899 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12900
12901 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12902 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12903
12904 /* Create and register base metrics */
12905 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12906 machineNetRx, machineNetTx);
12907 aCollector->registerBaseMetric(machineNetRate);
12908
12909 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12910 guestLoadUser, guestLoadKernel, guestLoadIdle);
12911 aCollector->registerBaseMetric(guestCpuLoad);
12912
12913 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12914 guestMemTotal, guestMemFree,
12915 guestMemBalloon, guestMemShared,
12916 guestMemCache, guestPagedTotal);
12917 aCollector->registerBaseMetric(guestCpuMem);
12918
12919 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12920 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12921 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12922 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12923
12924 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12925 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12926 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12927 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12928
12929 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12930 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12931 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12932 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12933
12934 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12935 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12936 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12937 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12938
12939 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12940 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12941 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12942 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12943
12944 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12945 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12946 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12947 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12948
12949 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12950 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12951 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12952 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12953
12954 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12955 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12956 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12957 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12958
12959 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12960 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12961 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12962 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12963
12964 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12965 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12966 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12967 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12968
12969 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12970 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12971 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12972 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12973}
12974
12975void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12976{
12977 AssertReturnVoid(isWriteLockOnCurrentThread());
12978
12979 if (aCollector)
12980 {
12981 aCollector->unregisterMetricsFor(aMachine);
12982 aCollector->unregisterBaseMetricsFor(aMachine);
12983 }
12984}
12985
12986#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12987
12988
12989////////////////////////////////////////////////////////////////////////////////
12990
12991DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12992
12993HRESULT SessionMachine::FinalConstruct()
12994{
12995 LogFlowThisFunc(("\n"));
12996
12997 mClientToken = NULL;
12998
12999 return BaseFinalConstruct();
13000}
13001
13002void SessionMachine::FinalRelease()
13003{
13004 LogFlowThisFunc(("\n"));
13005
13006 Assert(!mClientToken);
13007 /* paranoia, should not hang around any more */
13008 if (mClientToken)
13009 {
13010 delete mClientToken;
13011 mClientToken = NULL;
13012 }
13013
13014 uninit(Uninit::Unexpected);
13015
13016 BaseFinalRelease();
13017}
13018
13019/**
13020 * @note Must be called only by Machine::LockMachine() from its own write lock.
13021 */
13022HRESULT SessionMachine::init(Machine *aMachine)
13023{
13024 LogFlowThisFuncEnter();
13025 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
13026
13027 AssertReturn(aMachine, E_INVALIDARG);
13028
13029 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
13030
13031 /* Enclose the state transition NotReady->InInit->Ready */
13032 AutoInitSpan autoInitSpan(this);
13033 AssertReturn(autoInitSpan.isOk(), E_FAIL);
13034
13035 HRESULT rc = S_OK;
13036
13037 RT_ZERO(mAuthLibCtx);
13038
13039 /* create the machine client token */
13040 try
13041 {
13042 mClientToken = new ClientToken(aMachine, this);
13043 if (!mClientToken->isReady())
13044 {
13045 delete mClientToken;
13046 mClientToken = NULL;
13047 rc = E_FAIL;
13048 }
13049 }
13050 catch (std::bad_alloc &)
13051 {
13052 rc = E_OUTOFMEMORY;
13053 }
13054 if (FAILED(rc))
13055 return rc;
13056
13057 /* memorize the peer Machine */
13058 unconst(mPeer) = aMachine;
13059 /* share the parent pointer */
13060 unconst(mParent) = aMachine->mParent;
13061
13062 /* take the pointers to data to share */
13063 mData.share(aMachine->mData);
13064 mSSData.share(aMachine->mSSData);
13065
13066 mUserData.share(aMachine->mUserData);
13067 mHWData.share(aMachine->mHWData);
13068 mMediumAttachments.share(aMachine->mMediumAttachments);
13069
13070 mStorageControllers.allocate();
13071 for (StorageControllerList::const_iterator
13072 it = aMachine->mStorageControllers->begin();
13073 it != aMachine->mStorageControllers->end();
13074 ++it)
13075 {
13076 ComObjPtr<StorageController> ctl;
13077 ctl.createObject();
13078 ctl->init(this, *it);
13079 mStorageControllers->push_back(ctl);
13080 }
13081
13082 mUSBControllers.allocate();
13083 for (USBControllerList::const_iterator
13084 it = aMachine->mUSBControllers->begin();
13085 it != aMachine->mUSBControllers->end();
13086 ++it)
13087 {
13088 ComObjPtr<USBController> ctl;
13089 ctl.createObject();
13090 ctl->init(this, *it);
13091 mUSBControllers->push_back(ctl);
13092 }
13093
13094 unconst(mBIOSSettings).createObject();
13095 mBIOSSettings->init(this, aMachine->mBIOSSettings);
13096
13097 unconst(mRecordingSettings).createObject();
13098 mRecordingSettings->init(this, aMachine->mRecordingSettings);
13099
13100 unconst(mTrustedPlatformModule).createObject();
13101 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
13102
13103 unconst(mNvramStore).createObject();
13104 mNvramStore->init(this, aMachine->mNvramStore);
13105
13106 /* create another GraphicsAdapter object that will be mutable */
13107 unconst(mGraphicsAdapter).createObject();
13108 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
13109 /* create another VRDEServer object that will be mutable */
13110 unconst(mVRDEServer).createObject();
13111 mVRDEServer->init(this, aMachine->mVRDEServer);
13112 /* create another audio settings object that will be mutable */
13113 unconst(mAudioSettings).createObject();
13114 mAudioSettings->init(this, aMachine->mAudioSettings);
13115 /* create a list of serial ports that will be mutable */
13116 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
13117 {
13118 unconst(mSerialPorts[slot]).createObject();
13119 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
13120 }
13121 /* create a list of parallel ports that will be mutable */
13122 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
13123 {
13124 unconst(mParallelPorts[slot]).createObject();
13125 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
13126 }
13127
13128 /* create another USB device filters object that will be mutable */
13129 unconst(mUSBDeviceFilters).createObject();
13130 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
13131
13132 /* create a list of network adapters that will be mutable */
13133 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
13134 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13135 {
13136 unconst(mNetworkAdapters[slot]).createObject();
13137 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
13138 }
13139
13140 /* create another bandwidth control object that will be mutable */
13141 unconst(mBandwidthControl).createObject();
13142 mBandwidthControl->init(this, aMachine->mBandwidthControl);
13143
13144 /* default is to delete saved state on Saved -> PoweredOff transition */
13145 mRemoveSavedState = true;
13146
13147 /* Confirm a successful initialization when it's the case */
13148 autoInitSpan.setSucceeded();
13149
13150 miNATNetworksStarted = 0;
13151
13152 LogFlowThisFuncLeave();
13153 return rc;
13154}
13155
13156/**
13157 * Uninitializes this session object. If the reason is other than
13158 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
13159 * or the client watcher code.
13160 *
13161 * @param aReason uninitialization reason
13162 *
13163 * @note Locks mParent + this object for writing.
13164 */
13165void SessionMachine::uninit(Uninit::Reason aReason)
13166{
13167 LogFlowThisFuncEnter();
13168 LogFlowThisFunc(("reason=%d\n", aReason));
13169
13170 /*
13171 * Strongly reference ourselves to prevent this object deletion after
13172 * mData->mSession.mMachine.setNull() below (which can release the last
13173 * reference and call the destructor). Important: this must be done before
13174 * accessing any members (and before AutoUninitSpan that does it as well).
13175 * This self reference will be released as the very last step on return.
13176 */
13177 ComObjPtr<SessionMachine> selfRef;
13178 if (aReason != Uninit::Unexpected)
13179 selfRef = this;
13180
13181 /* Enclose the state transition Ready->InUninit->NotReady */
13182 AutoUninitSpan autoUninitSpan(this);
13183 if (autoUninitSpan.uninitDone())
13184 {
13185 LogFlowThisFunc(("Already uninitialized\n"));
13186 LogFlowThisFuncLeave();
13187 return;
13188 }
13189
13190 if (autoUninitSpan.initFailed())
13191 {
13192 /* We've been called by init() because it's failed. It's not really
13193 * necessary (nor it's safe) to perform the regular uninit sequence
13194 * below, the following is enough.
13195 */
13196 LogFlowThisFunc(("Initialization failed.\n"));
13197 /* destroy the machine client token */
13198 if (mClientToken)
13199 {
13200 delete mClientToken;
13201 mClientToken = NULL;
13202 }
13203 uninitDataAndChildObjects();
13204 mData.free();
13205 unconst(mParent) = NULL;
13206 unconst(mPeer) = NULL;
13207 LogFlowThisFuncLeave();
13208 return;
13209 }
13210
13211 MachineState_T lastState;
13212 {
13213 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
13214 lastState = mData->mMachineState;
13215 }
13216 NOREF(lastState);
13217
13218#ifdef VBOX_WITH_USB
13219 // release all captured USB devices, but do this before requesting the locks below
13220 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
13221 {
13222 /* Console::captureUSBDevices() is called in the VM process only after
13223 * setting the machine state to Starting or Restoring.
13224 * Console::detachAllUSBDevices() will be called upon successful
13225 * termination. So, we need to release USB devices only if there was
13226 * an abnormal termination of a running VM.
13227 *
13228 * This is identical to SessionMachine::DetachAllUSBDevices except
13229 * for the aAbnormal argument. */
13230 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13231 AssertComRC(rc);
13232 NOREF(rc);
13233
13234 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13235 if (service)
13236 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
13237 }
13238#endif /* VBOX_WITH_USB */
13239
13240 // we need to lock this object in uninit() because the lock is shared
13241 // with mPeer (as well as data we modify below). mParent lock is needed
13242 // by several calls to it.
13243 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
13244
13245#ifdef VBOX_WITH_RESOURCE_USAGE_API
13246 /*
13247 * It is safe to call Machine::i_unregisterMetrics() here because
13248 * PerformanceCollector::samplerCallback no longer accesses guest methods
13249 * holding the lock.
13250 */
13251 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
13252 /* The guest must be unregistered after its metrics (@bugref{5949}). */
13253 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
13254 if (mCollectorGuest)
13255 {
13256 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
13257 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
13258 mCollectorGuest = NULL;
13259 }
13260#endif
13261
13262 if (aReason == Uninit::Abnormal)
13263 {
13264 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
13265
13266 /*
13267 * Move the VM to the 'Aborted' machine state unless we are restoring a
13268 * VM that was in the 'Saved' machine state. In that case, if the VM
13269 * fails before reaching either the 'Restoring' machine state or the
13270 * 'Running' machine state then we set the machine state to
13271 * 'AbortedSaved' in order to preserve the saved state file so that the
13272 * VM can be restored in the future.
13273 */
13274 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
13275 i_setMachineState(MachineState_AbortedSaved);
13276 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
13277 i_setMachineState(MachineState_Aborted);
13278 }
13279
13280 // any machine settings modified?
13281 if (mData->flModifications)
13282 {
13283 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
13284 i_rollback(false /* aNotify */);
13285 }
13286
13287 mData->mSession.mPID = NIL_RTPROCESS;
13288
13289 if (aReason == Uninit::Unexpected)
13290 {
13291 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
13292 * client watcher thread to update the set of machines that have open
13293 * sessions. */
13294 mParent->i_updateClientWatcher();
13295 }
13296
13297 /* uninitialize all remote controls */
13298 if (mData->mSession.mRemoteControls.size())
13299 {
13300 LogFlowThisFunc(("Closing remote sessions (%d):\n",
13301 mData->mSession.mRemoteControls.size()));
13302
13303 /* Always restart a the beginning, since the iterator is invalidated
13304 * by using erase(). */
13305 for (Data::Session::RemoteControlList::iterator
13306 it = mData->mSession.mRemoteControls.begin();
13307 it != mData->mSession.mRemoteControls.end();
13308 it = mData->mSession.mRemoteControls.begin())
13309 {
13310 ComPtr<IInternalSessionControl> pControl = *it;
13311 mData->mSession.mRemoteControls.erase(it);
13312 multilock.release();
13313 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
13314 HRESULT rc = pControl->Uninitialize();
13315 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
13316 if (FAILED(rc))
13317 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
13318 multilock.acquire();
13319 }
13320 mData->mSession.mRemoteControls.clear();
13321 }
13322
13323 /* Remove all references to the NAT network service. The service will stop
13324 * if all references (also from other VMs) are removed. */
13325 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
13326 {
13327 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13328 {
13329 BOOL enabled;
13330 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13331 if ( FAILED(hrc)
13332 || !enabled)
13333 continue;
13334
13335 NetworkAttachmentType_T type;
13336 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13337 if ( SUCCEEDED(hrc)
13338 && type == NetworkAttachmentType_NATNetwork)
13339 {
13340 Bstr name;
13341 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13342 if (SUCCEEDED(hrc))
13343 {
13344 multilock.release();
13345 Utf8Str strName(name);
13346 LogRel(("VM '%s' stops using NAT network '%s'\n",
13347 mUserData->s.strName.c_str(), strName.c_str()));
13348 mParent->i_natNetworkRefDec(strName);
13349 multilock.acquire();
13350 }
13351 }
13352 }
13353 }
13354
13355 /*
13356 * An expected uninitialization can come only from #i_checkForDeath().
13357 * Otherwise it means that something's gone really wrong (for example,
13358 * the Session implementation has released the VirtualBox reference
13359 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
13360 * etc). However, it's also possible, that the client releases the IPC
13361 * semaphore correctly (i.e. before it releases the VirtualBox reference),
13362 * but the VirtualBox release event comes first to the server process.
13363 * This case is practically possible, so we should not assert on an
13364 * unexpected uninit, just log a warning.
13365 */
13366
13367 if (aReason == Uninit::Unexpected)
13368 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
13369
13370 if (aReason != Uninit::Normal)
13371 {
13372 mData->mSession.mDirectControl.setNull();
13373 }
13374 else
13375 {
13376 /* this must be null here (see #OnSessionEnd()) */
13377 Assert(mData->mSession.mDirectControl.isNull());
13378 Assert(mData->mSession.mState == SessionState_Unlocking);
13379 Assert(!mData->mSession.mProgress.isNull());
13380 }
13381 if (mData->mSession.mProgress)
13382 {
13383 if (aReason == Uninit::Normal)
13384 mData->mSession.mProgress->i_notifyComplete(S_OK);
13385 else
13386 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
13387 COM_IIDOF(ISession),
13388 getComponentName(),
13389 tr("The VM session was aborted"));
13390 mData->mSession.mProgress.setNull();
13391 }
13392
13393 if (mConsoleTaskData.mProgress)
13394 {
13395 Assert(aReason == Uninit::Abnormal);
13396 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
13397 COM_IIDOF(ISession),
13398 getComponentName(),
13399 tr("The VM session was aborted"));
13400 mConsoleTaskData.mProgress.setNull();
13401 }
13402
13403 /* remove the association between the peer machine and this session machine */
13404 Assert( (SessionMachine*)mData->mSession.mMachine == this
13405 || aReason == Uninit::Unexpected);
13406
13407 /* reset the rest of session data */
13408 mData->mSession.mLockType = LockType_Null;
13409 mData->mSession.mMachine.setNull();
13410 mData->mSession.mState = SessionState_Unlocked;
13411 mData->mSession.mName.setNull();
13412
13413 /* destroy the machine client token before leaving the exclusive lock */
13414 if (mClientToken)
13415 {
13416 delete mClientToken;
13417 mClientToken = NULL;
13418 }
13419
13420 /* fire an event */
13421 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
13422
13423 uninitDataAndChildObjects();
13424
13425 /* free the essential data structure last */
13426 mData.free();
13427
13428 /* release the exclusive lock before setting the below two to NULL */
13429 multilock.release();
13430
13431 unconst(mParent) = NULL;
13432 unconst(mPeer) = NULL;
13433
13434 AuthLibUnload(&mAuthLibCtx);
13435
13436 LogFlowThisFuncLeave();
13437}
13438
13439// util::Lockable interface
13440////////////////////////////////////////////////////////////////////////////////
13441
13442/**
13443 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13444 * with the primary Machine instance (mPeer).
13445 */
13446RWLockHandle *SessionMachine::lockHandle() const
13447{
13448 AssertReturn(mPeer != NULL, NULL);
13449 return mPeer->lockHandle();
13450}
13451
13452// IInternalMachineControl methods
13453////////////////////////////////////////////////////////////////////////////////
13454
13455/**
13456 * Passes collected guest statistics to performance collector object
13457 */
13458HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13459 ULONG aCpuKernel, ULONG aCpuIdle,
13460 ULONG aMemTotal, ULONG aMemFree,
13461 ULONG aMemBalloon, ULONG aMemShared,
13462 ULONG aMemCache, ULONG aPageTotal,
13463 ULONG aAllocVMM, ULONG aFreeVMM,
13464 ULONG aBalloonedVMM, ULONG aSharedVMM,
13465 ULONG aVmNetRx, ULONG aVmNetTx)
13466{
13467#ifdef VBOX_WITH_RESOURCE_USAGE_API
13468 if (mCollectorGuest)
13469 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13470 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13471 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13472 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13473
13474 return S_OK;
13475#else
13476 NOREF(aValidStats);
13477 NOREF(aCpuUser);
13478 NOREF(aCpuKernel);
13479 NOREF(aCpuIdle);
13480 NOREF(aMemTotal);
13481 NOREF(aMemFree);
13482 NOREF(aMemBalloon);
13483 NOREF(aMemShared);
13484 NOREF(aMemCache);
13485 NOREF(aPageTotal);
13486 NOREF(aAllocVMM);
13487 NOREF(aFreeVMM);
13488 NOREF(aBalloonedVMM);
13489 NOREF(aSharedVMM);
13490 NOREF(aVmNetRx);
13491 NOREF(aVmNetTx);
13492 return E_NOTIMPL;
13493#endif
13494}
13495
13496////////////////////////////////////////////////////////////////////////////////
13497//
13498// SessionMachine task records
13499//
13500////////////////////////////////////////////////////////////////////////////////
13501
13502/**
13503 * Task record for saving the machine state.
13504 */
13505class SessionMachine::SaveStateTask
13506 : public Machine::Task
13507{
13508public:
13509 SaveStateTask(SessionMachine *m,
13510 Progress *p,
13511 const Utf8Str &t,
13512 Reason_T enmReason,
13513 const Utf8Str &strStateFilePath)
13514 : Task(m, p, t),
13515 m_enmReason(enmReason),
13516 m_strStateFilePath(strStateFilePath)
13517 {}
13518
13519private:
13520 void handler()
13521 {
13522 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13523 }
13524
13525 Reason_T m_enmReason;
13526 Utf8Str m_strStateFilePath;
13527
13528 friend class SessionMachine;
13529};
13530
13531/**
13532 * Task thread implementation for SessionMachine::SaveState(), called from
13533 * SessionMachine::taskHandler().
13534 *
13535 * @note Locks this object for writing.
13536 *
13537 * @param task
13538 * @return
13539 */
13540void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13541{
13542 LogFlowThisFuncEnter();
13543
13544 AutoCaller autoCaller(this);
13545 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13546 if (FAILED(autoCaller.rc()))
13547 {
13548 /* we might have been uninitialized because the session was accidentally
13549 * closed by the client, so don't assert */
13550 HRESULT rc = setError(E_FAIL,
13551 tr("The session has been accidentally closed"));
13552 task.m_pProgress->i_notifyComplete(rc);
13553 LogFlowThisFuncLeave();
13554 return;
13555 }
13556
13557 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13558
13559 HRESULT rc = S_OK;
13560
13561 try
13562 {
13563 ComPtr<IInternalSessionControl> directControl;
13564 if (mData->mSession.mLockType == LockType_VM)
13565 directControl = mData->mSession.mDirectControl;
13566 if (directControl.isNull())
13567 throw setError(VBOX_E_INVALID_VM_STATE,
13568 tr("Trying to save state without a running VM"));
13569 alock.release();
13570 BOOL fSuspendedBySave;
13571 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13572 Assert(!fSuspendedBySave);
13573 alock.acquire();
13574
13575 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13576 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13577 throw E_FAIL);
13578
13579 if (SUCCEEDED(rc))
13580 {
13581 mSSData->strStateFilePath = task.m_strStateFilePath;
13582
13583 /* save all VM settings */
13584 rc = i_saveSettings(NULL, alock);
13585 // no need to check whether VirtualBox.xml needs saving also since
13586 // we can't have a name change pending at this point
13587 }
13588 else
13589 {
13590 // On failure, set the state to the state we had at the beginning.
13591 i_setMachineState(task.m_machineStateBackup);
13592 i_updateMachineStateOnClient();
13593
13594 // Delete the saved state file (might have been already created).
13595 // No need to check whether this is shared with a snapshot here
13596 // because we certainly created a fresh saved state file here.
13597 i_deleteFile(task.m_strStateFilePath, true /* fIgnoreFailures */);
13598 }
13599 }
13600 catch (HRESULT aRC) { rc = aRC; }
13601
13602 task.m_pProgress->i_notifyComplete(rc);
13603
13604 LogFlowThisFuncLeave();
13605}
13606
13607/**
13608 * @note Locks this object for writing.
13609 */
13610HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13611{
13612 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13613}
13614
13615HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13616{
13617 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13618
13619 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13620 if (FAILED(rc)) return rc;
13621
13622 if ( mData->mMachineState != MachineState_Running
13623 && mData->mMachineState != MachineState_Paused
13624 )
13625 return setError(VBOX_E_INVALID_VM_STATE,
13626 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13627 Global::stringifyMachineState(mData->mMachineState));
13628
13629 ComObjPtr<Progress> pProgress;
13630 pProgress.createObject();
13631 rc = pProgress->init(i_getVirtualBox(),
13632 static_cast<IMachine *>(this) /* aInitiator */,
13633 tr("Saving the execution state of the virtual machine"),
13634 FALSE /* aCancelable */);
13635 if (FAILED(rc))
13636 return rc;
13637
13638 Utf8Str strStateFilePath;
13639 i_composeSavedStateFilename(strStateFilePath);
13640
13641 /* create and start the task on a separate thread (note that it will not
13642 * start working until we release alock) */
13643 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13644 rc = pTask->createThread();
13645 if (FAILED(rc))
13646 return rc;
13647
13648 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13649 i_setMachineState(MachineState_Saving);
13650 i_updateMachineStateOnClient();
13651
13652 pProgress.queryInterfaceTo(aProgress.asOutParam());
13653
13654 return S_OK;
13655}
13656
13657/**
13658 * @note Locks this object for writing.
13659 */
13660HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13661{
13662 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13663
13664 HRESULT rc = i_checkStateDependency(MutableStateDep);
13665 if (FAILED(rc)) return rc;
13666
13667 if ( mData->mMachineState != MachineState_PoweredOff
13668 && mData->mMachineState != MachineState_Teleported
13669 && mData->mMachineState != MachineState_Aborted
13670 )
13671 return setError(VBOX_E_INVALID_VM_STATE,
13672 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13673 Global::stringifyMachineState(mData->mMachineState));
13674
13675 com::Utf8Str stateFilePathFull;
13676 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13677 if (RT_FAILURE(vrc))
13678 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13679 tr("Invalid saved state file path '%s' (%Rrc)"),
13680 aSavedStateFile.c_str(),
13681 vrc);
13682
13683 mSSData->strStateFilePath = stateFilePathFull;
13684
13685 /* The below i_setMachineState() will detect the state transition and will
13686 * update the settings file */
13687
13688 return i_setMachineState(MachineState_Saved);
13689}
13690
13691/**
13692 * @note Locks this object for writing.
13693 */
13694HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13695{
13696 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13697
13698 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13699 if (FAILED(rc)) return rc;
13700
13701 if ( mData->mMachineState != MachineState_Saved
13702 && mData->mMachineState != MachineState_AbortedSaved)
13703 return setError(VBOX_E_INVALID_VM_STATE,
13704 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13705 Global::stringifyMachineState(mData->mMachineState));
13706
13707 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13708
13709 /*
13710 * Saved -> PoweredOff transition will be detected in the SessionMachine
13711 * and properly handled.
13712 */
13713 rc = i_setMachineState(MachineState_PoweredOff);
13714 return rc;
13715}
13716
13717
13718/**
13719 * @note Locks the same as #i_setMachineState() does.
13720 */
13721HRESULT SessionMachine::updateState(MachineState_T aState)
13722{
13723 return i_setMachineState(aState);
13724}
13725
13726/**
13727 * @note Locks this object for writing.
13728 */
13729HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13730{
13731 IProgress *pProgress(aProgress);
13732
13733 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13734
13735 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13736
13737 if (mData->mSession.mState != SessionState_Locked)
13738 return VBOX_E_INVALID_OBJECT_STATE;
13739
13740 if (!mData->mSession.mProgress.isNull())
13741 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13742
13743 /* If we didn't reference the NAT network service yet, add a reference to
13744 * force a start */
13745 if (miNATNetworksStarted < 1)
13746 {
13747 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13748 {
13749 BOOL enabled;
13750 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13751 if ( FAILED(hrc)
13752 || !enabled)
13753 continue;
13754
13755 NetworkAttachmentType_T type;
13756 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13757 if ( SUCCEEDED(hrc)
13758 && type == NetworkAttachmentType_NATNetwork)
13759 {
13760 Bstr name;
13761 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13762 if (SUCCEEDED(hrc))
13763 {
13764 Utf8Str strName(name);
13765 LogRel(("VM '%s' starts using NAT network '%s'\n",
13766 mUserData->s.strName.c_str(), strName.c_str()));
13767 mPeer->lockHandle()->unlockWrite();
13768 mParent->i_natNetworkRefInc(strName);
13769#ifdef RT_LOCK_STRICT
13770 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13771#else
13772 mPeer->lockHandle()->lockWrite();
13773#endif
13774 }
13775 }
13776 }
13777 miNATNetworksStarted++;
13778 }
13779
13780 LogFlowThisFunc(("returns S_OK.\n"));
13781 return S_OK;
13782}
13783
13784/**
13785 * @note Locks this object for writing.
13786 */
13787HRESULT SessionMachine::endPowerUp(LONG aResult)
13788{
13789 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13790
13791 if (mData->mSession.mState != SessionState_Locked)
13792 return VBOX_E_INVALID_OBJECT_STATE;
13793
13794 /* Finalize the LaunchVMProcess progress object. */
13795 if (mData->mSession.mProgress)
13796 {
13797 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13798 mData->mSession.mProgress.setNull();
13799 }
13800
13801 if (SUCCEEDED((HRESULT)aResult))
13802 {
13803#ifdef VBOX_WITH_RESOURCE_USAGE_API
13804 /* The VM has been powered up successfully, so it makes sense
13805 * now to offer the performance metrics for a running machine
13806 * object. Doing it earlier wouldn't be safe. */
13807 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13808 mData->mSession.mPID);
13809#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13810 }
13811
13812 return S_OK;
13813}
13814
13815/**
13816 * @note Locks this object for writing.
13817 */
13818HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13819{
13820 LogFlowThisFuncEnter();
13821
13822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13823
13824 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13825 E_FAIL);
13826
13827 /* create a progress object to track operation completion */
13828 ComObjPtr<Progress> pProgress;
13829 pProgress.createObject();
13830 pProgress->init(i_getVirtualBox(),
13831 static_cast<IMachine *>(this) /* aInitiator */,
13832 tr("Stopping the virtual machine"),
13833 FALSE /* aCancelable */);
13834
13835 /* fill in the console task data */
13836 mConsoleTaskData.mLastState = mData->mMachineState;
13837 mConsoleTaskData.mProgress = pProgress;
13838
13839 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13840 i_setMachineState(MachineState_Stopping);
13841
13842 pProgress.queryInterfaceTo(aProgress.asOutParam());
13843
13844 return S_OK;
13845}
13846
13847/**
13848 * @note Locks this object for writing.
13849 */
13850HRESULT SessionMachine::endPoweringDown(LONG aResult,
13851 const com::Utf8Str &aErrMsg)
13852{
13853 HRESULT const hrcResult = (HRESULT)aResult;
13854 LogFlowThisFuncEnter();
13855
13856 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13857
13858 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13859 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13860 && mConsoleTaskData.mLastState != MachineState_Null,
13861 E_FAIL);
13862
13863 /*
13864 * On failure, set the state to the state we had when BeginPoweringDown()
13865 * was called (this is expected by Console::PowerDown() and the associated
13866 * task). On success the VM process already changed the state to
13867 * MachineState_PoweredOff, so no need to do anything.
13868 */
13869 if (FAILED(hrcResult))
13870 i_setMachineState(mConsoleTaskData.mLastState);
13871
13872 /* notify the progress object about operation completion */
13873 Assert(mConsoleTaskData.mProgress);
13874 if (SUCCEEDED(hrcResult))
13875 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13876 else
13877 {
13878 if (aErrMsg.length())
13879 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13880 COM_IIDOF(ISession),
13881 getComponentName(),
13882 aErrMsg.c_str());
13883 else
13884 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13885 }
13886
13887 /* clear out the temporary saved state data */
13888 mConsoleTaskData.mLastState = MachineState_Null;
13889 mConsoleTaskData.mProgress.setNull();
13890
13891 LogFlowThisFuncLeave();
13892 return S_OK;
13893}
13894
13895
13896/**
13897 * Goes through the USB filters of the given machine to see if the given
13898 * device matches any filter or not.
13899 *
13900 * @note Locks the same as USBController::hasMatchingFilter() does.
13901 */
13902HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13903 BOOL *aMatched,
13904 ULONG *aMaskedInterfaces)
13905{
13906 LogFlowThisFunc(("\n"));
13907
13908#ifdef VBOX_WITH_USB
13909 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13910#else
13911 NOREF(aDevice);
13912 NOREF(aMaskedInterfaces);
13913 *aMatched = FALSE;
13914#endif
13915
13916 return S_OK;
13917}
13918
13919/**
13920 * @note Locks the same as Host::captureUSBDevice() does.
13921 */
13922HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13923{
13924 LogFlowThisFunc(("\n"));
13925
13926#ifdef VBOX_WITH_USB
13927 /* if captureDeviceForVM() fails, it must have set extended error info */
13928 clearError();
13929 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13930 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13931 return rc;
13932
13933 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13934 AssertReturn(service, E_FAIL);
13935 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13936#else
13937 RT_NOREF(aId, aCaptureFilename);
13938 return E_NOTIMPL;
13939#endif
13940}
13941
13942/**
13943 * @note Locks the same as Host::detachUSBDevice() does.
13944 */
13945HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13946 BOOL aDone)
13947{
13948 LogFlowThisFunc(("\n"));
13949
13950#ifdef VBOX_WITH_USB
13951 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13952 AssertReturn(service, E_FAIL);
13953 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13954#else
13955 NOREF(aId);
13956 NOREF(aDone);
13957 return E_NOTIMPL;
13958#endif
13959}
13960
13961/**
13962 * Inserts all machine filters to the USB proxy service and then calls
13963 * Host::autoCaptureUSBDevices().
13964 *
13965 * Called by Console from the VM process upon VM startup.
13966 *
13967 * @note Locks what called methods lock.
13968 */
13969HRESULT SessionMachine::autoCaptureUSBDevices()
13970{
13971 LogFlowThisFunc(("\n"));
13972
13973#ifdef VBOX_WITH_USB
13974 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13975 AssertComRC(rc);
13976 NOREF(rc);
13977
13978 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13979 AssertReturn(service, E_FAIL);
13980 return service->autoCaptureDevicesForVM(this);
13981#else
13982 return S_OK;
13983#endif
13984}
13985
13986/**
13987 * Removes all machine filters from the USB proxy service and then calls
13988 * Host::detachAllUSBDevices().
13989 *
13990 * Called by Console from the VM process upon normal VM termination or by
13991 * SessionMachine::uninit() upon abnormal VM termination (from under the
13992 * Machine/SessionMachine lock).
13993 *
13994 * @note Locks what called methods lock.
13995 */
13996HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13997{
13998 LogFlowThisFunc(("\n"));
13999
14000#ifdef VBOX_WITH_USB
14001 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
14002 AssertComRC(rc);
14003 NOREF(rc);
14004
14005 USBProxyService *service = mParent->i_host()->i_usbProxyService();
14006 AssertReturn(service, E_FAIL);
14007 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
14008#else
14009 NOREF(aDone);
14010 return S_OK;
14011#endif
14012}
14013
14014/**
14015 * @note Locks this object for writing.
14016 */
14017HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
14018 ComPtr<IProgress> &aProgress)
14019{
14020 LogFlowThisFuncEnter();
14021
14022 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
14023 /*
14024 * We don't assert below because it might happen that a non-direct session
14025 * informs us it is closed right after we've been uninitialized -- it's ok.
14026 */
14027
14028 /* get IInternalSessionControl interface */
14029 ComPtr<IInternalSessionControl> control(aSession);
14030
14031 ComAssertRet(!control.isNull(), E_INVALIDARG);
14032
14033 /* Creating a Progress object requires the VirtualBox lock, and
14034 * thus locking it here is required by the lock order rules. */
14035 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
14036
14037 if (control == mData->mSession.mDirectControl)
14038 {
14039 /* The direct session is being normally closed by the client process
14040 * ----------------------------------------------------------------- */
14041
14042 /* go to the closing state (essential for all open*Session() calls and
14043 * for #i_checkForDeath()) */
14044 Assert(mData->mSession.mState == SessionState_Locked);
14045 mData->mSession.mState = SessionState_Unlocking;
14046
14047 /* set direct control to NULL to release the remote instance */
14048 mData->mSession.mDirectControl.setNull();
14049 LogFlowThisFunc(("Direct control is set to NULL\n"));
14050
14051 if (mData->mSession.mProgress)
14052 {
14053 /* finalize the progress, someone might wait if a frontend
14054 * closes the session before powering on the VM. */
14055 mData->mSession.mProgress->notifyComplete(E_FAIL,
14056 COM_IIDOF(ISession),
14057 getComponentName(),
14058 tr("The VM session was closed before any attempt to power it on"));
14059 mData->mSession.mProgress.setNull();
14060 }
14061
14062 /* Create the progress object the client will use to wait until
14063 * #i_checkForDeath() is called to uninitialize this session object after
14064 * it releases the IPC semaphore.
14065 * Note! Because we're "reusing" mProgress here, this must be a proxy
14066 * object just like for LaunchVMProcess. */
14067 Assert(mData->mSession.mProgress.isNull());
14068 ComObjPtr<ProgressProxy> progress;
14069 progress.createObject();
14070 ComPtr<IUnknown> pPeer(mPeer);
14071 progress->init(mParent, pPeer,
14072 Bstr(tr("Closing session")).raw(),
14073 FALSE /* aCancelable */);
14074 progress.queryInterfaceTo(aProgress.asOutParam());
14075 mData->mSession.mProgress = progress;
14076 }
14077 else
14078 {
14079 /* the remote session is being normally closed */
14080 bool found = false;
14081 for (Data::Session::RemoteControlList::iterator
14082 it = mData->mSession.mRemoteControls.begin();
14083 it != mData->mSession.mRemoteControls.end();
14084 ++it)
14085 {
14086 if (control == *it)
14087 {
14088 found = true;
14089 // This MUST be erase(it), not remove(*it) as the latter
14090 // triggers a very nasty use after free due to the place where
14091 // the value "lives".
14092 mData->mSession.mRemoteControls.erase(it);
14093 break;
14094 }
14095 }
14096 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
14097 E_INVALIDARG);
14098 }
14099
14100 /* signal the client watcher thread, because the client is going away */
14101 mParent->i_updateClientWatcher();
14102
14103 LogFlowThisFuncLeave();
14104 return S_OK;
14105}
14106
14107HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14108 std::vector<com::Utf8Str> &aValues,
14109 std::vector<LONG64> &aTimestamps,
14110 std::vector<com::Utf8Str> &aFlags)
14111{
14112 LogFlowThisFunc(("\n"));
14113
14114#ifdef VBOX_WITH_GUEST_PROPS
14115 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14116
14117 size_t cEntries = mHWData->mGuestProperties.size();
14118 aNames.resize(cEntries);
14119 aValues.resize(cEntries);
14120 aTimestamps.resize(cEntries);
14121 aFlags.resize(cEntries);
14122
14123 size_t i = 0;
14124 for (HWData::GuestPropertyMap::const_iterator
14125 it = mHWData->mGuestProperties.begin();
14126 it != mHWData->mGuestProperties.end();
14127 ++it, ++i)
14128 {
14129 aNames[i] = it->first;
14130 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
14131 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
14132
14133 aValues[i] = it->second.strValue;
14134 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
14135 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
14136
14137 aTimestamps[i] = it->second.mTimestamp;
14138
14139 /* If it is NULL, keep it NULL. */
14140 if (it->second.mFlags)
14141 {
14142 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
14143 GuestPropWriteFlags(it->second.mFlags, szFlags);
14144 aFlags[i] = szFlags;
14145 }
14146 else
14147 aFlags[i] = "";
14148 }
14149 return S_OK;
14150#else
14151 ReturnComNotImplemented();
14152#endif
14153}
14154
14155HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
14156 const com::Utf8Str &aValue,
14157 LONG64 aTimestamp,
14158 const com::Utf8Str &aFlags,
14159 BOOL fWasDeleted)
14160{
14161 LogFlowThisFunc(("\n"));
14162
14163#ifdef VBOX_WITH_GUEST_PROPS
14164 try
14165 {
14166 /*
14167 * Convert input up front.
14168 */
14169 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
14170 if (aFlags.length())
14171 {
14172 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
14173 AssertRCReturn(vrc, E_INVALIDARG);
14174 }
14175
14176 /*
14177 * Now grab the object lock, validate the state and do the update.
14178 */
14179
14180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14181
14182 if (!Global::IsOnline(mData->mMachineState))
14183 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
14184
14185 i_setModified(IsModified_MachineData);
14186 mHWData.backup();
14187
14188 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
14189 if (it != mHWData->mGuestProperties.end())
14190 {
14191 if (!fWasDeleted)
14192 {
14193 it->second.strValue = aValue;
14194 it->second.mTimestamp = aTimestamp;
14195 it->second.mFlags = fFlags;
14196 }
14197 else
14198 mHWData->mGuestProperties.erase(it);
14199
14200 mData->mGuestPropertiesModified = TRUE;
14201 }
14202 else if (!fWasDeleted)
14203 {
14204 HWData::GuestProperty prop;
14205 prop.strValue = aValue;
14206 prop.mTimestamp = aTimestamp;
14207 prop.mFlags = fFlags;
14208
14209 mHWData->mGuestProperties[aName] = prop;
14210 mData->mGuestPropertiesModified = TRUE;
14211 }
14212
14213 alock.release();
14214
14215 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fWasDeleted);
14216 }
14217 catch (...)
14218 {
14219 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
14220 }
14221 return S_OK;
14222#else
14223 ReturnComNotImplemented();
14224#endif
14225}
14226
14227
14228HRESULT SessionMachine::lockMedia()
14229{
14230 AutoMultiWriteLock2 alock(this->lockHandle(),
14231 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14232
14233 AssertReturn( mData->mMachineState == MachineState_Starting
14234 || mData->mMachineState == MachineState_Restoring
14235 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
14236
14237 clearError();
14238 alock.release();
14239 return i_lockMedia();
14240}
14241
14242HRESULT SessionMachine::unlockMedia()
14243{
14244 HRESULT hrc = i_unlockMedia();
14245 return hrc;
14246}
14247
14248HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14249 ComPtr<IMediumAttachment> &aNewAttachment)
14250{
14251 // request the host lock first, since might be calling Host methods for getting host drives;
14252 // next, protect the media tree all the while we're in here, as well as our member variables
14253 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
14254 this->lockHandle(),
14255 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14256
14257 IMediumAttachment *iAttach = aAttachment;
14258 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
14259
14260 Utf8Str ctrlName;
14261 LONG lPort;
14262 LONG lDevice;
14263 bool fTempEject;
14264 {
14265 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14266
14267 /* Need to query the details first, as the IMediumAttachment reference
14268 * might be to the original settings, which we are going to change. */
14269 ctrlName = pAttach->i_getControllerName();
14270 lPort = pAttach->i_getPort();
14271 lDevice = pAttach->i_getDevice();
14272 fTempEject = pAttach->i_getTempEject();
14273 }
14274
14275 if (!fTempEject)
14276 {
14277 /* Remember previously mounted medium. The medium before taking the
14278 * backup is not necessarily the same thing. */
14279 ComObjPtr<Medium> oldmedium;
14280 oldmedium = pAttach->i_getMedium();
14281
14282 i_setModified(IsModified_Storage);
14283 mMediumAttachments.backup();
14284
14285 // The backup operation makes the pAttach reference point to the
14286 // old settings. Re-get the correct reference.
14287 pAttach = i_findAttachment(*mMediumAttachments.data(),
14288 ctrlName,
14289 lPort,
14290 lDevice);
14291
14292 {
14293 AutoCaller autoAttachCaller(this);
14294 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
14295
14296 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14297 if (!oldmedium.isNull())
14298 oldmedium->i_removeBackReference(mData->mUuid);
14299
14300 pAttach->i_updateMedium(NULL);
14301 pAttach->i_updateEjected();
14302 }
14303
14304 i_setModified(IsModified_Storage);
14305 }
14306 else
14307 {
14308 {
14309 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14310 pAttach->i_updateEjected();
14311 }
14312 }
14313
14314 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
14315
14316 return S_OK;
14317}
14318
14319HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
14320 com::Utf8Str &aResult)
14321{
14322 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14323
14324 HRESULT hr = S_OK;
14325
14326 if (!mAuthLibCtx.hAuthLibrary)
14327 {
14328 /* Load the external authentication library. */
14329 Bstr authLibrary;
14330 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
14331
14332 Utf8Str filename = authLibrary;
14333
14334 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
14335 if (RT_FAILURE(vrc))
14336 hr = setErrorBoth(E_FAIL, vrc,
14337 tr("Could not load the external authentication library '%s' (%Rrc)"),
14338 filename.c_str(), vrc);
14339 }
14340
14341 /* The auth library might need the machine lock. */
14342 alock.release();
14343
14344 if (FAILED(hr))
14345 return hr;
14346
14347 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
14348 {
14349 enum VRDEAuthParams
14350 {
14351 parmUuid = 1,
14352 parmGuestJudgement,
14353 parmUser,
14354 parmPassword,
14355 parmDomain,
14356 parmClientId
14357 };
14358
14359 AuthResult result = AuthResultAccessDenied;
14360
14361 Guid uuid(aAuthParams[parmUuid]);
14362 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
14363 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
14364
14365 result = AuthLibAuthenticate(&mAuthLibCtx,
14366 uuid.raw(), guestJudgement,
14367 aAuthParams[parmUser].c_str(),
14368 aAuthParams[parmPassword].c_str(),
14369 aAuthParams[parmDomain].c_str(),
14370 u32ClientId);
14371
14372 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
14373 size_t cbPassword = aAuthParams[parmPassword].length();
14374 if (cbPassword)
14375 {
14376 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
14377 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
14378 }
14379
14380 if (result == AuthResultAccessGranted)
14381 aResult = "granted";
14382 else
14383 aResult = "denied";
14384
14385 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
14386 aAuthParams[parmUser].c_str(), aResult.c_str()));
14387 }
14388 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
14389 {
14390 enum VRDEAuthDisconnectParams
14391 {
14392 parmUuid = 1,
14393 parmClientId
14394 };
14395
14396 Guid uuid(aAuthParams[parmUuid]);
14397 uint32_t u32ClientId = 0;
14398 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
14399 }
14400 else
14401 {
14402 hr = E_INVALIDARG;
14403 }
14404
14405 return hr;
14406}
14407
14408// public methods only for internal purposes
14409/////////////////////////////////////////////////////////////////////////////
14410
14411#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14412/**
14413 * Called from the client watcher thread to check for expected or unexpected
14414 * death of the client process that has a direct session to this machine.
14415 *
14416 * On Win32 and on OS/2, this method is called only when we've got the
14417 * mutex (i.e. the client has either died or terminated normally) so it always
14418 * returns @c true (the client is terminated, the session machine is
14419 * uninitialized).
14420 *
14421 * On other platforms, the method returns @c true if the client process has
14422 * terminated normally or abnormally and the session machine was uninitialized,
14423 * and @c false if the client process is still alive.
14424 *
14425 * @note Locks this object for writing.
14426 */
14427bool SessionMachine::i_checkForDeath()
14428{
14429 Uninit::Reason reason;
14430 bool terminated = false;
14431
14432 /* Enclose autoCaller with a block because calling uninit() from under it
14433 * will deadlock. */
14434 {
14435 AutoCaller autoCaller(this);
14436 if (!autoCaller.isOk())
14437 {
14438 /* return true if not ready, to cause the client watcher to exclude
14439 * the corresponding session from watching */
14440 LogFlowThisFunc(("Already uninitialized!\n"));
14441 return true;
14442 }
14443
14444 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14445
14446 /* Determine the reason of death: if the session state is Closing here,
14447 * everything is fine. Otherwise it means that the client did not call
14448 * OnSessionEnd() before it released the IPC semaphore. This may happen
14449 * either because the client process has abnormally terminated, or
14450 * because it simply forgot to call ISession::Close() before exiting. We
14451 * threat the latter also as an abnormal termination (see
14452 * Session::uninit() for details). */
14453 reason = mData->mSession.mState == SessionState_Unlocking ?
14454 Uninit::Normal :
14455 Uninit::Abnormal;
14456
14457 if (mClientToken)
14458 terminated = mClientToken->release();
14459 } /* AutoCaller block */
14460
14461 if (terminated)
14462 uninit(reason);
14463
14464 return terminated;
14465}
14466
14467void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14468{
14469 LogFlowThisFunc(("\n"));
14470
14471 strTokenId.setNull();
14472
14473 AutoCaller autoCaller(this);
14474 AssertComRCReturnVoid(autoCaller.rc());
14475
14476 Assert(mClientToken);
14477 if (mClientToken)
14478 mClientToken->getId(strTokenId);
14479}
14480#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14481IToken *SessionMachine::i_getToken()
14482{
14483 LogFlowThisFunc(("\n"));
14484
14485 AutoCaller autoCaller(this);
14486 AssertComRCReturn(autoCaller.rc(), NULL);
14487
14488 Assert(mClientToken);
14489 if (mClientToken)
14490 return mClientToken->getToken();
14491 else
14492 return NULL;
14493}
14494#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14495
14496Machine::ClientToken *SessionMachine::i_getClientToken()
14497{
14498 LogFlowThisFunc(("\n"));
14499
14500 AutoCaller autoCaller(this);
14501 AssertComRCReturn(autoCaller.rc(), NULL);
14502
14503 return mClientToken;
14504}
14505
14506
14507/**
14508 * @note Locks this object for reading.
14509 */
14510HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14511{
14512 LogFlowThisFunc(("\n"));
14513
14514 AutoCaller autoCaller(this);
14515 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14516
14517 ComPtr<IInternalSessionControl> directControl;
14518 {
14519 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14520 if (mData->mSession.mLockType == LockType_VM)
14521 directControl = mData->mSession.mDirectControl;
14522 }
14523
14524 /* ignore notifications sent after #OnSessionEnd() is called */
14525 if (!directControl)
14526 return S_OK;
14527
14528 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14529}
14530
14531/**
14532 * @note Locks this object for reading.
14533 */
14534HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
14535 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
14536 const Utf8Str &aGuestIp, LONG aGuestPort)
14537{
14538 LogFlowThisFunc(("\n"));
14539
14540 AutoCaller autoCaller(this);
14541 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14542
14543 ComPtr<IInternalSessionControl> directControl;
14544 {
14545 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14546 if (mData->mSession.mLockType == LockType_VM)
14547 directControl = mData->mSession.mDirectControl;
14548 }
14549
14550 /* ignore notifications sent after #OnSessionEnd() is called */
14551 if (!directControl)
14552 return S_OK;
14553 /*
14554 * instead acting like callback we ask IVirtualBox deliver corresponding event
14555 */
14556
14557 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14558 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14559 return S_OK;
14560}
14561
14562/**
14563 * @note Locks this object for reading.
14564 */
14565HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14566{
14567 LogFlowThisFunc(("\n"));
14568
14569 AutoCaller autoCaller(this);
14570 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14571
14572 ComPtr<IInternalSessionControl> directControl;
14573 {
14574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14575 if (mData->mSession.mLockType == LockType_VM)
14576 directControl = mData->mSession.mDirectControl;
14577 }
14578
14579 /* ignore notifications sent after #OnSessionEnd() is called */
14580 if (!directControl)
14581 return S_OK;
14582
14583 return directControl->OnAudioAdapterChange(audioAdapter);
14584}
14585
14586/**
14587 * @note Locks this object for reading.
14588 */
14589HRESULT SessionMachine::i_onHostAudioDeviceChange(IHostAudioDevice *aDevice, BOOL aNew, AudioDeviceState_T aState, IVirtualBoxErrorInfo *aErrInfo)
14590{
14591 LogFlowThisFunc(("\n"));
14592
14593 AutoCaller autoCaller(this);
14594 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14595
14596 ComPtr<IInternalSessionControl> directControl;
14597 {
14598 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14599 if (mData->mSession.mLockType == LockType_VM)
14600 directControl = mData->mSession.mDirectControl;
14601 }
14602
14603 /* ignore notifications sent after #OnSessionEnd() is called */
14604 if (!directControl)
14605 return S_OK;
14606
14607 return directControl->OnHostAudioDeviceChange(aDevice, aNew, aState, aErrInfo);
14608}
14609
14610/**
14611 * @note Locks this object for reading.
14612 */
14613HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14614{
14615 LogFlowThisFunc(("\n"));
14616
14617 AutoCaller autoCaller(this);
14618 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14619
14620 ComPtr<IInternalSessionControl> directControl;
14621 {
14622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14623 if (mData->mSession.mLockType == LockType_VM)
14624 directControl = mData->mSession.mDirectControl;
14625 }
14626
14627 /* ignore notifications sent after #OnSessionEnd() is called */
14628 if (!directControl)
14629 return S_OK;
14630
14631 return directControl->OnSerialPortChange(serialPort);
14632}
14633
14634/**
14635 * @note Locks this object for reading.
14636 */
14637HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14638{
14639 LogFlowThisFunc(("\n"));
14640
14641 AutoCaller autoCaller(this);
14642 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14643
14644 ComPtr<IInternalSessionControl> directControl;
14645 {
14646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14647 if (mData->mSession.mLockType == LockType_VM)
14648 directControl = mData->mSession.mDirectControl;
14649 }
14650
14651 /* ignore notifications sent after #OnSessionEnd() is called */
14652 if (!directControl)
14653 return S_OK;
14654
14655 return directControl->OnParallelPortChange(parallelPort);
14656}
14657
14658/**
14659 * @note Locks this object for reading.
14660 */
14661HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14662{
14663 LogFlowThisFunc(("\n"));
14664
14665 AutoCaller autoCaller(this);
14666 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14667
14668 ComPtr<IInternalSessionControl> directControl;
14669 {
14670 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14671 if (mData->mSession.mLockType == LockType_VM)
14672 directControl = mData->mSession.mDirectControl;
14673 }
14674
14675 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14676
14677 /* ignore notifications sent after #OnSessionEnd() is called */
14678 if (!directControl)
14679 return S_OK;
14680
14681 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14682}
14683
14684/**
14685 * @note Locks this object for reading.
14686 */
14687HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14688{
14689 LogFlowThisFunc(("\n"));
14690
14691 AutoCaller autoCaller(this);
14692 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14693
14694 ComPtr<IInternalSessionControl> directControl;
14695 {
14696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14697 if (mData->mSession.mLockType == LockType_VM)
14698 directControl = mData->mSession.mDirectControl;
14699 }
14700
14701 mParent->i_onMediumChanged(aAttachment);
14702
14703 /* ignore notifications sent after #OnSessionEnd() is called */
14704 if (!directControl)
14705 return S_OK;
14706
14707 return directControl->OnMediumChange(aAttachment, aForce);
14708}
14709
14710HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14711{
14712 LogFlowThisFunc(("\n"));
14713
14714 AutoCaller autoCaller(this);
14715 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14716
14717 ComPtr<IInternalSessionControl> directControl;
14718 {
14719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14720 if (mData->mSession.mLockType == LockType_VM)
14721 directControl = mData->mSession.mDirectControl;
14722 }
14723
14724 /* ignore notifications sent after #OnSessionEnd() is called */
14725 if (!directControl)
14726 return S_OK;
14727
14728 return directControl->OnVMProcessPriorityChange(aPriority);
14729}
14730
14731/**
14732 * @note Locks this object for reading.
14733 */
14734HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14735{
14736 LogFlowThisFunc(("\n"));
14737
14738 AutoCaller autoCaller(this);
14739 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14740
14741 ComPtr<IInternalSessionControl> directControl;
14742 {
14743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14744 if (mData->mSession.mLockType == LockType_VM)
14745 directControl = mData->mSession.mDirectControl;
14746 }
14747
14748 /* ignore notifications sent after #OnSessionEnd() is called */
14749 if (!directControl)
14750 return S_OK;
14751
14752 return directControl->OnCPUChange(aCPU, aRemove);
14753}
14754
14755HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14756{
14757 LogFlowThisFunc(("\n"));
14758
14759 AutoCaller autoCaller(this);
14760 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14761
14762 ComPtr<IInternalSessionControl> directControl;
14763 {
14764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14765 if (mData->mSession.mLockType == LockType_VM)
14766 directControl = mData->mSession.mDirectControl;
14767 }
14768
14769 /* ignore notifications sent after #OnSessionEnd() is called */
14770 if (!directControl)
14771 return S_OK;
14772
14773 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14774}
14775
14776/**
14777 * @note Locks this object for reading.
14778 */
14779HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14780{
14781 LogFlowThisFunc(("\n"));
14782
14783 AutoCaller autoCaller(this);
14784 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14785
14786 ComPtr<IInternalSessionControl> directControl;
14787 {
14788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14789 if (mData->mSession.mLockType == LockType_VM)
14790 directControl = mData->mSession.mDirectControl;
14791 }
14792
14793 /* ignore notifications sent after #OnSessionEnd() is called */
14794 if (!directControl)
14795 return S_OK;
14796
14797 return directControl->OnVRDEServerChange(aRestart);
14798}
14799
14800/**
14801 * @note Locks this object for reading.
14802 */
14803HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14804{
14805 LogFlowThisFunc(("\n"));
14806
14807 AutoCaller autoCaller(this);
14808 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14809
14810 ComPtr<IInternalSessionControl> directControl;
14811 {
14812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14813 if (mData->mSession.mLockType == LockType_VM)
14814 directControl = mData->mSession.mDirectControl;
14815 }
14816
14817 /* ignore notifications sent after #OnSessionEnd() is called */
14818 if (!directControl)
14819 return S_OK;
14820
14821 return directControl->OnRecordingChange(aEnable);
14822}
14823
14824/**
14825 * @note Locks this object for reading.
14826 */
14827HRESULT SessionMachine::i_onUSBControllerChange()
14828{
14829 LogFlowThisFunc(("\n"));
14830
14831 AutoCaller autoCaller(this);
14832 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14833
14834 ComPtr<IInternalSessionControl> directControl;
14835 {
14836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14837 if (mData->mSession.mLockType == LockType_VM)
14838 directControl = mData->mSession.mDirectControl;
14839 }
14840
14841 /* ignore notifications sent after #OnSessionEnd() is called */
14842 if (!directControl)
14843 return S_OK;
14844
14845 return directControl->OnUSBControllerChange();
14846}
14847
14848/**
14849 * @note Locks this object for reading.
14850 */
14851HRESULT SessionMachine::i_onSharedFolderChange()
14852{
14853 LogFlowThisFunc(("\n"));
14854
14855 AutoCaller autoCaller(this);
14856 AssertComRCReturnRC(autoCaller.rc());
14857
14858 ComPtr<IInternalSessionControl> directControl;
14859 {
14860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14861 if (mData->mSession.mLockType == LockType_VM)
14862 directControl = mData->mSession.mDirectControl;
14863 }
14864
14865 /* ignore notifications sent after #OnSessionEnd() is called */
14866 if (!directControl)
14867 return S_OK;
14868
14869 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14870}
14871
14872/**
14873 * @note Locks this object for reading.
14874 */
14875HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14876{
14877 LogFlowThisFunc(("\n"));
14878
14879 AutoCaller autoCaller(this);
14880 AssertComRCReturnRC(autoCaller.rc());
14881
14882 ComPtr<IInternalSessionControl> directControl;
14883 {
14884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14885 if (mData->mSession.mLockType == LockType_VM)
14886 directControl = mData->mSession.mDirectControl;
14887 }
14888
14889 /* ignore notifications sent after #OnSessionEnd() is called */
14890 if (!directControl)
14891 return S_OK;
14892
14893 return directControl->OnClipboardModeChange(aClipboardMode);
14894}
14895
14896/**
14897 * @note Locks this object for reading.
14898 */
14899HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14900{
14901 LogFlowThisFunc(("\n"));
14902
14903 AutoCaller autoCaller(this);
14904 AssertComRCReturnRC(autoCaller.rc());
14905
14906 ComPtr<IInternalSessionControl> directControl;
14907 {
14908 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14909 if (mData->mSession.mLockType == LockType_VM)
14910 directControl = mData->mSession.mDirectControl;
14911 }
14912
14913 /* ignore notifications sent after #OnSessionEnd() is called */
14914 if (!directControl)
14915 return S_OK;
14916
14917 return directControl->OnClipboardFileTransferModeChange(aEnable);
14918}
14919
14920/**
14921 * @note Locks this object for reading.
14922 */
14923HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14924{
14925 LogFlowThisFunc(("\n"));
14926
14927 AutoCaller autoCaller(this);
14928 AssertComRCReturnRC(autoCaller.rc());
14929
14930 ComPtr<IInternalSessionControl> directControl;
14931 {
14932 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14933 if (mData->mSession.mLockType == LockType_VM)
14934 directControl = mData->mSession.mDirectControl;
14935 }
14936
14937 /* ignore notifications sent after #OnSessionEnd() is called */
14938 if (!directControl)
14939 return S_OK;
14940
14941 return directControl->OnDnDModeChange(aDnDMode);
14942}
14943
14944/**
14945 * @note Locks this object for reading.
14946 */
14947HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14948{
14949 LogFlowThisFunc(("\n"));
14950
14951 AutoCaller autoCaller(this);
14952 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14953
14954 ComPtr<IInternalSessionControl> directControl;
14955 {
14956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14957 if (mData->mSession.mLockType == LockType_VM)
14958 directControl = mData->mSession.mDirectControl;
14959 }
14960
14961 /* ignore notifications sent after #OnSessionEnd() is called */
14962 if (!directControl)
14963 return S_OK;
14964
14965 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14966}
14967
14968/**
14969 * @note Locks this object for reading.
14970 */
14971HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14972{
14973 LogFlowThisFunc(("\n"));
14974
14975 AutoCaller autoCaller(this);
14976 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14977
14978 ComPtr<IInternalSessionControl> directControl;
14979 {
14980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14981 if (mData->mSession.mLockType == LockType_VM)
14982 directControl = mData->mSession.mDirectControl;
14983 }
14984
14985 /* ignore notifications sent after #OnSessionEnd() is called */
14986 if (!directControl)
14987 return S_OK;
14988
14989 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14990}
14991
14992/**
14993 * Returns @c true if this machine's USB controller reports it has a matching
14994 * filter for the given USB device and @c false otherwise.
14995 *
14996 * @note locks this object for reading.
14997 */
14998bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14999{
15000 AutoCaller autoCaller(this);
15001 /* silently return if not ready -- this method may be called after the
15002 * direct machine session has been called */
15003 if (!autoCaller.isOk())
15004 return false;
15005
15006#ifdef VBOX_WITH_USB
15007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15008
15009 switch (mData->mMachineState)
15010 {
15011 case MachineState_Starting:
15012 case MachineState_Restoring:
15013 case MachineState_TeleportingIn:
15014 case MachineState_Paused:
15015 case MachineState_Running:
15016 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
15017 * elsewhere... */
15018 alock.release();
15019 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
15020 default: break;
15021 }
15022#else
15023 NOREF(aDevice);
15024 NOREF(aMaskedIfs);
15025#endif
15026 return false;
15027}
15028
15029/**
15030 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
15031 */
15032HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
15033 IVirtualBoxErrorInfo *aError,
15034 ULONG aMaskedIfs,
15035 const com::Utf8Str &aCaptureFilename)
15036{
15037 LogFlowThisFunc(("\n"));
15038
15039 AutoCaller autoCaller(this);
15040
15041 /* This notification may happen after the machine object has been
15042 * uninitialized (the session was closed), so don't assert. */
15043 if (FAILED(autoCaller.rc())) return autoCaller.rc();
15044
15045 ComPtr<IInternalSessionControl> directControl;
15046 {
15047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15048 if (mData->mSession.mLockType == LockType_VM)
15049 directControl = mData->mSession.mDirectControl;
15050 }
15051
15052 /* fail on notifications sent after #OnSessionEnd() is called, it is
15053 * expected by the caller */
15054 if (!directControl)
15055 return E_FAIL;
15056
15057 /* No locks should be held at this point. */
15058 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
15059 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
15060
15061 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
15062}
15063
15064/**
15065 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
15066 */
15067HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
15068 IVirtualBoxErrorInfo *aError)
15069{
15070 LogFlowThisFunc(("\n"));
15071
15072 AutoCaller autoCaller(this);
15073
15074 /* This notification may happen after the machine object has been
15075 * uninitialized (the session was closed), so don't assert. */
15076 if (FAILED(autoCaller.rc())) return autoCaller.rc();
15077
15078 ComPtr<IInternalSessionControl> directControl;
15079 {
15080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15081 if (mData->mSession.mLockType == LockType_VM)
15082 directControl = mData->mSession.mDirectControl;
15083 }
15084
15085 /* fail on notifications sent after #OnSessionEnd() is called, it is
15086 * expected by the caller */
15087 if (!directControl)
15088 return E_FAIL;
15089
15090 /* No locks should be held at this point. */
15091 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
15092 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
15093
15094 return directControl->OnUSBDeviceDetach(aId, aError);
15095}
15096
15097// protected methods
15098/////////////////////////////////////////////////////////////////////////////
15099
15100/**
15101 * Deletes the given file if it is no longer in use by either the current machine state
15102 * (if the machine is "saved") or any of the machine's snapshots.
15103 *
15104 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
15105 * but is different for each SnapshotMachine. When calling this, the order of calling this
15106 * function on the one hand and changing that variable OR the snapshots tree on the other hand
15107 * is therefore critical. I know, it's all rather messy.
15108 *
15109 * @param strStateFile
15110 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
15111 * the test for whether the saved state file is in use.
15112 */
15113void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
15114 Snapshot *pSnapshotToIgnore)
15115{
15116 // it is safe to delete this saved state file if it is not currently in use by the machine ...
15117 if ( (strStateFile.isNotEmpty())
15118 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
15119 )
15120 // ... and it must also not be shared with other snapshots
15121 if ( !mData->mFirstSnapshot
15122 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
15123 // this checks the SnapshotMachine's state file paths
15124 )
15125 i_deleteFile(strStateFile, true /* fIgnoreFailures */);
15126}
15127
15128/**
15129 * Locks the attached media.
15130 *
15131 * All attached hard disks are locked for writing and DVD/floppy are locked for
15132 * reading. Parents of attached hard disks (if any) are locked for reading.
15133 *
15134 * This method also performs accessibility check of all media it locks: if some
15135 * media is inaccessible, the method will return a failure and a bunch of
15136 * extended error info objects per each inaccessible medium.
15137 *
15138 * Note that this method is atomic: if it returns a success, all media are
15139 * locked as described above; on failure no media is locked at all (all
15140 * succeeded individual locks will be undone).
15141 *
15142 * The caller is responsible for doing the necessary state sanity checks.
15143 *
15144 * The locks made by this method must be undone by calling #unlockMedia() when
15145 * no more needed.
15146 */
15147HRESULT SessionMachine::i_lockMedia()
15148{
15149 AutoCaller autoCaller(this);
15150 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15151
15152 AutoMultiWriteLock2 alock(this->lockHandle(),
15153 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
15154
15155 /* bail out if trying to lock things with already set up locking */
15156 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
15157
15158 MultiResult mrc(S_OK);
15159
15160 /* Collect locking information for all medium objects attached to the VM. */
15161 for (MediumAttachmentList::const_iterator
15162 it = mMediumAttachments->begin();
15163 it != mMediumAttachments->end();
15164 ++it)
15165 {
15166 MediumAttachment *pAtt = *it;
15167 DeviceType_T devType = pAtt->i_getType();
15168 Medium *pMedium = pAtt->i_getMedium();
15169
15170 MediumLockList *pMediumLockList(new MediumLockList());
15171 // There can be attachments without a medium (floppy/dvd), and thus
15172 // it's impossible to create a medium lock list. It still makes sense
15173 // to have the empty medium lock list in the map in case a medium is
15174 // attached later.
15175 if (pMedium != NULL)
15176 {
15177 MediumType_T mediumType = pMedium->i_getType();
15178 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
15179 || mediumType == MediumType_Shareable;
15180 bool fIsVitalImage = (devType == DeviceType_HardDisk);
15181
15182 alock.release();
15183 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
15184 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
15185 false /* fMediumLockWriteAll */,
15186 NULL,
15187 *pMediumLockList);
15188 alock.acquire();
15189 if (FAILED(mrc))
15190 {
15191 delete pMediumLockList;
15192 mData->mSession.mLockedMedia.Clear();
15193 break;
15194 }
15195 }
15196
15197 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
15198 if (FAILED(rc))
15199 {
15200 mData->mSession.mLockedMedia.Clear();
15201 mrc = setError(rc,
15202 tr("Collecting locking information for all attached media failed"));
15203 break;
15204 }
15205 }
15206
15207 if (SUCCEEDED(mrc))
15208 {
15209 /* Now lock all media. If this fails, nothing is locked. */
15210 alock.release();
15211 HRESULT rc = mData->mSession.mLockedMedia.Lock();
15212 alock.acquire();
15213 if (FAILED(rc))
15214 {
15215 mrc = setError(rc,
15216 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
15217 }
15218 }
15219
15220 return mrc;
15221}
15222
15223/**
15224 * Undoes the locks made by by #lockMedia().
15225 */
15226HRESULT SessionMachine::i_unlockMedia()
15227{
15228 AutoCaller autoCaller(this);
15229 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15230
15231 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15232
15233 /* we may be holding important error info on the current thread;
15234 * preserve it */
15235 ErrorInfoKeeper eik;
15236
15237 HRESULT rc = mData->mSession.mLockedMedia.Clear();
15238 AssertComRC(rc);
15239 return rc;
15240}
15241
15242/**
15243 * Helper to change the machine state (reimplementation).
15244 *
15245 * @note Locks this object for writing.
15246 * @note This method must not call i_saveSettings or SaveSettings, otherwise
15247 * it can cause crashes in random places due to unexpectedly committing
15248 * the current settings. The caller is responsible for that. The call
15249 * to saveStateSettings is fine, because this method does not commit.
15250 */
15251HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
15252{
15253 LogFlowThisFuncEnter();
15254
15255 AutoCaller autoCaller(this);
15256 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15257
15258 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15259
15260 MachineState_T oldMachineState = mData->mMachineState;
15261
15262 AssertMsgReturn(oldMachineState != aMachineState,
15263 ("oldMachineState=%s, aMachineState=%s\n",
15264 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
15265 E_FAIL);
15266
15267 HRESULT rc = S_OK;
15268
15269 int stsFlags = 0;
15270 bool deleteSavedState = false;
15271
15272 /* detect some state transitions */
15273
15274 if ( ( ( oldMachineState == MachineState_Saved
15275 || oldMachineState == MachineState_AbortedSaved
15276 )
15277 && aMachineState == MachineState_Restoring
15278 )
15279 || ( ( oldMachineState == MachineState_PoweredOff
15280 || oldMachineState == MachineState_Teleported
15281 || oldMachineState == MachineState_Aborted
15282 )
15283 && ( aMachineState == MachineState_TeleportingIn
15284 || aMachineState == MachineState_Starting
15285 )
15286 )
15287 )
15288 {
15289 /* The EMT thread is about to start */
15290
15291 /* Nothing to do here for now... */
15292
15293 /// @todo NEWMEDIA don't let mDVDDrive and other children
15294 /// change anything when in the Starting/Restoring state
15295 }
15296 else if ( ( oldMachineState == MachineState_Running
15297 || oldMachineState == MachineState_Paused
15298 || oldMachineState == MachineState_Teleporting
15299 || oldMachineState == MachineState_OnlineSnapshotting
15300 || oldMachineState == MachineState_LiveSnapshotting
15301 || oldMachineState == MachineState_Stuck
15302 || oldMachineState == MachineState_Starting
15303 || oldMachineState == MachineState_Stopping
15304 || oldMachineState == MachineState_Saving
15305 || oldMachineState == MachineState_Restoring
15306 || oldMachineState == MachineState_TeleportingPausedVM
15307 || oldMachineState == MachineState_TeleportingIn
15308 )
15309 && ( aMachineState == MachineState_PoweredOff
15310 || aMachineState == MachineState_Saved
15311 || aMachineState == MachineState_Teleported
15312 || aMachineState == MachineState_Aborted
15313 || aMachineState == MachineState_AbortedSaved
15314 )
15315 )
15316 {
15317 /* The EMT thread has just stopped, unlock attached media. Note that as
15318 * opposed to locking that is done from Console, we do unlocking here
15319 * because the VM process may have aborted before having a chance to
15320 * properly unlock all media it locked. */
15321
15322 unlockMedia();
15323 }
15324
15325 if (oldMachineState == MachineState_Restoring)
15326 {
15327 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
15328 {
15329 /*
15330 * delete the saved state file once the machine has finished
15331 * restoring from it (note that Console sets the state from
15332 * Restoring to AbortedSaved if the VM couldn't restore successfully,
15333 * to give the user an ability to fix an error and retry --
15334 * we keep the saved state file in this case)
15335 */
15336 deleteSavedState = true;
15337 }
15338 }
15339 else if ( oldMachineState == MachineState_Saved
15340 && ( aMachineState == MachineState_PoweredOff
15341 || aMachineState == MachineState_Teleported
15342 )
15343 )
15344 {
15345 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
15346 deleteSavedState = true;
15347 mData->mCurrentStateModified = TRUE;
15348 stsFlags |= SaveSTS_CurStateModified;
15349 }
15350 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
15351 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
15352
15353 if ( aMachineState == MachineState_Starting
15354 || aMachineState == MachineState_Restoring
15355 || aMachineState == MachineState_TeleportingIn
15356 )
15357 {
15358 /* set the current state modified flag to indicate that the current
15359 * state is no more identical to the state in the
15360 * current snapshot */
15361 if (!mData->mCurrentSnapshot.isNull())
15362 {
15363 mData->mCurrentStateModified = TRUE;
15364 stsFlags |= SaveSTS_CurStateModified;
15365 }
15366 }
15367
15368 if (deleteSavedState)
15369 {
15370 if (mRemoveSavedState)
15371 {
15372 Assert(!mSSData->strStateFilePath.isEmpty());
15373
15374 // it is safe to delete the saved state file if ...
15375 if ( !mData->mFirstSnapshot // ... we have no snapshots or
15376 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
15377 // ... none of the snapshots share the saved state file
15378 )
15379 i_deleteFile(mSSData->strStateFilePath, true /* fIgnoreFailures */);
15380 }
15381
15382 mSSData->strStateFilePath.setNull();
15383 stsFlags |= SaveSTS_StateFilePath;
15384 }
15385
15386 /* redirect to the underlying peer machine */
15387 mPeer->i_setMachineState(aMachineState);
15388
15389 if ( oldMachineState != MachineState_RestoringSnapshot
15390 && ( aMachineState == MachineState_PoweredOff
15391 || aMachineState == MachineState_Teleported
15392 || aMachineState == MachineState_Aborted
15393 || aMachineState == MachineState_AbortedSaved
15394 || aMachineState == MachineState_Saved))
15395 {
15396 /* the machine has stopped execution
15397 * (or the saved state file was adopted) */
15398 stsFlags |= SaveSTS_StateTimeStamp;
15399 }
15400
15401 if ( ( oldMachineState == MachineState_PoweredOff
15402 || oldMachineState == MachineState_Aborted
15403 || oldMachineState == MachineState_Teleported
15404 )
15405 && aMachineState == MachineState_Saved)
15406 {
15407 /* the saved state file was adopted */
15408 Assert(!mSSData->strStateFilePath.isEmpty());
15409 stsFlags |= SaveSTS_StateFilePath;
15410 }
15411
15412#ifdef VBOX_WITH_GUEST_PROPS
15413 if ( aMachineState == MachineState_PoweredOff
15414 || aMachineState == MachineState_Aborted
15415 || aMachineState == MachineState_Teleported)
15416 {
15417 /* Make sure any transient guest properties get removed from the
15418 * property store on shutdown. */
15419 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
15420
15421 /* remove it from the settings representation */
15422 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
15423 for (settings::GuestPropertiesList::iterator
15424 it = llGuestProperties.begin();
15425 it != llGuestProperties.end();
15426 /*nothing*/)
15427 {
15428 const settings::GuestProperty &prop = *it;
15429 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
15430 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
15431 {
15432 it = llGuestProperties.erase(it);
15433 fNeedsSaving = true;
15434 }
15435 else
15436 {
15437 ++it;
15438 }
15439 }
15440
15441 /* Additionally remove it from the HWData representation. Required to
15442 * keep everything in sync, as this is what the API keeps using. */
15443 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
15444 for (HWData::GuestPropertyMap::iterator
15445 it = llHWGuestProperties.begin();
15446 it != llHWGuestProperties.end();
15447 /*nothing*/)
15448 {
15449 uint32_t fFlags = it->second.mFlags;
15450 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
15451 {
15452 /* iterator where we need to continue after the erase call
15453 * (C++03 is a fact still, and it doesn't return the iterator
15454 * which would allow continuing) */
15455 HWData::GuestPropertyMap::iterator it2 = it;
15456 ++it2;
15457 llHWGuestProperties.erase(it);
15458 it = it2;
15459 fNeedsSaving = true;
15460 }
15461 else
15462 {
15463 ++it;
15464 }
15465 }
15466
15467 if (fNeedsSaving)
15468 {
15469 mData->mCurrentStateModified = TRUE;
15470 stsFlags |= SaveSTS_CurStateModified;
15471 }
15472 }
15473#endif /* VBOX_WITH_GUEST_PROPS */
15474
15475 rc = i_saveStateSettings(stsFlags);
15476
15477 if ( ( oldMachineState != MachineState_PoweredOff
15478 && oldMachineState != MachineState_Aborted
15479 && oldMachineState != MachineState_Teleported
15480 )
15481 && ( aMachineState == MachineState_PoweredOff
15482 || aMachineState == MachineState_Aborted
15483 || aMachineState == MachineState_Teleported
15484 )
15485 )
15486 {
15487 /* we've been shut down for any reason */
15488 /* no special action so far */
15489 }
15490
15491 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, ::stringifyMachineState(mData->mMachineState) ));
15492 LogFlowThisFuncLeave();
15493 return rc;
15494}
15495
15496/**
15497 * Sends the current machine state value to the VM process.
15498 *
15499 * @note Locks this object for reading, then calls a client process.
15500 */
15501HRESULT SessionMachine::i_updateMachineStateOnClient()
15502{
15503 AutoCaller autoCaller(this);
15504 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15505
15506 ComPtr<IInternalSessionControl> directControl;
15507 {
15508 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15509 AssertReturn(!!mData, E_FAIL);
15510 if (mData->mSession.mLockType == LockType_VM)
15511 directControl = mData->mSession.mDirectControl;
15512
15513 /* directControl may be already set to NULL here in #OnSessionEnd()
15514 * called too early by the direct session process while there is still
15515 * some operation (like deleting the snapshot) in progress. The client
15516 * process in this case is waiting inside Session::close() for the
15517 * "end session" process object to complete, while #uninit() called by
15518 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15519 * operation to complete. For now, we accept this inconsistent behavior
15520 * and simply do nothing here. */
15521
15522 if (mData->mSession.mState == SessionState_Unlocking)
15523 return S_OK;
15524 }
15525
15526 /* ignore notifications sent after #OnSessionEnd() is called */
15527 if (!directControl)
15528 return S_OK;
15529
15530 return directControl->UpdateMachineState(mData->mMachineState);
15531}
15532
15533
15534/*static*/
15535HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15536{
15537 va_list args;
15538 va_start(args, pcszMsg);
15539 HRESULT rc = setErrorInternalV(aResultCode,
15540 getStaticClassIID(),
15541 getStaticComponentName(),
15542 pcszMsg, args,
15543 false /* aWarning */,
15544 true /* aLogIt */);
15545 va_end(args);
15546 return rc;
15547}
15548
15549
15550HRESULT Machine::updateState(MachineState_T aState)
15551{
15552 NOREF(aState);
15553 ReturnComNotImplemented();
15554}
15555
15556HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15557{
15558 NOREF(aProgress);
15559 ReturnComNotImplemented();
15560}
15561
15562HRESULT Machine::endPowerUp(LONG aResult)
15563{
15564 NOREF(aResult);
15565 ReturnComNotImplemented();
15566}
15567
15568HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15569{
15570 NOREF(aProgress);
15571 ReturnComNotImplemented();
15572}
15573
15574HRESULT Machine::endPoweringDown(LONG aResult,
15575 const com::Utf8Str &aErrMsg)
15576{
15577 NOREF(aResult);
15578 NOREF(aErrMsg);
15579 ReturnComNotImplemented();
15580}
15581
15582HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15583 BOOL *aMatched,
15584 ULONG *aMaskedInterfaces)
15585{
15586 NOREF(aDevice);
15587 NOREF(aMatched);
15588 NOREF(aMaskedInterfaces);
15589 ReturnComNotImplemented();
15590
15591}
15592
15593HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15594{
15595 NOREF(aId); NOREF(aCaptureFilename);
15596 ReturnComNotImplemented();
15597}
15598
15599HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15600 BOOL aDone)
15601{
15602 NOREF(aId);
15603 NOREF(aDone);
15604 ReturnComNotImplemented();
15605}
15606
15607HRESULT Machine::autoCaptureUSBDevices()
15608{
15609 ReturnComNotImplemented();
15610}
15611
15612HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15613{
15614 NOREF(aDone);
15615 ReturnComNotImplemented();
15616}
15617
15618HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15619 ComPtr<IProgress> &aProgress)
15620{
15621 NOREF(aSession);
15622 NOREF(aProgress);
15623 ReturnComNotImplemented();
15624}
15625
15626HRESULT Machine::finishOnlineMergeMedium()
15627{
15628 ReturnComNotImplemented();
15629}
15630
15631HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15632 std::vector<com::Utf8Str> &aValues,
15633 std::vector<LONG64> &aTimestamps,
15634 std::vector<com::Utf8Str> &aFlags)
15635{
15636 NOREF(aNames);
15637 NOREF(aValues);
15638 NOREF(aTimestamps);
15639 NOREF(aFlags);
15640 ReturnComNotImplemented();
15641}
15642
15643HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15644 const com::Utf8Str &aValue,
15645 LONG64 aTimestamp,
15646 const com::Utf8Str &aFlags,
15647 BOOL fWasDeleted)
15648{
15649 NOREF(aName);
15650 NOREF(aValue);
15651 NOREF(aTimestamp);
15652 NOREF(aFlags);
15653 NOREF(fWasDeleted);
15654 ReturnComNotImplemented();
15655}
15656
15657HRESULT Machine::lockMedia()
15658{
15659 ReturnComNotImplemented();
15660}
15661
15662HRESULT Machine::unlockMedia()
15663{
15664 ReturnComNotImplemented();
15665}
15666
15667HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15668 ComPtr<IMediumAttachment> &aNewAttachment)
15669{
15670 NOREF(aAttachment);
15671 NOREF(aNewAttachment);
15672 ReturnComNotImplemented();
15673}
15674
15675HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15676 ULONG aCpuUser,
15677 ULONG aCpuKernel,
15678 ULONG aCpuIdle,
15679 ULONG aMemTotal,
15680 ULONG aMemFree,
15681 ULONG aMemBalloon,
15682 ULONG aMemShared,
15683 ULONG aMemCache,
15684 ULONG aPagedTotal,
15685 ULONG aMemAllocTotal,
15686 ULONG aMemFreeTotal,
15687 ULONG aMemBalloonTotal,
15688 ULONG aMemSharedTotal,
15689 ULONG aVmNetRx,
15690 ULONG aVmNetTx)
15691{
15692 NOREF(aValidStats);
15693 NOREF(aCpuUser);
15694 NOREF(aCpuKernel);
15695 NOREF(aCpuIdle);
15696 NOREF(aMemTotal);
15697 NOREF(aMemFree);
15698 NOREF(aMemBalloon);
15699 NOREF(aMemShared);
15700 NOREF(aMemCache);
15701 NOREF(aPagedTotal);
15702 NOREF(aMemAllocTotal);
15703 NOREF(aMemFreeTotal);
15704 NOREF(aMemBalloonTotal);
15705 NOREF(aMemSharedTotal);
15706 NOREF(aVmNetRx);
15707 NOREF(aVmNetTx);
15708 ReturnComNotImplemented();
15709}
15710
15711HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15712 com::Utf8Str &aResult)
15713{
15714 NOREF(aAuthParams);
15715 NOREF(aResult);
15716 ReturnComNotImplemented();
15717}
15718
15719com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15720{
15721 com::Utf8Str strControllerName = "Unknown";
15722 switch (aBusType)
15723 {
15724 case StorageBus_IDE:
15725 {
15726 strControllerName = "IDE";
15727 break;
15728 }
15729 case StorageBus_SATA:
15730 {
15731 strControllerName = "SATA";
15732 break;
15733 }
15734 case StorageBus_SCSI:
15735 {
15736 strControllerName = "SCSI";
15737 break;
15738 }
15739 case StorageBus_Floppy:
15740 {
15741 strControllerName = "Floppy";
15742 break;
15743 }
15744 case StorageBus_SAS:
15745 {
15746 strControllerName = "SAS";
15747 break;
15748 }
15749 case StorageBus_USB:
15750 {
15751 strControllerName = "USB";
15752 break;
15753 }
15754 default:
15755 break;
15756 }
15757 return strControllerName;
15758}
15759
15760HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15761{
15762 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15763
15764 AutoCaller autoCaller(this);
15765 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15766
15767 HRESULT rc = S_OK;
15768
15769 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15770 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15771 rc = getUSBDeviceFilters(usbDeviceFilters);
15772 if (FAILED(rc)) return rc;
15773
15774 NOREF(aFlags);
15775 com::Utf8Str osTypeId;
15776 ComObjPtr<GuestOSType> osType = NULL;
15777
15778 /* Get the guest os type as a string from the VB. */
15779 rc = getOSTypeId(osTypeId);
15780 if (FAILED(rc)) return rc;
15781
15782 /* Get the os type obj that coresponds, can be used to get
15783 * the defaults for this guest OS. */
15784 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15785 if (FAILED(rc)) return rc;
15786
15787 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15788
15789 /* Let the OS type select 64-bit ness. */
15790 mHWData->mLongMode = osType->i_is64Bit()
15791 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15792
15793 /* Let the OS type enable the X2APIC */
15794 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15795
15796 /* This one covers IOAPICEnabled. */
15797 mBIOSSettings->i_applyDefaults(osType);
15798
15799 /* Initialize default record settings. */
15800 mRecordingSettings->i_applyDefaults();
15801
15802 /* Initialize default BIOS settings here */
15803 /* Hardware virtualization must be ON by default */
15804 mHWData->mAPIC = true;
15805 mHWData->mHWVirtExEnabled = true;
15806
15807 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15808 if (FAILED(rc)) return rc;
15809
15810 rc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15811 if (FAILED(rc)) return rc;
15812
15813 /* Graphics stuff. */
15814 GraphicsControllerType_T graphicsController;
15815 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15816 if (FAILED(rc)) return rc;
15817
15818 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15819 if (FAILED(rc)) return rc;
15820
15821 ULONG vramSize;
15822 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15823 if (FAILED(rc)) return rc;
15824
15825 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15826 if (FAILED(rc)) return rc;
15827
15828 BOOL fAccelerate2DVideoEnabled;
15829 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15830 if (FAILED(rc)) return rc;
15831
15832 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15833 if (FAILED(rc)) return rc;
15834
15835 BOOL fAccelerate3DEnabled;
15836 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15837 if (FAILED(rc)) return rc;
15838
15839 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15840 if (FAILED(rc)) return rc;
15841
15842 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15843 if (FAILED(rc)) return rc;
15844
15845 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15846 if (FAILED(rc)) return rc;
15847
15848 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15849 if (FAILED(rc)) return rc;
15850
15851 BOOL mRTCUseUTC;
15852 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15853 if (FAILED(rc)) return rc;
15854
15855 setRTCUseUTC(mRTCUseUTC);
15856 if (FAILED(rc)) return rc;
15857
15858 /* the setter does more than just the assignment, so use it */
15859 ChipsetType_T enmChipsetType;
15860 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15861 if (FAILED(rc)) return rc;
15862
15863 rc = COMSETTER(ChipsetType)(enmChipsetType);
15864 if (FAILED(rc)) return rc;
15865
15866 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15867 if (FAILED(rc)) return rc;
15868
15869 /* Apply IOMMU defaults. */
15870 IommuType_T enmIommuType;
15871 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15872 if (FAILED(rc)) return rc;
15873
15874 rc = COMSETTER(IommuType)(enmIommuType);
15875 if (FAILED(rc)) return rc;
15876
15877 /* Apply network adapters defaults */
15878 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15879 mNetworkAdapters[slot]->i_applyDefaults(osType);
15880
15881 /* Apply serial port defaults */
15882 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15883 mSerialPorts[slot]->i_applyDefaults(osType);
15884
15885 /* Apply parallel port defaults - not OS dependent*/
15886 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15887 mParallelPorts[slot]->i_applyDefaults();
15888
15889 /* This one covers the TPM type. */
15890 mTrustedPlatformModule->i_applyDefaults(osType);
15891
15892 /* This one covers secure boot. */
15893 rc = mNvramStore->i_applyDefaults(osType);
15894 if (FAILED(rc)) return rc;
15895
15896 /* Audio stuff. */
15897 rc = mAudioSettings->i_applyDefaults(osType);
15898 if (FAILED(rc)) return rc;
15899
15900 /* Storage Controllers */
15901 StorageControllerType_T hdStorageControllerType;
15902 StorageBus_T hdStorageBusType;
15903 StorageControllerType_T dvdStorageControllerType;
15904 StorageBus_T dvdStorageBusType;
15905 BOOL recommendedFloppy;
15906 ComPtr<IStorageController> floppyController;
15907 ComPtr<IStorageController> hdController;
15908 ComPtr<IStorageController> dvdController;
15909 Utf8Str strFloppyName, strDVDName, strHDName;
15910
15911 /* GUI auto generates controller names using bus type. Do the same*/
15912 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15913
15914 /* Floppy recommended? add one. */
15915 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15916 if (FAILED(rc)) return rc;
15917 if (recommendedFloppy)
15918 {
15919 rc = addStorageController(strFloppyName,
15920 StorageBus_Floppy,
15921 floppyController);
15922 if (FAILED(rc)) return rc;
15923 }
15924
15925 /* Setup one DVD storage controller. */
15926 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15927 if (FAILED(rc)) return rc;
15928
15929 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15930 if (FAILED(rc)) return rc;
15931
15932 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15933
15934 rc = addStorageController(strDVDName,
15935 dvdStorageBusType,
15936 dvdController);
15937 if (FAILED(rc)) return rc;
15938
15939 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15940 if (FAILED(rc)) return rc;
15941
15942 /* Setup one HDD storage controller. */
15943 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15944 if (FAILED(rc)) return rc;
15945
15946 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15947 if (FAILED(rc)) return rc;
15948
15949 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15950
15951 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15952 {
15953 rc = addStorageController(strHDName,
15954 hdStorageBusType,
15955 hdController);
15956 if (FAILED(rc)) return rc;
15957
15958 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15959 if (FAILED(rc)) return rc;
15960 }
15961 else
15962 {
15963 /* The HD controller is the same as DVD: */
15964 hdController = dvdController;
15965 }
15966
15967 /* Limit the AHCI port count if it's used because windows has trouble with
15968 * too many ports and other guest (OS X in particular) may take extra long
15969 * boot: */
15970
15971 // pParent = static_cast<Medium*>(aP)
15972 IStorageController *temp = hdController;
15973 ComObjPtr<StorageController> storageController;
15974 storageController = static_cast<StorageController *>(temp);
15975
15976 // tempHDController = aHDController;
15977 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15978 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15979 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15980 storageController->COMSETTER(PortCount)(1);
15981
15982 /* USB stuff */
15983
15984 bool ohciEnabled = false;
15985
15986 ComPtr<IUSBController> usbController;
15987 BOOL recommendedUSB3;
15988 BOOL recommendedUSB;
15989 BOOL usbProxyAvailable;
15990
15991 getUSBProxyAvailable(&usbProxyAvailable);
15992 if (FAILED(rc)) return rc;
15993
15994 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15995 if (FAILED(rc)) return rc;
15996 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15997 if (FAILED(rc)) return rc;
15998
15999 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
16000 {
16001#ifdef VBOX_WITH_EXTPACK
16002 /* USB 3.0 is only available if the proper ExtPack is installed. */
16003 ExtPackManager *aManager = mParent->i_getExtPackManager();
16004 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
16005 {
16006 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
16007 if (FAILED(rc)) return rc;
16008
16009 /* xHci includes OHCI */
16010 ohciEnabled = true;
16011 }
16012#endif
16013 }
16014 if ( !ohciEnabled
16015 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
16016 {
16017 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16018 if (FAILED(rc)) return rc;
16019 ohciEnabled = true;
16020
16021#ifdef VBOX_WITH_EXTPACK
16022 /* USB 2.0 is only available if the proper ExtPack is installed.
16023 * Note. Configuring EHCI here and providing messages about
16024 * the missing extpack isn't exactly clean, but it is a
16025 * necessary evil to patch over legacy compatability issues
16026 * introduced by the new distribution model. */
16027 ExtPackManager *manager = mParent->i_getExtPackManager();
16028 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
16029 {
16030 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
16031 if (FAILED(rc)) return rc;
16032 }
16033#endif
16034 }
16035
16036 /* Set recommended human interface device types: */
16037 BOOL recommendedUSBHID;
16038 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
16039 if (FAILED(rc)) return rc;
16040
16041 if (recommendedUSBHID)
16042 {
16043 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
16044 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
16045 if (!ohciEnabled && !usbDeviceFilters.isNull())
16046 {
16047 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16048 if (FAILED(rc)) return rc;
16049 }
16050 }
16051
16052 BOOL recommendedUSBTablet;
16053 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
16054 if (FAILED(rc)) return rc;
16055
16056 if (recommendedUSBTablet)
16057 {
16058 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
16059 if (!ohciEnabled && !usbDeviceFilters.isNull())
16060 {
16061 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16062 if (FAILED(rc)) return rc;
16063 }
16064 }
16065
16066 /* Enable the VMMDev testing feature for bootsector VMs: */
16067 if (osTypeId == "VBoxBS_64")
16068 {
16069 rc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
16070 if (FAILED(rc))
16071 return rc;
16072 }
16073
16074 return S_OK;
16075}
16076
16077#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
16078/**
16079 * Task record for change encryption settins.
16080 */
16081class Machine::ChangeEncryptionTask
16082 : public Machine::Task
16083{
16084public:
16085 ChangeEncryptionTask(Machine *m,
16086 Progress *p,
16087 const Utf8Str &t,
16088 const com::Utf8Str &aCurrentPassword,
16089 const com::Utf8Str &aCipher,
16090 const com::Utf8Str &aNewPassword,
16091 const com::Utf8Str &aNewPasswordId,
16092 const BOOL aForce,
16093 const MediaList &llMedia)
16094 : Task(m, p, t),
16095 mstrNewPassword(aNewPassword),
16096 mstrCurrentPassword(aCurrentPassword),
16097 mstrCipher(aCipher),
16098 mstrNewPasswordId(aNewPasswordId),
16099 mForce(aForce),
16100 mllMedia(llMedia)
16101 {}
16102
16103 ~ChangeEncryptionTask()
16104 {
16105 if (mstrNewPassword.length())
16106 RTMemWipeThoroughly(mstrNewPassword.mutableRaw(), mstrNewPassword.length(), 10 /* cPasses */);
16107 if (mstrCurrentPassword.length())
16108 RTMemWipeThoroughly(mstrCurrentPassword.mutableRaw(), mstrCurrentPassword.length(), 10 /* cPasses */);
16109 if (m_pCryptoIf)
16110 {
16111 m_pMachine->i_getVirtualBox()->i_releaseCryptoIf(m_pCryptoIf);
16112 m_pCryptoIf = NULL;
16113 }
16114 }
16115
16116 Utf8Str mstrNewPassword;
16117 Utf8Str mstrCurrentPassword;
16118 Utf8Str mstrCipher;
16119 Utf8Str mstrNewPasswordId;
16120 BOOL mForce;
16121 MediaList mllMedia;
16122 PCVBOXCRYPTOIF m_pCryptoIf;
16123private:
16124 void handler()
16125 {
16126 try
16127 {
16128 m_pMachine->i_changeEncryptionHandler(*this);
16129 }
16130 catch (...)
16131 {
16132 LogRel(("Some exception in the function Machine::i_changeEncryptionHandler()\n"));
16133 }
16134 }
16135
16136 friend void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task);
16137};
16138
16139/**
16140 * Scans specified directory and fills list by files found
16141 *
16142 * @returns VBox status code.
16143 * @param lstFiles
16144 * @param strDir
16145 * @param filePattern
16146 */
16147int Machine::i_findFiles(std::list<com::Utf8Str> &lstFiles, const com::Utf8Str &strDir,
16148 const com::Utf8Str &strPattern)
16149{
16150 /* To get all entries including subdirectories. */
16151 char *pszFilePattern = RTPathJoinA(strDir.c_str(), "*");
16152 if (!pszFilePattern)
16153 return VERR_NO_STR_MEMORY;
16154
16155 PRTDIRENTRYEX pDirEntry = NULL;
16156 RTDIR hDir;
16157 size_t cbDirEntry = sizeof(RTDIRENTRYEX);
16158 int rc = RTDirOpenFiltered(&hDir, pszFilePattern, RTDIRFILTER_WINNT, 0 /*fFlags*/);
16159 if (RT_SUCCESS(rc))
16160 {
16161 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
16162 if (pDirEntry)
16163 {
16164 while ( (rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK))
16165 != VERR_NO_MORE_FILES)
16166 {
16167 char *pszFilePath = NULL;
16168
16169 if (rc == VERR_BUFFER_OVERFLOW)
16170 {
16171 /* allocate new buffer. */
16172 RTMemFree(pDirEntry);
16173 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbDirEntry);
16174 if (!pDirEntry)
16175 {
16176 rc = VERR_NO_MEMORY;
16177 break;
16178 }
16179 /* Retry. */
16180 rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
16181 if (RT_FAILURE(rc))
16182 break;
16183 }
16184 else if (RT_FAILURE(rc))
16185 break;
16186
16187 /* Exclude . and .. */
16188 if ( (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '\0')
16189 || (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '.' && pDirEntry->szName[2] == '\0'))
16190 continue;
16191 if (RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode))
16192 {
16193 char *pszSubDirPath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
16194 if (!pszSubDirPath)
16195 {
16196 rc = VERR_NO_STR_MEMORY;
16197 break;
16198 }
16199 rc = i_findFiles(lstFiles, pszSubDirPath, strPattern);
16200 RTMemFree(pszSubDirPath);
16201 if (RT_FAILURE(rc))
16202 break;
16203 continue;
16204 }
16205
16206 /* We got the new entry. */
16207 if (!RTFS_IS_FILE(pDirEntry->Info.Attr.fMode))
16208 continue;
16209
16210 if (!RTStrSimplePatternMatch(strPattern.c_str(), pDirEntry->szName))
16211 continue;
16212
16213 /* Prepend the path to the libraries. */
16214 pszFilePath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
16215 if (!pszFilePath)
16216 {
16217 rc = VERR_NO_STR_MEMORY;
16218 break;
16219 }
16220
16221 lstFiles.push_back(pszFilePath);
16222 RTStrFree(pszFilePath);
16223 }
16224
16225 RTMemFree(pDirEntry);
16226 }
16227 else
16228 rc = VERR_NO_MEMORY;
16229
16230 RTDirClose(hDir);
16231 }
16232 else
16233 {
16234 /* On Windows the above immediately signals that there are no
16235 * files matching, while on other platforms enumerating the
16236 * files below fails. Either way: stop searching. */
16237 }
16238
16239 if ( rc == VERR_NO_MORE_FILES
16240 || rc == VERR_FILE_NOT_FOUND
16241 || rc == VERR_PATH_NOT_FOUND)
16242 rc = VINF_SUCCESS;
16243 RTStrFree(pszFilePattern);
16244 return rc;
16245}
16246
16247/**
16248 * Helper to set up an I/O stream to read or write a possibly encrypted file.
16249 *
16250 * @returns VBox status code.
16251 * @param pszFilename The file to open.
16252 * @param pCryptoIf Pointer to the cryptographic interface if the file should be encrypted or contains encrypted data.
16253 * @param pszKeyStore The keystore if the file should be encrypted or contains encrypted data.
16254 * @param pszPassword The password if the file should be encrypted or contains encrypted data.
16255 * @param fOpen The open flags for the file.
16256 * @param phVfsIos Where to store the handle to the I/O stream on success.
16257 */
16258int Machine::i_createIoStreamForFile(const char *pszFilename, PCVBOXCRYPTOIF pCryptoIf,
16259 const char *pszKeyStore, const char *pszPassword,
16260 uint64_t fOpen, PRTVFSIOSTREAM phVfsIos)
16261{
16262 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
16263 int vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
16264 if (RT_SUCCESS(vrc))
16265 {
16266 if (pCryptoIf)
16267 {
16268 RTVFSFILE hVfsFileCrypto = NIL_RTVFSFILE;
16269 vrc = pCryptoIf->pfnCryptoFileFromVfsFile(hVfsFile, pszKeyStore, pszPassword, &hVfsFileCrypto);
16270 if (RT_SUCCESS(vrc))
16271 {
16272 RTVfsFileRelease(hVfsFile);
16273 hVfsFile = hVfsFileCrypto;
16274 }
16275 }
16276
16277 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
16278 RTVfsFileRelease(hVfsFile);
16279 }
16280
16281 return vrc;
16282}
16283
16284/**
16285 * Helper function processing all actions for one component (saved state files,
16286 * NVRAM files, etc). Used by Machine::i_changeEncryptionHandler only.
16287 *
16288 * @param task
16289 * @param strDirectory
16290 * @param strFilePattern
16291 * @param strMagic
16292 * @param strKeyStore
16293 * @param strKeyId
16294 * @return
16295 */
16296HRESULT Machine::i_changeEncryptionForComponent(ChangeEncryptionTask &task, const com::Utf8Str strDirectory,
16297 const com::Utf8Str strFilePattern, com::Utf8Str &strKeyStore,
16298 com::Utf8Str &strKeyId, int iCipherMode)
16299{
16300 bool fDecrypt = task.mstrCurrentPassword.isNotEmpty()
16301 && task.mstrCipher.isEmpty()
16302 && task.mstrNewPassword.isEmpty()
16303 && task.mstrNewPasswordId.isEmpty();
16304 bool fEncrypt = task.mstrCurrentPassword.isEmpty()
16305 && task.mstrCipher.isNotEmpty()
16306 && task.mstrNewPassword.isNotEmpty()
16307 && task.mstrNewPasswordId.isNotEmpty();
16308
16309 /* check if the cipher is changed which causes the reencryption*/
16310
16311 const char *pszTaskCipher = NULL;
16312 if (task.mstrCipher.isNotEmpty())
16313 pszTaskCipher = getCipherString(task.mstrCipher.c_str(), iCipherMode);
16314
16315 if (!task.mForce && !fDecrypt && !fEncrypt)
16316 {
16317 char *pszCipher = NULL;
16318 int vrc = task.m_pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(strKeyStore.c_str(),
16319 NULL /*pszPassword*/,
16320 NULL /*ppbKey*/,
16321 NULL /*pcbKey*/,
16322 &pszCipher);
16323 if (RT_SUCCESS(vrc))
16324 {
16325 task.mForce = strcmp(pszTaskCipher, pszCipher) != 0;
16326 RTMemFree(pszCipher);
16327 }
16328 else
16329 return setErrorBoth(E_FAIL, vrc, tr("Obtain cipher for '%s' files failed (%Rrc)"),
16330 strFilePattern.c_str(), vrc);
16331 }
16332
16333 /* Only the password needs to be changed */
16334 if (!task.mForce && !fDecrypt && !fEncrypt)
16335 {
16336 Assert(task.m_pCryptoIf);
16337
16338 VBOXCRYPTOCTX hCryptoCtx;
16339 int vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(strKeyStore.c_str(), task.mstrCurrentPassword.c_str(), &hCryptoCtx);
16340 if (RT_FAILURE(vrc))
16341 return setErrorBoth(E_FAIL, vrc, tr("Loading old key store for '%s' files failed, (%Rrc)"),
16342 strFilePattern.c_str(), vrc);
16343 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
16344 if (RT_FAILURE(vrc))
16345 return setErrorBoth(E_FAIL, vrc, tr("Changing the password for '%s' files failed, (%Rrc)"),
16346 strFilePattern.c_str(), vrc);
16347
16348 char *pszKeyStore = NULL;
16349 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16350 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16351 if (RT_FAILURE(vrc))
16352 return setErrorBoth(E_FAIL, vrc, tr("Saving the key store for '%s' files failed, (%Rrc)"),
16353 strFilePattern.c_str(), vrc);
16354 strKeyStore = pszKeyStore;
16355 RTMemFree(pszKeyStore);
16356 strKeyId = task.mstrNewPasswordId;
16357 return S_OK;
16358 }
16359
16360 /* Reencryption required */
16361 HRESULT rc = S_OK;
16362 int vrc = VINF_SUCCESS;
16363
16364 std::list<com::Utf8Str> lstFiles;
16365 if (SUCCEEDED(rc))
16366 {
16367 vrc = i_findFiles(lstFiles, strDirectory, strFilePattern);
16368 if (RT_FAILURE(vrc))
16369 rc = setErrorBoth(E_FAIL, vrc, tr("Getting file list for '%s' files failed, (%Rrc)"),
16370 strFilePattern.c_str(), vrc);
16371 }
16372 com::Utf8Str strNewKeyStore;
16373 if (SUCCEEDED(rc))
16374 {
16375 if (!fDecrypt)
16376 {
16377 VBOXCRYPTOCTX hCryptoCtx;
16378 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(pszTaskCipher, task.mstrNewPassword.c_str(), &hCryptoCtx);
16379 if (RT_FAILURE(vrc))
16380 return setErrorBoth(E_FAIL, vrc, tr("Create new key store for '%s' files failed, (%Rrc)"),
16381 strFilePattern.c_str(), vrc);
16382
16383 char *pszKeyStore = NULL;
16384 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16385 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16386 if (RT_FAILURE(vrc))
16387 return setErrorBoth(E_FAIL, vrc, tr("Saving the new key store for '%s' files failed, (%Rrc)"),
16388 strFilePattern.c_str(), vrc);
16389 strNewKeyStore = pszKeyStore;
16390 RTMemFree(pszKeyStore);
16391 }
16392
16393 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
16394 it != lstFiles.end();
16395 ++it)
16396 {
16397 RTVFSIOSTREAM hVfsIosOld = NIL_RTVFSIOSTREAM;
16398 RTVFSIOSTREAM hVfsIosNew = NIL_RTVFSIOSTREAM;
16399
16400 uint64_t fOpenForRead = RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE;
16401 uint64_t fOpenForWrite = RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE;
16402
16403 vrc = i_createIoStreamForFile((*it).c_str(),
16404 fEncrypt ? NULL : task.m_pCryptoIf,
16405 fEncrypt ? NULL : strKeyStore.c_str(),
16406 fEncrypt ? NULL : task.mstrCurrentPassword.c_str(),
16407 fOpenForRead, &hVfsIosOld);
16408 if (RT_SUCCESS(vrc))
16409 {
16410 vrc = i_createIoStreamForFile((*it + ".tmp").c_str(),
16411 fDecrypt ? NULL : task.m_pCryptoIf,
16412 fDecrypt ? NULL : strNewKeyStore.c_str(),
16413 fDecrypt ? NULL : task.mstrNewPassword.c_str(),
16414 fOpenForWrite, &hVfsIosNew);
16415 if (RT_FAILURE(vrc))
16416 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
16417 (*it + ".tmp").c_str(), vrc);
16418 }
16419 else
16420 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
16421 (*it).c_str(), vrc);
16422
16423 if (RT_SUCCESS(vrc))
16424 {
16425 vrc = RTVfsUtilPumpIoStreams(hVfsIosOld, hVfsIosNew, BUF_DATA_SIZE);
16426 if (RT_FAILURE(vrc))
16427 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Changing encryption of the file '%s' failed with %Rrc"),
16428 (*it).c_str(), vrc);
16429 }
16430
16431 if (hVfsIosOld != NIL_RTVFSIOSTREAM)
16432 RTVfsIoStrmRelease(hVfsIosOld);
16433 if (hVfsIosNew != NIL_RTVFSIOSTREAM)
16434 RTVfsIoStrmRelease(hVfsIosNew);
16435 }
16436 }
16437
16438 if (SUCCEEDED(rc))
16439 {
16440 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
16441 it != lstFiles.end();
16442 ++it)
16443 {
16444 vrc = RTFileRename((*it + ".tmp").c_str(), (*it).c_str(), RTPATHRENAME_FLAGS_REPLACE);
16445 if (RT_FAILURE(vrc))
16446 {
16447 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming the file '%s' failed, (%Rrc)"),
16448 (*it + ".tmp").c_str(), vrc);
16449 break;
16450 }
16451 }
16452 }
16453
16454 if (SUCCEEDED(rc))
16455 {
16456 strKeyStore = strNewKeyStore;
16457 strKeyId = task.mstrNewPasswordId;
16458 }
16459
16460 return rc;
16461}
16462
16463/**
16464 * Task thread implementation for Machine::changeEncryption(), called from
16465 * Machine::taskHandler().
16466 *
16467 * @note Locks this object for writing.
16468 *
16469 * @param task
16470 * @return
16471 */
16472void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task)
16473{
16474 LogFlowThisFuncEnter();
16475
16476 AutoCaller autoCaller(this);
16477 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
16478 if (FAILED(autoCaller.rc()))
16479 {
16480 /* we might have been uninitialized because the session was accidentally
16481 * closed by the client, so don't assert */
16482 HRESULT rc = setError(E_FAIL,
16483 tr("The session has been accidentally closed"));
16484 task.m_pProgress->i_notifyComplete(rc);
16485 LogFlowThisFuncLeave();
16486 return;
16487 }
16488
16489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16490
16491 HRESULT rc = S_OK;
16492 com::Utf8Str strOldKeyId = mData->mstrKeyId;
16493 com::Utf8Str strOldKeyStore = mData->mstrKeyStore;
16494 try
16495 {
16496 rc = this->i_getVirtualBox()->i_retainCryptoIf(&task.m_pCryptoIf);
16497 if (FAILED(rc))
16498 throw rc;
16499
16500 if (task.mstrCurrentPassword.isEmpty())
16501 {
16502 if (mData->mstrKeyStore.isNotEmpty())
16503 throw setError(VBOX_E_PASSWORD_INCORRECT,
16504 tr("The password given for the encrypted VM is incorrect"));
16505 }
16506 else
16507 {
16508 if (mData->mstrKeyStore.isEmpty())
16509 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16510 tr("The VM is not configured for encryption"));
16511 rc = checkEncryptionPassword(task.mstrCurrentPassword);
16512 if (rc == VBOX_E_PASSWORD_INCORRECT)
16513 throw setError(VBOX_E_PASSWORD_INCORRECT,
16514 tr("The password to decrypt the VM is incorrect"));
16515 }
16516
16517 if (task.mstrCipher.isNotEmpty())
16518 {
16519 if ( task.mstrNewPassword.isEmpty()
16520 && task.mstrNewPasswordId.isEmpty()
16521 && task.mstrCurrentPassword.isNotEmpty())
16522 {
16523 /* An empty password and password ID will default to the current password. */
16524 task.mstrNewPassword = task.mstrCurrentPassword;
16525 }
16526 else if (task.mstrNewPassword.isEmpty())
16527 throw setError(VBOX_E_OBJECT_NOT_FOUND,
16528 tr("A password must be given for the VM encryption"));
16529 else if (task.mstrNewPasswordId.isEmpty())
16530 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16531 tr("A valid identifier for the password must be given"));
16532 }
16533 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
16534 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16535 tr("The password and password identifier must be empty if the output should be unencrypted"));
16536
16537 /*
16538 * Save config.
16539 * Must be first operation to prevent making encrypted copies
16540 * for old version of the config file.
16541 */
16542 int fSave = Machine::SaveS_Force;
16543 if (task.mstrNewPassword.isNotEmpty())
16544 {
16545 VBOXCRYPTOCTX hCryptoCtx;
16546
16547 int vrc = VINF_SUCCESS;
16548 if (task.mForce || task.mstrCurrentPassword.isEmpty() || task.mstrCipher.isNotEmpty())
16549 {
16550 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(getCipherString(task.mstrCipher.c_str(), CipherModeGcm),
16551 task.mstrNewPassword.c_str(), &hCryptoCtx);
16552 if (RT_FAILURE(vrc))
16553 throw setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
16554 }
16555 else
16556 {
16557 vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(mData->mstrKeyStore.c_str(),
16558 task.mstrCurrentPassword.c_str(),
16559 &hCryptoCtx);
16560 if (RT_FAILURE(vrc))
16561 throw setErrorBoth(E_FAIL, vrc, tr("Loading old key store failed, (%Rrc)"), vrc);
16562 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
16563 if (RT_FAILURE(vrc))
16564 throw setErrorBoth(E_FAIL, vrc, tr("Changing the password failed, (%Rrc)"), vrc);
16565 }
16566
16567 char *pszKeyStore;
16568 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16569 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16570 if (RT_FAILURE(vrc))
16571 throw setErrorBoth(E_FAIL, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
16572 mData->mstrKeyStore = pszKeyStore;
16573 RTStrFree(pszKeyStore);
16574 mData->mstrKeyId = task.mstrNewPasswordId;
16575 size_t cbPassword = task.mstrNewPassword.length() + 1;
16576 uint8_t *pbPassword = (uint8_t *)task.mstrNewPassword.c_str();
16577 mData->mpKeyStore->deleteSecretKey(task.mstrNewPasswordId);
16578 mData->mpKeyStore->addSecretKey(task.mstrNewPasswordId, pbPassword, cbPassword);
16579 mNvramStore->i_addPassword(task.mstrNewPasswordId, task.mstrNewPassword);
16580
16581 /*
16582 * Remove backuped config after saving because it can contain
16583 * unencrypted version of the config
16584 */
16585 fSave |= Machine::SaveS_RemoveBackup;
16586 }
16587 else
16588 {
16589 mData->mstrKeyId.setNull();
16590 mData->mstrKeyStore.setNull();
16591 }
16592
16593 Bstr bstrCurrentPassword(task.mstrCurrentPassword);
16594 Bstr bstrCipher(getCipherString(task.mstrCipher.c_str(), CipherModeXts));
16595 Bstr bstrNewPassword(task.mstrNewPassword);
16596 Bstr bstrNewPasswordId(task.mstrNewPasswordId);
16597 /* encrypt mediums */
16598 alock.release();
16599 for (MediaList::iterator it = task.mllMedia.begin();
16600 it != task.mllMedia.end();
16601 ++it)
16602 {
16603 ComPtr<IProgress> pProgress1;
16604 HRESULT hrc = (*it)->ChangeEncryption(bstrCurrentPassword.raw(), bstrCipher.raw(),
16605 bstrNewPassword.raw(), bstrNewPasswordId.raw(),
16606 pProgress1.asOutParam());
16607 if (FAILED(hrc)) throw hrc;
16608 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress1, 0 /* indefinite wait */);
16609 if (FAILED(hrc)) throw hrc;
16610 }
16611 alock.acquire();
16612
16613 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the SAV files")).raw(), 1);
16614
16615 Utf8Str strFullSnapshotFolder;
16616 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
16617
16618 /* .sav files (main and snapshots) */
16619 rc = i_changeEncryptionForComponent(task, strFullSnapshotFolder, "*.sav",
16620 mSSData->strStateKeyStore, mSSData->strStateKeyId, CipherModeGcm);
16621 if (FAILED(rc))
16622 /* the helper function already sets error object */
16623 throw rc;
16624
16625 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the NVRAM files")).raw(), 1);
16626
16627 /* .nvram files */
16628 com::Utf8Str strNVRAMKeyId;
16629 com::Utf8Str strNVRAMKeyStore;
16630 rc = mNvramStore->i_getEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16631 if (FAILED(rc))
16632 throw setError(rc, tr("Getting NVRAM encryption settings failed (%Rhrc)"), rc);
16633
16634 Utf8Str strMachineFolder;
16635 i_calculateFullPath(".", strMachineFolder);
16636
16637 rc = i_changeEncryptionForComponent(task, strMachineFolder, "*.nvram",
16638 strNVRAMKeyStore, strNVRAMKeyId, CipherModeGcm);
16639 if (FAILED(rc))
16640 /* the helper function already sets error object */
16641 throw rc;
16642
16643 rc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16644 if (FAILED(rc))
16645 throw setError(rc, tr("Setting NVRAM encryption settings failed (%Rhrc)"), rc);
16646
16647 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of log files")).raw(), 1);
16648
16649 /* .log files */
16650 com::Utf8Str strLogFolder;
16651 i_getLogFolder(strLogFolder);
16652 rc = i_changeEncryptionForComponent(task, strLogFolder, "VBox.log*",
16653 mData->mstrLogKeyStore, mData->mstrLogKeyId, CipherModeCtr);
16654 if (FAILED(rc))
16655 /* the helper function already sets error object */
16656 throw rc;
16657
16658 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the config file")).raw(), 1);
16659
16660 i_saveSettings(NULL, alock, fSave);
16661 }
16662 catch (HRESULT aRC)
16663 {
16664 rc = aRC;
16665 mData->mstrKeyId = strOldKeyId;
16666 mData->mstrKeyStore = strOldKeyStore;
16667 }
16668
16669 task.m_pProgress->i_notifyComplete(rc);
16670
16671 LogFlowThisFuncLeave();
16672}
16673#endif /*!VBOX_WITH_FULL_VM_ENCRYPTION*/
16674
16675HRESULT Machine::changeEncryption(const com::Utf8Str &aCurrentPassword,
16676 const com::Utf8Str &aCipher,
16677 const com::Utf8Str &aNewPassword,
16678 const com::Utf8Str &aNewPasswordId,
16679 BOOL aForce,
16680 ComPtr<IProgress> &aProgress)
16681{
16682 LogFlowFuncEnter();
16683
16684#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16685 RT_NOREF(aCurrentPassword, aCipher, aNewPassword, aNewPasswordId, aForce, aProgress);
16686 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16687#else
16688 /* make the VM accessible */
16689 if (!mData->mAccessible)
16690 {
16691 if ( aCurrentPassword.isEmpty()
16692 || mData->mstrKeyId.isEmpty())
16693 return setError(E_ACCESSDENIED, tr("Machine is inaccessible"));
16694
16695 HRESULT rc = addEncryptionPassword(mData->mstrKeyId, aCurrentPassword);
16696 if (FAILED(rc))
16697 return rc;
16698 }
16699
16700 AutoLimitedCaller autoCaller(this);
16701 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16702
16703 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16704
16705 /* define mediums to be change encryption */
16706
16707 MediaList llMedia;
16708 for (MediumAttachmentList::iterator
16709 it = mMediumAttachments->begin();
16710 it != mMediumAttachments->end();
16711 ++it)
16712 {
16713 ComObjPtr<MediumAttachment> &pAttach = *it;
16714 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
16715
16716 if (!pMedium.isNull())
16717 {
16718 AutoCaller mac(pMedium);
16719 if (FAILED(mac.rc())) return mac.rc();
16720 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
16721 DeviceType_T devType = pMedium->i_getDeviceType();
16722 if (devType == DeviceType_HardDisk)
16723 {
16724 /*
16725 * We need to move to last child because the Medium::changeEncryption
16726 * encrypts all chain of specified medium with its parents.
16727 * Also we perform cheking of back reference and children for
16728 * all media in the chain to raise error before we start any action.
16729 * So, we first move into root parent and then we will move to last child
16730 * keeping latter in the list for encryption.
16731 */
16732
16733 /* move to root parent */
16734 ComObjPtr<Medium> pTmpMedium = pMedium;
16735 while (pTmpMedium.isNotNull())
16736 {
16737 AutoCaller mediumAC(pTmpMedium);
16738 if (FAILED(mediumAC.rc())) return mac.rc();
16739 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16740
16741 /* Cannot encrypt media which are attached to more than one virtual machine. */
16742 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16743 if (cBackRefs > 1)
16744 return setError(VBOX_E_INVALID_OBJECT_STATE,
16745 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16746 pTmpMedium->i_getName().c_str(), cBackRefs);
16747
16748 size_t cChildren = pTmpMedium->i_getChildren().size();
16749 if (cChildren > 1)
16750 return setError(VBOX_E_INVALID_OBJECT_STATE,
16751 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16752 pTmpMedium->i_getName().c_str(), cChildren);
16753
16754 pTmpMedium = pTmpMedium->i_getParent();
16755 }
16756 /* move to last child */
16757 pTmpMedium = pMedium;
16758 while (pTmpMedium.isNotNull() && pTmpMedium->i_getChildren().size() != 0)
16759 {
16760 AutoCaller mediumAC(pTmpMedium);
16761 if (FAILED(mediumAC.rc())) return mac.rc();
16762 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16763
16764 /* Cannot encrypt media which are attached to more than one virtual machine. */
16765 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16766 if (cBackRefs > 1)
16767 return setError(VBOX_E_INVALID_OBJECT_STATE,
16768 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16769 pTmpMedium->i_getName().c_str(), cBackRefs);
16770
16771 size_t cChildren = pTmpMedium->i_getChildren().size();
16772 if (cChildren > 1)
16773 return setError(VBOX_E_INVALID_OBJECT_STATE,
16774 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16775 pTmpMedium->i_getName().c_str(), cChildren);
16776
16777 pTmpMedium = pTmpMedium->i_getChildren().front();
16778 }
16779 llMedia.push_back(pTmpMedium);
16780 }
16781 }
16782 }
16783
16784 ComObjPtr<Progress> pProgress;
16785 pProgress.createObject();
16786 HRESULT rc = pProgress->init(i_getVirtualBox(),
16787 static_cast<IMachine*>(this) /* aInitiator */,
16788 tr("Change encryption"),
16789 TRUE /* fCancellable */,
16790 (ULONG)(4 + + llMedia.size()), // cOperations
16791 tr("Change encryption of the mediuma"));
16792 if (FAILED(rc))
16793 return rc;
16794
16795 /* create and start the task on a separate thread (note that it will not
16796 * start working until we release alock) */
16797 ChangeEncryptionTask *pTask = new ChangeEncryptionTask(this, pProgress, "VM encryption",
16798 aCurrentPassword, aCipher, aNewPassword,
16799 aNewPasswordId, aForce, llMedia);
16800 rc = pTask->createThread();
16801 pTask = NULL;
16802 if (FAILED(rc))
16803 return rc;
16804
16805 pProgress.queryInterfaceTo(aProgress.asOutParam());
16806
16807 LogFlowFuncLeave();
16808
16809 return S_OK;
16810#endif
16811}
16812
16813HRESULT Machine::getEncryptionSettings(com::Utf8Str &aCipher,
16814 com::Utf8Str &aPasswordId)
16815{
16816#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16817 RT_NOREF(aCipher, aPasswordId);
16818 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16819#else
16820 AutoLimitedCaller autoCaller(this);
16821 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16822
16823 PCVBOXCRYPTOIF pCryptoIf = NULL;
16824 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16825 if (FAILED(hrc)) return hrc; /* Error is set */
16826
16827 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16828
16829 if (mData->mstrKeyStore.isNotEmpty())
16830 {
16831 char *pszCipher = NULL;
16832 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), NULL /*pszPassword*/,
16833 NULL /*ppbKey*/, NULL /*pcbKey*/, &pszCipher);
16834 if (RT_SUCCESS(vrc))
16835 {
16836 aCipher = getCipherStringWithoutMode(pszCipher);
16837 RTStrFree(pszCipher);
16838 aPasswordId = mData->mstrKeyId;
16839 }
16840 else
16841 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
16842 tr("Failed to query the encryption settings with %Rrc"),
16843 vrc);
16844 }
16845 else
16846 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16847
16848 mParent->i_releaseCryptoIf(pCryptoIf);
16849
16850 return hrc;
16851#endif
16852}
16853
16854HRESULT Machine::checkEncryptionPassword(const com::Utf8Str &aPassword)
16855{
16856#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16857 RT_NOREF(aPassword);
16858 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16859#else
16860 AutoLimitedCaller autoCaller(this);
16861 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16862
16863 PCVBOXCRYPTOIF pCryptoIf = NULL;
16864 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16865 if (FAILED(hrc)) return hrc; /* Error is set */
16866
16867 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16868
16869 if (mData->mstrKeyStore.isNotEmpty())
16870 {
16871 char *pszCipher = NULL;
16872 uint8_t *pbDek = NULL;
16873 size_t cbDek = 0;
16874 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), aPassword.c_str(),
16875 &pbDek, &cbDek, &pszCipher);
16876 if (RT_SUCCESS(vrc))
16877 {
16878 RTStrFree(pszCipher);
16879 RTMemSaferFree(pbDek, cbDek);
16880 }
16881 else
16882 hrc = setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc,
16883 tr("The password supplied for the encrypted machine is incorrect"));
16884 }
16885 else
16886 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16887
16888 mParent->i_releaseCryptoIf(pCryptoIf);
16889
16890 return hrc;
16891#endif
16892}
16893
16894HRESULT Machine::addEncryptionPassword(const com::Utf8Str &aId,
16895 const com::Utf8Str &aPassword)
16896{
16897#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16898 RT_NOREF(aId, aPassword);
16899 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16900#else
16901 AutoLimitedCaller autoCaller(this);
16902 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16903
16904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16905
16906 size_t cbPassword = aPassword.length() + 1;
16907 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
16908
16909 mData->mpKeyStore->addSecretKey(aId, pbPassword, cbPassword);
16910
16911 if ( mData->mAccessible
16912 && mData->mSession.mState == SessionState_Locked
16913 && mData->mSession.mLockType == LockType_VM
16914 && mData->mSession.mDirectControl != NULL)
16915 {
16916 /* get the console from the direct session */
16917 ComPtr<IConsole> console;
16918 HRESULT rc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16919 ComAssertComRC(rc);
16920 /* send passsword to console */
16921 console->AddEncryptionPassword(Bstr(aId).raw(),
16922 Bstr(aPassword).raw(),
16923 TRUE);
16924 }
16925
16926 if (mData->mstrKeyId == aId)
16927 {
16928 HRESULT hrc = checkEncryptionPassword(aPassword);
16929 if (FAILED(hrc))
16930 return hrc;
16931
16932 if (SUCCEEDED(hrc))
16933 {
16934 /*
16935 * Encryption is used and password is correct,
16936 * Reinit the machine if required.
16937 */
16938 BOOL fAccessible;
16939 alock.release();
16940 getAccessible(&fAccessible);
16941 alock.acquire();
16942 }
16943 }
16944
16945 /*
16946 * Add the password into the NvramStore only after
16947 * the machine becomes accessible and the NvramStore
16948 * contains key id and key store.
16949 */
16950 if (mNvramStore.isNotNull())
16951 mNvramStore->i_addPassword(aId, aPassword);
16952
16953 return S_OK;
16954#endif
16955}
16956
16957HRESULT Machine::addEncryptionPasswords(const std::vector<com::Utf8Str> &aIds,
16958 const std::vector<com::Utf8Str> &aPasswords)
16959{
16960#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16961 RT_NOREF(aIds, aPasswords);
16962 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16963#else
16964 if (aIds.size() != aPasswords.size())
16965 return setError(E_INVALIDARG, tr("Id and passwords arrays must have the same size"));
16966
16967 HRESULT hrc = S_OK;
16968 for (size_t i = 0; i < aIds.size() && SUCCEEDED(hrc); ++i)
16969 hrc = addEncryptionPassword(aIds[i], aPasswords[i]);
16970
16971 return hrc;
16972#endif
16973}
16974
16975HRESULT Machine::removeEncryptionPassword(AutoCaller &autoCaller, const com::Utf8Str &aId)
16976{
16977#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16978 RT_NOREF(autoCaller, aId);
16979 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16980#else
16981 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16982
16983 if ( mData->mAccessible
16984 && mData->mSession.mState == SessionState_Locked
16985 && mData->mSession.mLockType == LockType_VM
16986 && mData->mSession.mDirectControl != NULL)
16987 {
16988 /* get the console from the direct session */
16989 ComPtr<IConsole> console;
16990 HRESULT rc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16991 ComAssertComRC(rc);
16992 /* send passsword to console */
16993 console->RemoveEncryptionPassword(Bstr(aId).raw());
16994 }
16995
16996 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty() && mData->mstrKeyId == aId)
16997 {
16998 if (Global::IsOnlineOrTransient(mData->mMachineState))
16999 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
17000 alock.release();
17001 autoCaller.release();
17002 /* return because all passwords are purged when machine becomes inaccessible; */
17003 return i_setInaccessible();
17004 }
17005
17006 if (mNvramStore.isNotNull())
17007 mNvramStore->i_removePassword(aId);
17008 mData->mpKeyStore->deleteSecretKey(aId);
17009 return S_OK;
17010#endif
17011}
17012
17013HRESULT Machine::clearAllEncryptionPasswords(AutoCaller &autoCaller)
17014{
17015#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
17016 RT_NOREF(autoCaller);
17017 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
17018#else
17019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
17020
17021 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty())
17022 {
17023 if (Global::IsOnlineOrTransient(mData->mMachineState))
17024 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
17025 alock.release();
17026 autoCaller.release();
17027 /* return because all passwords are purged when machine becomes inaccessible; */
17028 return i_setInaccessible();
17029 }
17030
17031 mNvramStore->i_removeAllPasswords();
17032 mData->mpKeyStore->deleteAllSecretKeys(false, true);
17033 return S_OK;
17034#endif
17035}
17036
17037#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
17038HRESULT Machine::i_setInaccessible()
17039{
17040 if (!mData->mAccessible)
17041 return S_OK;
17042
17043 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
17044 VirtualBox *pParent = mParent;
17045 com::Utf8Str strConfigFile = mData->m_strConfigFile;
17046 Guid id(i_getId());
17047
17048 alock.release();
17049
17050 uninit();
17051 HRESULT rc = initFromSettings(pParent, strConfigFile, &id, com::Utf8Str());
17052
17053 alock.acquire();
17054 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
17055 return rc;
17056}
17057#endif
17058
17059/* This isn't handled entirely by the wrapper generator yet. */
17060#ifdef VBOX_WITH_XPCOM
17061NS_DECL_CLASSINFO(SessionMachine)
17062NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
17063
17064NS_DECL_CLASSINFO(SnapshotMachine)
17065NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
17066#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