VirtualBox

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

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

Main/MachineImpl: Implement the change encryption functionality for a VM (untested), bugref:9955

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 594.7 KB
Line 
1/* $Id: MachineImpl.cpp 94782 2022-05-02 12:18:59Z 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);
433
434 if (FAILED(hrc))
435 return hrc; /* Error is set. */
436 }
437 else
438 return hrc; /* Error is set. */
439 }
440#endif
441
442 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
443 if (FAILED(rc)) return rc;
444
445 if (SUCCEEDED(rc))
446 {
447 // create an empty machine config
448 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
449
450 rc = initDataAndChildObjects();
451 }
452
453 if (SUCCEEDED(rc))
454 {
455#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
456 mSSData->strStateKeyId = strSsmKeyId;
457 mSSData->strStateKeyStore = strSsmKeyStore;
458#endif
459
460 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
461 mData->mAccessible = TRUE;
462
463 unconst(mData->mUuid) = aId;
464
465 mUserData->s.strName = strName;
466
467 if (llGroups.size())
468 mUserData->s.llGroups = llGroups;
469
470 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
471 // the "name sync" flag determines whether the machine directory gets renamed along
472 // with the machine file; say so if the settings file name is the same as the
473 // settings file parent directory (machine directory)
474 mUserData->s.fNameSync = i_isInOwnDir();
475
476 // initialize the default snapshots folder
477 rc = COMSETTER(SnapshotFolder)(NULL);
478 AssertComRC(rc);
479
480 if (aOsType)
481 {
482 /* Store OS type */
483 mUserData->s.strOsType = aOsType->i_id();
484
485 /* Let the OS type select 64-bit ness. */
486 mHWData->mLongMode = aOsType->i_is64Bit()
487 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
488
489 /* Let the OS type enable the X2APIC */
490 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
491
492 rc = aOsType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
493 AssertComRC(rc);
494 }
495 else if (!strOsType.isEmpty())
496 {
497 /* Store OS type */
498 mUserData->s.strOsType = strOsType;
499
500 /* No guest OS type object. Pick some plausible defaults which the
501 * host can handle. There's no way to know or validate anything. */
502 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
503 mHWData->mX2APIC = false;
504 }
505
506 /* Apply BIOS defaults. */
507 mBIOSSettings->i_applyDefaults(aOsType);
508
509 /* Apply TPM defaults. */
510 mTrustedPlatformModule->i_applyDefaults(aOsType);
511
512 /* Apply record defaults. */
513 mRecordingSettings->i_applyDefaults();
514
515 /* Apply network adapters defaults */
516 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
517 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
518
519 /* Apply serial port defaults */
520 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
521 mSerialPorts[slot]->i_applyDefaults(aOsType);
522
523 /* Apply parallel port defaults */
524 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
525 mParallelPorts[slot]->i_applyDefaults();
526
527 /* Enable the VMMDev testing feature for bootsector VMs: */
528 if (aOsType && aOsType->i_id() == "VBoxBS_64")
529 mData->pMachineConfigFile->mapExtraDataItems["VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled"] = "1";
530
531#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
532 rc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
533#endif
534 if (SUCCEEDED(rc))
535 {
536 /* At this point the changing of the current state modification
537 * flag is allowed. */
538 i_allowStateModification();
539
540 /* commit all changes made during the initialization */
541 i_commit();
542 }
543 }
544
545 /* Confirm a successful initialization when it's the case */
546 if (SUCCEEDED(rc))
547 {
548#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
549 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
550 {
551 size_t cbPassword = aPassword.length() + 1;
552 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
553 mData->mpKeyStore->addSecretKey(aPasswordId, pbPassword, cbPassword);
554 }
555#endif
556
557 if (mData->mAccessible)
558 autoInitSpan.setSucceeded();
559 else
560 autoInitSpan.setLimited();
561 }
562
563 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
564 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
565 mData->mRegistered,
566 mData->mAccessible,
567 rc));
568
569 LogFlowThisFuncLeave();
570
571 return rc;
572}
573
574/**
575 * Initializes a new instance with data from machine XML (formerly Init_Registered).
576 * Gets called in two modes:
577 *
578 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
579 * UUID is specified and we mark the machine as "registered";
580 *
581 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
582 * and the machine remains unregistered until RegisterMachine() is called.
583 *
584 * @param aParent Associated parent object
585 * @param strConfigFile Local file system path to the VM settings file (can
586 * be relative to the VirtualBox config directory).
587 * @param aId UUID of the machine or NULL (see above).
588 * @param strPassword Password for decrypting the config
589 *
590 * @return Success indicator. if not S_OK, the machine object is invalid
591 */
592HRESULT Machine::initFromSettings(VirtualBox *aParent,
593 const Utf8Str &strConfigFile,
594 const Guid *aId,
595 const com::Utf8Str &strPassword)
596{
597 LogFlowThisFuncEnter();
598 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
599
600 PCVBOXCRYPTOIF pCryptoIf = NULL;
601#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
602 if (strPassword.isNotEmpty())
603 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
604#else
605 if (strPassword.isNotEmpty())
606 {
607 /* Get at the crpytographic interface. */
608 HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
609 if (FAILED(hrc))
610 return hrc; /* Error is set. */
611 }
612#endif
613
614 /* Enclose the state transition NotReady->InInit->Ready */
615 AutoInitSpan autoInitSpan(this);
616 AssertReturn(autoInitSpan.isOk(), E_FAIL);
617
618 HRESULT rc = initImpl(aParent, strConfigFile);
619 if (FAILED(rc)) return rc;
620
621 if (aId)
622 {
623 // loading a registered VM:
624 unconst(mData->mUuid) = *aId;
625 mData->mRegistered = TRUE;
626 // now load the settings from XML:
627 rc = i_registeredInit();
628 // this calls initDataAndChildObjects() and loadSettings()
629 }
630 else
631 {
632 // opening an unregistered VM (VirtualBox::OpenMachine()):
633 rc = initDataAndChildObjects();
634
635 if (SUCCEEDED(rc))
636 {
637 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
638 mData->mAccessible = TRUE;
639
640 try
641 {
642 // load and parse machine XML; this will throw on XML or logic errors
643 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
644 pCryptoIf,
645 strPassword.c_str());
646
647 // reject VM UUID duplicates, they can happen if someone
648 // tries to register an already known VM config again
649 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
650 true /* fPermitInaccessible */,
651 false /* aDoSetError */,
652 NULL) != VBOX_E_OBJECT_NOT_FOUND)
653 {
654 throw setError(E_FAIL,
655 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
656 mData->m_strConfigFile.c_str());
657 }
658
659 // use UUID from machine config
660 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
661
662#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
663 // No exception is thrown if config is encrypted, allowing us to get the uuid and the encryption fields.
664 // We fill in the encryptions fields, and the rest will be filled in if all data parsed.
665 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
666 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
667#endif
668
669 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
670 {
671 // We just set the inaccessible state and fill the error info allowing the caller
672 // to register the machine with encrypted config even if the password is incorrect
673 mData->mAccessible = FALSE;
674
675 /* fetch the current error info */
676 mData->mAccessError = com::ErrorInfo();
677
678 throw 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);
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, VERR_NO_MEMORY);
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 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
912 if (RT_FAILURE(vrc2))
913 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
914 tr("Could not delete the existing settings file '%s' (%Rrc)"),
915 mData->m_strConfigFileFull.c_str(), vrc2);
916 }
917 }
918 else if ( vrc != VERR_FILE_NOT_FOUND
919 && vrc != VERR_PATH_NOT_FOUND
920 )
921 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
922 tr("Invalid machine settings file name '%s' (%Rrc)"),
923 mData->m_strConfigFileFull.c_str(),
924 vrc);
925 return rc;
926}
927
928/**
929 * Initializes the registered machine by loading the settings file.
930 * This method is separated from #init() in order to make it possible to
931 * retry the operation after VirtualBox startup instead of refusing to
932 * startup the whole VirtualBox server in case if the settings file of some
933 * registered VM is invalid or inaccessible.
934 *
935 * @note Must be always called from this object's write lock
936 * (unless called from #init() that doesn't need any locking).
937 * @note Locks the mUSBController method for writing.
938 * @note Subclasses must not call this method.
939 */
940HRESULT Machine::i_registeredInit()
941{
942 AssertReturn(!i_isSessionMachine(), E_FAIL);
943 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
944 AssertReturn(mData->mUuid.isValid(), E_FAIL);
945 AssertReturn(!mData->mAccessible, E_FAIL);
946
947 HRESULT rc = initDataAndChildObjects();
948
949 if (SUCCEEDED(rc))
950 {
951 /* Temporarily reset the registered flag in order to let setters
952 * potentially called from loadSettings() succeed (isMutable() used in
953 * all setters will return FALSE for a Machine instance if mRegistered
954 * is TRUE). */
955 mData->mRegistered = FALSE;
956
957 PCVBOXCRYPTOIF pCryptoIf = NULL;
958 SecretKey *pKey = NULL;
959 const char *pszPassword = NULL;
960#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
961 /* Resolve password and cryptographic support interface if machine is encrypted. */
962 if (mData->mstrKeyId.isNotEmpty())
963 {
964 /* Get at the crpytographic interface. */
965 rc = mParent->i_retainCryptoIf(&pCryptoIf);
966 if (SUCCEEDED(rc))
967 {
968 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
969 if (RT_SUCCESS(vrc))
970 pszPassword = (const char *)pKey->getKeyBuffer();
971 else
972 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to retain key for key ID '%s' with %Rrc"),
973 mData->mstrKeyId.c_str(), vrc);
974 }
975 }
976#else
977 RT_NOREF(pKey);
978#endif
979
980 if (SUCCEEDED(rc))
981 {
982 try
983 {
984 // load and parse machine XML; this will throw on XML or logic errors
985 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
986 pCryptoIf, pszPassword);
987
988 if (mData->mUuid != mData->pMachineConfigFile->uuid)
989 throw setError(E_FAIL,
990 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
991 mData->pMachineConfigFile->uuid.raw(),
992 mData->m_strConfigFileFull.c_str(),
993 mData->mUuid.toString().c_str(),
994 mParent->i_settingsFilePath().c_str());
995
996#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
997 // If config is encrypted, no exception is thrown allowing us to get the uuid and the encryption fields.
998 // We fill in the encryptions fields, and the rest will be filled in if all data parsed
999 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
1000 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
1001
1002 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
1003 throw setError(VBOX_E_PASSWORD_INCORRECT,
1004 tr("Config decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
1005 mData->pMachineConfigFile->uuid.raw());
1006#endif
1007
1008 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
1009 NULL /* const Guid *puuidRegistry */);
1010 if (FAILED(rc)) throw rc;
1011 }
1012 catch (HRESULT err)
1013 {
1014 /* we assume that error info is set by the thrower */
1015 rc = err;
1016 }
1017 catch (...)
1018 {
1019 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
1020 }
1021
1022 /* Restore the registered flag (even on failure) */
1023 mData->mRegistered = TRUE;
1024 }
1025
1026#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1027 if (pCryptoIf)
1028 mParent->i_releaseCryptoIf(pCryptoIf);
1029 if (pKey)
1030 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
1031#endif
1032 }
1033
1034 if (SUCCEEDED(rc))
1035 {
1036 /* Set mAccessible to TRUE only if we successfully locked and loaded
1037 * the settings file */
1038 mData->mAccessible = TRUE;
1039
1040 /* commit all changes made during loading the settings file */
1041 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
1042 /// @todo r=klaus for some reason the settings loading logic backs up
1043 // the settings, and therefore a commit is needed. Should probably be changed.
1044 }
1045 else
1046 {
1047 /* If the machine is registered, then, instead of returning a
1048 * failure, we mark it as inaccessible and set the result to
1049 * success to give it a try later */
1050
1051 /* fetch the current error info */
1052 mData->mAccessError = com::ErrorInfo();
1053 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
1054
1055 /* rollback all changes */
1056 i_rollback(false /* aNotify */);
1057
1058 // uninit media from this machine's media registry, or else
1059 // reloading the settings will fail
1060 mParent->i_unregisterMachineMedia(i_getId());
1061
1062 /* uninitialize the common part to make sure all data is reset to
1063 * default (null) values */
1064 uninitDataAndChildObjects();
1065
1066 rc = S_OK;
1067 }
1068
1069 return rc;
1070}
1071
1072/**
1073 * Uninitializes the instance.
1074 * Called either from FinalRelease() or by the parent when it gets destroyed.
1075 *
1076 * @note The caller of this method must make sure that this object
1077 * a) doesn't have active callers on the current thread and b) is not locked
1078 * by the current thread; otherwise uninit() will hang either a) due to
1079 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
1080 * a dead-lock caused by this thread waiting for all callers on the other
1081 * threads are done but preventing them from doing so by holding a lock.
1082 */
1083void Machine::uninit()
1084{
1085 LogFlowThisFuncEnter();
1086
1087 Assert(!isWriteLockOnCurrentThread());
1088
1089 Assert(!uRegistryNeedsSaving);
1090 if (uRegistryNeedsSaving)
1091 {
1092 AutoCaller autoCaller(this);
1093 if (SUCCEEDED(autoCaller.rc()))
1094 {
1095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1096 i_saveSettings(NULL, alock, Machine::SaveS_Force);
1097 }
1098 }
1099
1100 /* Enclose the state transition Ready->InUninit->NotReady */
1101 AutoUninitSpan autoUninitSpan(this);
1102 if (autoUninitSpan.uninitDone())
1103 return;
1104
1105 Assert(!i_isSnapshotMachine());
1106 Assert(!i_isSessionMachine());
1107 Assert(!!mData);
1108
1109 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
1110 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
1111
1112 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1113
1114 if (!mData->mSession.mMachine.isNull())
1115 {
1116 /* Theoretically, this can only happen if the VirtualBox server has been
1117 * terminated while there were clients running that owned open direct
1118 * sessions. Since in this case we are definitely called by
1119 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
1120 * won't happen on the client watcher thread (because it has a
1121 * VirtualBox caller for the duration of the
1122 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
1123 * cannot happen until the VirtualBox caller is released). This is
1124 * important, because SessionMachine::uninit() cannot correctly operate
1125 * after we return from this method (it expects the Machine instance is
1126 * still valid). We'll call it ourselves below.
1127 */
1128 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
1129 (SessionMachine*)mData->mSession.mMachine));
1130
1131 if (Global::IsOnlineOrTransient(mData->mMachineState))
1132 {
1133 Log1WarningThisFunc(("Setting state to Aborted!\n"));
1134 /* set machine state using SessionMachine reimplementation */
1135 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
1136 }
1137
1138 /*
1139 * Uninitialize SessionMachine using public uninit() to indicate
1140 * an unexpected uninitialization.
1141 */
1142 mData->mSession.mMachine->uninit();
1143 /* SessionMachine::uninit() must set mSession.mMachine to null */
1144 Assert(mData->mSession.mMachine.isNull());
1145 }
1146
1147 // uninit media from this machine's media registry, if they're still there
1148 Guid uuidMachine(i_getId());
1149
1150 /* the lock is no more necessary (SessionMachine is uninitialized) */
1151 alock.release();
1152
1153 /* XXX This will fail with
1154 * "cannot be closed because it is still attached to 1 virtual machines"
1155 * because at this point we did not call uninitDataAndChildObjects() yet
1156 * and therefore also removeBackReference() for all these mediums was not called! */
1157
1158 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
1159 mParent->i_unregisterMachineMedia(uuidMachine);
1160
1161 // has machine been modified?
1162 if (mData->flModifications)
1163 {
1164 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
1165 i_rollback(false /* aNotify */);
1166 }
1167
1168 if (mData->mAccessible)
1169 uninitDataAndChildObjects();
1170
1171#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1172 if (mData->mpKeyStore != NULL)
1173 delete mData->mpKeyStore;
1174#endif
1175
1176 /* free the essential data structure last */
1177 mData.free();
1178
1179 LogFlowThisFuncLeave();
1180}
1181
1182// Wrapped IMachine properties
1183/////////////////////////////////////////////////////////////////////////////
1184HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
1185{
1186 /* mParent is constant during life time, no need to lock */
1187 ComObjPtr<VirtualBox> pVirtualBox(mParent);
1188 aParent = pVirtualBox;
1189
1190 return S_OK;
1191}
1192
1193
1194HRESULT Machine::getAccessible(BOOL *aAccessible)
1195{
1196 /* In some cases (medium registry related), it is necessary to be able to
1197 * go through the list of all machines. Happens when an inaccessible VM
1198 * has a sensible medium registry. */
1199 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
1200 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1201
1202 HRESULT rc = S_OK;
1203
1204 if (!mData->mAccessible)
1205 {
1206 /* try to initialize the VM once more if not accessible */
1207
1208 AutoReinitSpan autoReinitSpan(this);
1209 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
1210
1211#ifdef DEBUG
1212 LogFlowThisFunc(("Dumping media backreferences\n"));
1213 mParent->i_dumpAllBackRefs();
1214#endif
1215
1216 if (mData->pMachineConfigFile)
1217 {
1218 // reset the XML file to force loadSettings() (called from i_registeredInit())
1219 // to parse it again; the file might have changed
1220 delete mData->pMachineConfigFile;
1221 mData->pMachineConfigFile = NULL;
1222 }
1223
1224 rc = i_registeredInit();
1225
1226 if (SUCCEEDED(rc) && mData->mAccessible)
1227 {
1228 autoReinitSpan.setSucceeded();
1229
1230 /* make sure interesting parties will notice the accessibility
1231 * state change */
1232 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
1233 mParent->i_onMachineDataChanged(mData->mUuid);
1234 }
1235 }
1236
1237 if (SUCCEEDED(rc))
1238 *aAccessible = mData->mAccessible;
1239
1240 LogFlowThisFuncLeave();
1241
1242 return rc;
1243}
1244
1245HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
1246{
1247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1248
1249 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
1250 {
1251 /* return shortly */
1252 aAccessError = NULL;
1253 return S_OK;
1254 }
1255
1256 HRESULT rc = S_OK;
1257
1258 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1259 rc = errorInfo.createObject();
1260 if (SUCCEEDED(rc))
1261 {
1262 errorInfo->init(mData->mAccessError.getResultCode(),
1263 mData->mAccessError.getInterfaceID().ref(),
1264 Utf8Str(mData->mAccessError.getComponent()).c_str(),
1265 Utf8Str(mData->mAccessError.getText()));
1266 aAccessError = errorInfo;
1267 }
1268
1269 return rc;
1270}
1271
1272HRESULT Machine::getName(com::Utf8Str &aName)
1273{
1274 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1275
1276 aName = mUserData->s.strName;
1277
1278 return S_OK;
1279}
1280
1281HRESULT Machine::setName(const com::Utf8Str &aName)
1282{
1283 // prohibit setting a UUID only as the machine name, or else it can
1284 // never be found by findMachine()
1285 Guid test(aName);
1286
1287 if (test.isValid())
1288 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1289
1290 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1291
1292 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1293 if (FAILED(rc)) return rc;
1294
1295 i_setModified(IsModified_MachineData);
1296 mUserData.backup();
1297 mUserData->s.strName = aName;
1298
1299 return S_OK;
1300}
1301
1302HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1303{
1304 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1305
1306 aDescription = mUserData->s.strDescription;
1307
1308 return S_OK;
1309}
1310
1311HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1312{
1313 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1314
1315 // this can be done in principle in any state as it doesn't affect the VM
1316 // significantly, but play safe by not messing around while complex
1317 // activities are going on
1318 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1319 if (FAILED(rc)) return rc;
1320
1321 i_setModified(IsModified_MachineData);
1322 mUserData.backup();
1323 mUserData->s.strDescription = aDescription;
1324
1325 return S_OK;
1326}
1327
1328HRESULT Machine::getId(com::Guid &aId)
1329{
1330 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1331
1332 aId = mData->mUuid;
1333
1334 return S_OK;
1335}
1336
1337HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1338{
1339 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1340 aGroups.resize(mUserData->s.llGroups.size());
1341 size_t i = 0;
1342 for (StringsList::const_iterator
1343 it = mUserData->s.llGroups.begin();
1344 it != mUserData->s.llGroups.end();
1345 ++it, ++i)
1346 aGroups[i] = (*it);
1347
1348 return S_OK;
1349}
1350
1351HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1352{
1353 StringsList llGroups;
1354 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1355 if (FAILED(rc))
1356 return rc;
1357
1358 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1359
1360 rc = i_checkStateDependency(MutableOrSavedStateDep);
1361 if (FAILED(rc)) return rc;
1362
1363 i_setModified(IsModified_MachineData);
1364 mUserData.backup();
1365 mUserData->s.llGroups = llGroups;
1366
1367 return S_OK;
1368}
1369
1370HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1371{
1372 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1373
1374 aOSTypeId = mUserData->s.strOsType;
1375
1376 return S_OK;
1377}
1378
1379HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1380{
1381 /* look up the object by Id to check it is valid */
1382 ComObjPtr<GuestOSType> pGuestOSType;
1383 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1384
1385 /* when setting, always use the "etalon" value for consistency -- lookup
1386 * by ID is case-insensitive and the input value may have different case */
1387 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1388
1389 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1390
1391 HRESULT rc = i_checkStateDependency(MutableStateDep);
1392 if (FAILED(rc)) return rc;
1393
1394 i_setModified(IsModified_MachineData);
1395 mUserData.backup();
1396 mUserData->s.strOsType = osTypeId;
1397
1398 return S_OK;
1399}
1400
1401HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1402{
1403 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1404
1405 *aFirmwareType = mHWData->mFirmwareType;
1406
1407 return S_OK;
1408}
1409
1410HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1411{
1412 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1413
1414 HRESULT rc = i_checkStateDependency(MutableStateDep);
1415 if (FAILED(rc)) return rc;
1416
1417 i_setModified(IsModified_MachineData);
1418 mHWData.backup();
1419 mHWData->mFirmwareType = aFirmwareType;
1420 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1421 alock.release();
1422
1423 mNvramStore->i_updateNonVolatileStorageFile(strNVRAM);
1424
1425 return S_OK;
1426}
1427
1428HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1429{
1430 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1431
1432 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1433
1434 return S_OK;
1435}
1436
1437HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1438{
1439 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1440
1441 HRESULT rc = i_checkStateDependency(MutableStateDep);
1442 if (FAILED(rc)) return rc;
1443
1444 i_setModified(IsModified_MachineData);
1445 mHWData.backup();
1446 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1447
1448 return S_OK;
1449}
1450
1451HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1452{
1453 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1454
1455 *aPointingHIDType = mHWData->mPointingHIDType;
1456
1457 return S_OK;
1458}
1459
1460HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1461{
1462 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1463
1464 HRESULT rc = i_checkStateDependency(MutableStateDep);
1465 if (FAILED(rc)) return rc;
1466
1467 i_setModified(IsModified_MachineData);
1468 mHWData.backup();
1469 mHWData->mPointingHIDType = aPointingHIDType;
1470
1471 return S_OK;
1472}
1473
1474HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1475{
1476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1477
1478 *aChipsetType = mHWData->mChipsetType;
1479
1480 return S_OK;
1481}
1482
1483HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1484{
1485 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1486
1487 HRESULT rc = i_checkStateDependency(MutableStateDep);
1488 if (FAILED(rc)) return rc;
1489
1490 if (aChipsetType != mHWData->mChipsetType)
1491 {
1492 i_setModified(IsModified_MachineData);
1493 mHWData.backup();
1494 mHWData->mChipsetType = aChipsetType;
1495
1496 // Resize network adapter array, to be finalized on commit/rollback.
1497 // We must not throw away entries yet, otherwise settings are lost
1498 // without a way to roll back.
1499 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1500 size_t oldCount = mNetworkAdapters.size();
1501 if (newCount > oldCount)
1502 {
1503 mNetworkAdapters.resize(newCount);
1504 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1505 {
1506 unconst(mNetworkAdapters[slot]).createObject();
1507 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1508 }
1509 }
1510 }
1511
1512 return S_OK;
1513}
1514
1515HRESULT Machine::getIommuType(IommuType_T *aIommuType)
1516{
1517 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1518
1519 *aIommuType = mHWData->mIommuType;
1520
1521 return S_OK;
1522}
1523
1524HRESULT Machine::setIommuType(IommuType_T aIommuType)
1525{
1526 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1527
1528 HRESULT rc = i_checkStateDependency(MutableStateDep);
1529 if (FAILED(rc)) return rc;
1530
1531 if (aIommuType != mHWData->mIommuType)
1532 {
1533 if (aIommuType == IommuType_Intel)
1534 {
1535#ifndef VBOX_WITH_IOMMU_INTEL
1536 LogRelFunc(("Setting Intel IOMMU when Intel IOMMU support not available!\n"));
1537 return E_UNEXPECTED;
1538#endif
1539 }
1540
1541 i_setModified(IsModified_MachineData);
1542 mHWData.backup();
1543 mHWData->mIommuType = aIommuType;
1544 }
1545
1546 return S_OK;
1547}
1548
1549HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1550{
1551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1552
1553 aParavirtDebug = mHWData->mParavirtDebug;
1554 return S_OK;
1555}
1556
1557HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1558{
1559 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1560
1561 HRESULT rc = i_checkStateDependency(MutableStateDep);
1562 if (FAILED(rc)) return rc;
1563
1564 /** @todo Parse/validate options? */
1565 if (aParavirtDebug != mHWData->mParavirtDebug)
1566 {
1567 i_setModified(IsModified_MachineData);
1568 mHWData.backup();
1569 mHWData->mParavirtDebug = aParavirtDebug;
1570 }
1571
1572 return S_OK;
1573}
1574
1575HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1576{
1577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1578
1579 *aParavirtProvider = mHWData->mParavirtProvider;
1580
1581 return S_OK;
1582}
1583
1584HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1585{
1586 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1587
1588 HRESULT rc = i_checkStateDependency(MutableStateDep);
1589 if (FAILED(rc)) return rc;
1590
1591 if (aParavirtProvider != mHWData->mParavirtProvider)
1592 {
1593 i_setModified(IsModified_MachineData);
1594 mHWData.backup();
1595 mHWData->mParavirtProvider = aParavirtProvider;
1596 }
1597
1598 return S_OK;
1599}
1600
1601HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1602{
1603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1604
1605 *aParavirtProvider = mHWData->mParavirtProvider;
1606 switch (mHWData->mParavirtProvider)
1607 {
1608 case ParavirtProvider_None:
1609 case ParavirtProvider_HyperV:
1610 case ParavirtProvider_KVM:
1611 case ParavirtProvider_Minimal:
1612 break;
1613
1614 /* Resolve dynamic provider types to the effective types. */
1615 default:
1616 {
1617 ComObjPtr<GuestOSType> pGuestOSType;
1618 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1619 pGuestOSType);
1620 if (FAILED(hrc2) || pGuestOSType.isNull())
1621 {
1622 *aParavirtProvider = ParavirtProvider_None;
1623 break;
1624 }
1625
1626 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1627 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1628
1629 switch (mHWData->mParavirtProvider)
1630 {
1631 case ParavirtProvider_Legacy:
1632 {
1633 if (fOsXGuest)
1634 *aParavirtProvider = ParavirtProvider_Minimal;
1635 else
1636 *aParavirtProvider = ParavirtProvider_None;
1637 break;
1638 }
1639
1640 case ParavirtProvider_Default:
1641 {
1642 if (fOsXGuest)
1643 *aParavirtProvider = ParavirtProvider_Minimal;
1644 else if ( mUserData->s.strOsType == "Windows11_64"
1645 || mUserData->s.strOsType == "Windows10"
1646 || mUserData->s.strOsType == "Windows10_64"
1647 || mUserData->s.strOsType == "Windows81"
1648 || mUserData->s.strOsType == "Windows81_64"
1649 || mUserData->s.strOsType == "Windows8"
1650 || mUserData->s.strOsType == "Windows8_64"
1651 || mUserData->s.strOsType == "Windows7"
1652 || mUserData->s.strOsType == "Windows7_64"
1653 || mUserData->s.strOsType == "WindowsVista"
1654 || mUserData->s.strOsType == "WindowsVista_64"
1655 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1656 || mUserData->s.strOsType.startsWith("Windows201"))
1657 && mUserData->s.strOsType.endsWith("_64"))
1658 || mUserData->s.strOsType == "Windows2012"
1659 || mUserData->s.strOsType == "Windows2012_64"
1660 || mUserData->s.strOsType == "Windows2008"
1661 || mUserData->s.strOsType == "Windows2008_64")
1662 {
1663 *aParavirtProvider = ParavirtProvider_HyperV;
1664 }
1665 else if (guestTypeFamilyId == "Linux" &&
1666 mUserData->s.strOsType != "Linux22" && // Linux22 and Linux24{_64} excluded as they're too old
1667 mUserData->s.strOsType != "Linux24" && // to have any KVM paravirtualization support.
1668 mUserData->s.strOsType != "Linux24_64")
1669 {
1670 *aParavirtProvider = ParavirtProvider_KVM;
1671 }
1672 else
1673 *aParavirtProvider = ParavirtProvider_None;
1674 break;
1675 }
1676
1677 default: AssertFailedBreak(); /* Shut up MSC. */
1678 }
1679 break;
1680 }
1681 }
1682
1683 Assert( *aParavirtProvider == ParavirtProvider_None
1684 || *aParavirtProvider == ParavirtProvider_Minimal
1685 || *aParavirtProvider == ParavirtProvider_HyperV
1686 || *aParavirtProvider == ParavirtProvider_KVM);
1687 return S_OK;
1688}
1689
1690HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1691{
1692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1693
1694 aHardwareVersion = mHWData->mHWVersion;
1695
1696 return S_OK;
1697}
1698
1699HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1700{
1701 /* check known version */
1702 Utf8Str hwVersion = aHardwareVersion;
1703 if ( hwVersion.compare("1") != 0
1704 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1705 return setError(E_INVALIDARG,
1706 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1707
1708 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1709
1710 HRESULT rc = i_checkStateDependency(MutableStateDep);
1711 if (FAILED(rc)) return rc;
1712
1713 i_setModified(IsModified_MachineData);
1714 mHWData.backup();
1715 mHWData->mHWVersion = aHardwareVersion;
1716
1717 return S_OK;
1718}
1719
1720HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1721{
1722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1723
1724 if (!mHWData->mHardwareUUID.isZero())
1725 aHardwareUUID = mHWData->mHardwareUUID;
1726 else
1727 aHardwareUUID = mData->mUuid;
1728
1729 return S_OK;
1730}
1731
1732HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1733{
1734 if (!aHardwareUUID.isValid())
1735 return E_INVALIDARG;
1736
1737 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1738
1739 HRESULT rc = i_checkStateDependency(MutableStateDep);
1740 if (FAILED(rc)) return rc;
1741
1742 i_setModified(IsModified_MachineData);
1743 mHWData.backup();
1744 if (aHardwareUUID == mData->mUuid)
1745 mHWData->mHardwareUUID.clear();
1746 else
1747 mHWData->mHardwareUUID = aHardwareUUID;
1748
1749 return S_OK;
1750}
1751
1752HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1753{
1754 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1755
1756 *aMemorySize = mHWData->mMemorySize;
1757
1758 return S_OK;
1759}
1760
1761HRESULT Machine::setMemorySize(ULONG aMemorySize)
1762{
1763 /* check RAM limits */
1764 if ( aMemorySize < MM_RAM_MIN_IN_MB
1765 || aMemorySize > MM_RAM_MAX_IN_MB
1766 )
1767 return setError(E_INVALIDARG,
1768 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1769 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1770
1771 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1772
1773 HRESULT rc = i_checkStateDependency(MutableStateDep);
1774 if (FAILED(rc)) return rc;
1775
1776 i_setModified(IsModified_MachineData);
1777 mHWData.backup();
1778 mHWData->mMemorySize = aMemorySize;
1779
1780 return S_OK;
1781}
1782
1783HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1784{
1785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1786
1787 *aCPUCount = mHWData->mCPUCount;
1788
1789 return S_OK;
1790}
1791
1792HRESULT Machine::setCPUCount(ULONG aCPUCount)
1793{
1794 /* check CPU limits */
1795 if ( aCPUCount < SchemaDefs::MinCPUCount
1796 || aCPUCount > SchemaDefs::MaxCPUCount
1797 )
1798 return setError(E_INVALIDARG,
1799 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1800 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1801
1802 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1803
1804 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1805 if (mHWData->mCPUHotPlugEnabled)
1806 {
1807 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1808 {
1809 if (mHWData->mCPUAttached[idx])
1810 return setError(E_INVALIDARG,
1811 tr("There is still a CPU attached to socket %lu."
1812 "Detach the CPU before removing the socket"),
1813 aCPUCount, idx+1);
1814 }
1815 }
1816
1817 HRESULT rc = i_checkStateDependency(MutableStateDep);
1818 if (FAILED(rc)) return rc;
1819
1820 i_setModified(IsModified_MachineData);
1821 mHWData.backup();
1822 mHWData->mCPUCount = aCPUCount;
1823
1824 return S_OK;
1825}
1826
1827HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1828{
1829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1830
1831 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1832
1833 return S_OK;
1834}
1835
1836HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1837{
1838 HRESULT rc = S_OK;
1839
1840 /* check throttle limits */
1841 if ( aCPUExecutionCap < 1
1842 || aCPUExecutionCap > 100
1843 )
1844 return setError(E_INVALIDARG,
1845 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1846 aCPUExecutionCap, 1, 100);
1847
1848 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1849
1850 rc = i_checkStateDependency(MutableOrRunningStateDep);
1851 if (FAILED(rc)) return rc;
1852
1853 alock.release();
1854 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1855 alock.acquire();
1856 if (FAILED(rc)) return rc;
1857
1858 i_setModified(IsModified_MachineData);
1859 mHWData.backup();
1860 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1861
1862 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1863 if (Global::IsOnline(mData->mMachineState))
1864 i_saveSettings(NULL, alock);
1865
1866 return S_OK;
1867}
1868
1869HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1870{
1871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1872
1873 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1874
1875 return S_OK;
1876}
1877
1878HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1879{
1880 HRESULT rc = S_OK;
1881
1882 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1883
1884 rc = i_checkStateDependency(MutableStateDep);
1885 if (FAILED(rc)) return rc;
1886
1887 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1888 {
1889 if (aCPUHotPlugEnabled)
1890 {
1891 i_setModified(IsModified_MachineData);
1892 mHWData.backup();
1893
1894 /* Add the amount of CPUs currently attached */
1895 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1896 mHWData->mCPUAttached[i] = true;
1897 }
1898 else
1899 {
1900 /*
1901 * We can disable hotplug only if the amount of maximum CPUs is equal
1902 * to the amount of attached CPUs
1903 */
1904 unsigned cCpusAttached = 0;
1905 unsigned iHighestId = 0;
1906
1907 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1908 {
1909 if (mHWData->mCPUAttached[i])
1910 {
1911 cCpusAttached++;
1912 iHighestId = i;
1913 }
1914 }
1915
1916 if ( (cCpusAttached != mHWData->mCPUCount)
1917 || (iHighestId >= mHWData->mCPUCount))
1918 return setError(E_INVALIDARG,
1919 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1920
1921 i_setModified(IsModified_MachineData);
1922 mHWData.backup();
1923 }
1924 }
1925
1926 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1927
1928 return rc;
1929}
1930
1931HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1932{
1933 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1934
1935 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1936
1937 return S_OK;
1938}
1939
1940HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1941{
1942 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1943
1944 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1945 if (SUCCEEDED(hrc))
1946 {
1947 i_setModified(IsModified_MachineData);
1948 mHWData.backup();
1949 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1950 }
1951 return hrc;
1952}
1953
1954HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1955{
1956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1957 aCPUProfile = mHWData->mCpuProfile;
1958 return S_OK;
1959}
1960
1961HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1962{
1963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1964 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1965 if (SUCCEEDED(hrc))
1966 {
1967 i_setModified(IsModified_MachineData);
1968 mHWData.backup();
1969 /* Empty equals 'host'. */
1970 if (aCPUProfile.isNotEmpty())
1971 mHWData->mCpuProfile = aCPUProfile;
1972 else
1973 mHWData->mCpuProfile = "host";
1974 }
1975 return hrc;
1976}
1977
1978HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1979{
1980#ifdef VBOX_WITH_USB_CARDREADER
1981 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1982
1983 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1984
1985 return S_OK;
1986#else
1987 NOREF(aEmulatedUSBCardReaderEnabled);
1988 return E_NOTIMPL;
1989#endif
1990}
1991
1992HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1993{
1994#ifdef VBOX_WITH_USB_CARDREADER
1995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1996
1997 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1998 if (FAILED(rc)) return rc;
1999
2000 i_setModified(IsModified_MachineData);
2001 mHWData.backup();
2002 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
2003
2004 return S_OK;
2005#else
2006 NOREF(aEmulatedUSBCardReaderEnabled);
2007 return E_NOTIMPL;
2008#endif
2009}
2010
2011HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
2012{
2013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2014
2015 *aHPETEnabled = mHWData->mHPETEnabled;
2016
2017 return S_OK;
2018}
2019
2020HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
2021{
2022 HRESULT rc = S_OK;
2023
2024 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2025
2026 rc = i_checkStateDependency(MutableStateDep);
2027 if (FAILED(rc)) return rc;
2028
2029 i_setModified(IsModified_MachineData);
2030 mHWData.backup();
2031
2032 mHWData->mHPETEnabled = aHPETEnabled;
2033
2034 return rc;
2035}
2036
2037/** @todo this method should not be public */
2038HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2039{
2040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2041
2042 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2043
2044 return S_OK;
2045}
2046
2047/**
2048 * Set the memory balloon size.
2049 *
2050 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2051 * we have to make sure that we never call IGuest from here.
2052 */
2053HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2054{
2055 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2056#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2057 /* check limits */
2058 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2059 return setError(E_INVALIDARG,
2060 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2061 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2062
2063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2064
2065 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
2066 if (FAILED(rc)) return rc;
2067
2068 i_setModified(IsModified_MachineData);
2069 mHWData.backup();
2070 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2071
2072 return S_OK;
2073#else
2074 NOREF(aMemoryBalloonSize);
2075 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2076#endif
2077}
2078
2079HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2080{
2081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2082
2083 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2084 return S_OK;
2085}
2086
2087HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2088{
2089#ifdef VBOX_WITH_PAGE_SHARING
2090 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2091
2092 HRESULT rc = i_checkStateDependency(MutableStateDep);
2093 if (FAILED(rc)) return rc;
2094
2095 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2096 i_setModified(IsModified_MachineData);
2097 mHWData.backup();
2098 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2099 return S_OK;
2100#else
2101 NOREF(aPageFusionEnabled);
2102 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2103#endif
2104}
2105
2106HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2107{
2108 /* mBIOSSettings is constant during life time, no need to lock */
2109 aBIOSSettings = mBIOSSettings;
2110
2111 return S_OK;
2112}
2113
2114HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
2115{
2116 /* mTrustedPlatformModule is constant during life time, no need to lock */
2117 aTrustedPlatformModule = mTrustedPlatformModule;
2118
2119 return S_OK;
2120}
2121
2122HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
2123{
2124 /* mNvramStore is constant during life time, no need to lock */
2125 aNvramStore = mNvramStore;
2126
2127 return S_OK;
2128}
2129
2130HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
2131{
2132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2133
2134 aRecordingSettings = mRecordingSettings;
2135
2136 return S_OK;
2137}
2138
2139HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
2140{
2141 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2142
2143 aGraphicsAdapter = mGraphicsAdapter;
2144
2145 return S_OK;
2146}
2147
2148HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2149{
2150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2151
2152 switch (aProperty)
2153 {
2154 case CPUPropertyType_PAE:
2155 *aValue = mHWData->mPAEEnabled;
2156 break;
2157
2158 case CPUPropertyType_LongMode:
2159 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2160 *aValue = TRUE;
2161 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2162 *aValue = FALSE;
2163#if HC_ARCH_BITS == 64
2164 else
2165 *aValue = TRUE;
2166#else
2167 else
2168 {
2169 *aValue = FALSE;
2170
2171 ComObjPtr<GuestOSType> pGuestOSType;
2172 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2173 pGuestOSType);
2174 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
2175 {
2176 if (pGuestOSType->i_is64Bit())
2177 {
2178 ComObjPtr<Host> pHost = mParent->i_host();
2179 alock.release();
2180
2181 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2182 if (FAILED(hrc2))
2183 *aValue = FALSE;
2184 }
2185 }
2186 }
2187#endif
2188 break;
2189
2190 case CPUPropertyType_TripleFaultReset:
2191 *aValue = mHWData->mTripleFaultReset;
2192 break;
2193
2194 case CPUPropertyType_APIC:
2195 *aValue = mHWData->mAPIC;
2196 break;
2197
2198 case CPUPropertyType_X2APIC:
2199 *aValue = mHWData->mX2APIC;
2200 break;
2201
2202 case CPUPropertyType_IBPBOnVMExit:
2203 *aValue = mHWData->mIBPBOnVMExit;
2204 break;
2205
2206 case CPUPropertyType_IBPBOnVMEntry:
2207 *aValue = mHWData->mIBPBOnVMEntry;
2208 break;
2209
2210 case CPUPropertyType_SpecCtrl:
2211 *aValue = mHWData->mSpecCtrl;
2212 break;
2213
2214 case CPUPropertyType_SpecCtrlByHost:
2215 *aValue = mHWData->mSpecCtrlByHost;
2216 break;
2217
2218 case CPUPropertyType_HWVirt:
2219 *aValue = mHWData->mNestedHWVirt;
2220 break;
2221
2222 case CPUPropertyType_L1DFlushOnEMTScheduling:
2223 *aValue = mHWData->mL1DFlushOnSched;
2224 break;
2225
2226 case CPUPropertyType_L1DFlushOnVMEntry:
2227 *aValue = mHWData->mL1DFlushOnVMEntry;
2228 break;
2229
2230 case CPUPropertyType_MDSClearOnEMTScheduling:
2231 *aValue = mHWData->mMDSClearOnSched;
2232 break;
2233
2234 case CPUPropertyType_MDSClearOnVMEntry:
2235 *aValue = mHWData->mMDSClearOnVMEntry;
2236 break;
2237
2238 default:
2239 return E_INVALIDARG;
2240 }
2241 return S_OK;
2242}
2243
2244HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2245{
2246 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2247
2248 HRESULT rc = i_checkStateDependency(MutableStateDep);
2249 if (FAILED(rc)) return rc;
2250
2251 switch (aProperty)
2252 {
2253 case CPUPropertyType_PAE:
2254 i_setModified(IsModified_MachineData);
2255 mHWData.backup();
2256 mHWData->mPAEEnabled = !!aValue;
2257 break;
2258
2259 case CPUPropertyType_LongMode:
2260 i_setModified(IsModified_MachineData);
2261 mHWData.backup();
2262 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2263 break;
2264
2265 case CPUPropertyType_TripleFaultReset:
2266 i_setModified(IsModified_MachineData);
2267 mHWData.backup();
2268 mHWData->mTripleFaultReset = !!aValue;
2269 break;
2270
2271 case CPUPropertyType_APIC:
2272 if (mHWData->mX2APIC)
2273 aValue = TRUE;
2274 i_setModified(IsModified_MachineData);
2275 mHWData.backup();
2276 mHWData->mAPIC = !!aValue;
2277 break;
2278
2279 case CPUPropertyType_X2APIC:
2280 i_setModified(IsModified_MachineData);
2281 mHWData.backup();
2282 mHWData->mX2APIC = !!aValue;
2283 if (aValue)
2284 mHWData->mAPIC = !!aValue;
2285 break;
2286
2287 case CPUPropertyType_IBPBOnVMExit:
2288 i_setModified(IsModified_MachineData);
2289 mHWData.backup();
2290 mHWData->mIBPBOnVMExit = !!aValue;
2291 break;
2292
2293 case CPUPropertyType_IBPBOnVMEntry:
2294 i_setModified(IsModified_MachineData);
2295 mHWData.backup();
2296 mHWData->mIBPBOnVMEntry = !!aValue;
2297 break;
2298
2299 case CPUPropertyType_SpecCtrl:
2300 i_setModified(IsModified_MachineData);
2301 mHWData.backup();
2302 mHWData->mSpecCtrl = !!aValue;
2303 break;
2304
2305 case CPUPropertyType_SpecCtrlByHost:
2306 i_setModified(IsModified_MachineData);
2307 mHWData.backup();
2308 mHWData->mSpecCtrlByHost = !!aValue;
2309 break;
2310
2311 case CPUPropertyType_HWVirt:
2312 i_setModified(IsModified_MachineData);
2313 mHWData.backup();
2314 mHWData->mNestedHWVirt = !!aValue;
2315 break;
2316
2317 case CPUPropertyType_L1DFlushOnEMTScheduling:
2318 i_setModified(IsModified_MachineData);
2319 mHWData.backup();
2320 mHWData->mL1DFlushOnSched = !!aValue;
2321 break;
2322
2323 case CPUPropertyType_L1DFlushOnVMEntry:
2324 i_setModified(IsModified_MachineData);
2325 mHWData.backup();
2326 mHWData->mL1DFlushOnVMEntry = !!aValue;
2327 break;
2328
2329 case CPUPropertyType_MDSClearOnEMTScheduling:
2330 i_setModified(IsModified_MachineData);
2331 mHWData.backup();
2332 mHWData->mMDSClearOnSched = !!aValue;
2333 break;
2334
2335 case CPUPropertyType_MDSClearOnVMEntry:
2336 i_setModified(IsModified_MachineData);
2337 mHWData.backup();
2338 mHWData->mMDSClearOnVMEntry = !!aValue;
2339 break;
2340
2341 default:
2342 return E_INVALIDARG;
2343 }
2344 return S_OK;
2345}
2346
2347HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2348 ULONG *aValEcx, ULONG *aValEdx)
2349{
2350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2351 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2352 {
2353 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2354 it != mHWData->mCpuIdLeafList.end();
2355 ++it)
2356 {
2357 if (aOrdinal == 0)
2358 {
2359 const settings::CpuIdLeaf &rLeaf= *it;
2360 *aIdx = rLeaf.idx;
2361 *aSubIdx = rLeaf.idxSub;
2362 *aValEax = rLeaf.uEax;
2363 *aValEbx = rLeaf.uEbx;
2364 *aValEcx = rLeaf.uEcx;
2365 *aValEdx = rLeaf.uEdx;
2366 return S_OK;
2367 }
2368 aOrdinal--;
2369 }
2370 }
2371 return E_INVALIDARG;
2372}
2373
2374HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2375{
2376 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2377
2378 /*
2379 * Search the list.
2380 */
2381 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2382 {
2383 const settings::CpuIdLeaf &rLeaf= *it;
2384 if ( rLeaf.idx == aIdx
2385 && ( aSubIdx == UINT32_MAX
2386 || rLeaf.idxSub == aSubIdx) )
2387 {
2388 *aValEax = rLeaf.uEax;
2389 *aValEbx = rLeaf.uEbx;
2390 *aValEcx = rLeaf.uEcx;
2391 *aValEdx = rLeaf.uEdx;
2392 return S_OK;
2393 }
2394 }
2395
2396 return E_INVALIDARG;
2397}
2398
2399
2400HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2401{
2402 /*
2403 * Validate input before taking locks and checking state.
2404 */
2405 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2406 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2407 if ( aIdx >= UINT32_C(0x20)
2408 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2409 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2410 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2411
2412 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2413 HRESULT rc = i_checkStateDependency(MutableStateDep);
2414 if (FAILED(rc)) return rc;
2415
2416 /*
2417 * Impose a maximum number of leaves.
2418 */
2419 if (mHWData->mCpuIdLeafList.size() > 256)
2420 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2421
2422 /*
2423 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2424 */
2425 i_setModified(IsModified_MachineData);
2426 mHWData.backup();
2427
2428 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2429 {
2430 settings::CpuIdLeaf &rLeaf= *it;
2431 if ( rLeaf.idx == aIdx
2432 && ( aSubIdx == UINT32_MAX
2433 || rLeaf.idxSub == aSubIdx) )
2434 it = mHWData->mCpuIdLeafList.erase(it);
2435 else
2436 ++it;
2437 }
2438
2439 settings::CpuIdLeaf NewLeaf;
2440 NewLeaf.idx = aIdx;
2441 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2442 NewLeaf.uEax = aValEax;
2443 NewLeaf.uEbx = aValEbx;
2444 NewLeaf.uEcx = aValEcx;
2445 NewLeaf.uEdx = aValEdx;
2446 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2447 return S_OK;
2448}
2449
2450HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2451{
2452 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2453
2454 HRESULT rc = i_checkStateDependency(MutableStateDep);
2455 if (FAILED(rc)) return rc;
2456
2457 /*
2458 * Do the removal.
2459 */
2460 bool fModified = mHWData.isBackedUp();
2461 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2462 {
2463 settings::CpuIdLeaf &rLeaf= *it;
2464 if ( rLeaf.idx == aIdx
2465 && ( aSubIdx == UINT32_MAX
2466 || rLeaf.idxSub == aSubIdx) )
2467 {
2468 if (!fModified)
2469 {
2470 fModified = true;
2471 i_setModified(IsModified_MachineData);
2472 mHWData.backup();
2473 // Start from the beginning, since mHWData.backup() creates
2474 // a new list, causing iterator mixup. This makes sure that
2475 // the settings are not unnecessarily marked as modified,
2476 // at the price of extra list walking.
2477 it = mHWData->mCpuIdLeafList.begin();
2478 }
2479 else
2480 it = mHWData->mCpuIdLeafList.erase(it);
2481 }
2482 else
2483 ++it;
2484 }
2485
2486 return S_OK;
2487}
2488
2489HRESULT Machine::removeAllCPUIDLeaves()
2490{
2491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2492
2493 HRESULT rc = i_checkStateDependency(MutableStateDep);
2494 if (FAILED(rc)) return rc;
2495
2496 if (mHWData->mCpuIdLeafList.size() > 0)
2497 {
2498 i_setModified(IsModified_MachineData);
2499 mHWData.backup();
2500
2501 mHWData->mCpuIdLeafList.clear();
2502 }
2503
2504 return S_OK;
2505}
2506HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2507{
2508 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2509
2510 switch(aProperty)
2511 {
2512 case HWVirtExPropertyType_Enabled:
2513 *aValue = mHWData->mHWVirtExEnabled;
2514 break;
2515
2516 case HWVirtExPropertyType_VPID:
2517 *aValue = mHWData->mHWVirtExVPIDEnabled;
2518 break;
2519
2520 case HWVirtExPropertyType_NestedPaging:
2521 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2522 break;
2523
2524 case HWVirtExPropertyType_UnrestrictedExecution:
2525 *aValue = mHWData->mHWVirtExUXEnabled;
2526 break;
2527
2528 case HWVirtExPropertyType_LargePages:
2529 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2530 break;
2531
2532 case HWVirtExPropertyType_Force:
2533 *aValue = mHWData->mHWVirtExForceEnabled;
2534 break;
2535
2536 case HWVirtExPropertyType_UseNativeApi:
2537 *aValue = mHWData->mHWVirtExUseNativeApi;
2538 break;
2539
2540 case HWVirtExPropertyType_VirtVmsaveVmload:
2541 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2542 break;
2543
2544 default:
2545 return E_INVALIDARG;
2546 }
2547 return S_OK;
2548}
2549
2550HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2551{
2552 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2553
2554 HRESULT rc = i_checkStateDependency(MutableStateDep);
2555 if (FAILED(rc)) return rc;
2556
2557 switch (aProperty)
2558 {
2559 case HWVirtExPropertyType_Enabled:
2560 i_setModified(IsModified_MachineData);
2561 mHWData.backup();
2562 mHWData->mHWVirtExEnabled = !!aValue;
2563 break;
2564
2565 case HWVirtExPropertyType_VPID:
2566 i_setModified(IsModified_MachineData);
2567 mHWData.backup();
2568 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2569 break;
2570
2571 case HWVirtExPropertyType_NestedPaging:
2572 i_setModified(IsModified_MachineData);
2573 mHWData.backup();
2574 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2575 break;
2576
2577 case HWVirtExPropertyType_UnrestrictedExecution:
2578 i_setModified(IsModified_MachineData);
2579 mHWData.backup();
2580 mHWData->mHWVirtExUXEnabled = !!aValue;
2581 break;
2582
2583 case HWVirtExPropertyType_LargePages:
2584 i_setModified(IsModified_MachineData);
2585 mHWData.backup();
2586 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2587 break;
2588
2589 case HWVirtExPropertyType_Force:
2590 i_setModified(IsModified_MachineData);
2591 mHWData.backup();
2592 mHWData->mHWVirtExForceEnabled = !!aValue;
2593 break;
2594
2595 case HWVirtExPropertyType_UseNativeApi:
2596 i_setModified(IsModified_MachineData);
2597 mHWData.backup();
2598 mHWData->mHWVirtExUseNativeApi = !!aValue;
2599 break;
2600
2601 case HWVirtExPropertyType_VirtVmsaveVmload:
2602 i_setModified(IsModified_MachineData);
2603 mHWData.backup();
2604 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2605 break;
2606
2607 default:
2608 return E_INVALIDARG;
2609 }
2610
2611 return S_OK;
2612}
2613
2614HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2615{
2616 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2617
2618 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2619
2620 return S_OK;
2621}
2622
2623HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2624{
2625 /** @todo (r=dmik):
2626 * 1. Allow to change the name of the snapshot folder containing snapshots
2627 * 2. Rename the folder on disk instead of just changing the property
2628 * value (to be smart and not to leave garbage). Note that it cannot be
2629 * done here because the change may be rolled back. Thus, the right
2630 * place is #saveSettings().
2631 */
2632
2633 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2634
2635 HRESULT rc = i_checkStateDependency(MutableStateDep);
2636 if (FAILED(rc)) return rc;
2637
2638 if (!mData->mCurrentSnapshot.isNull())
2639 return setError(E_FAIL,
2640 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2641
2642 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2643
2644 if (strSnapshotFolder.isEmpty())
2645 strSnapshotFolder = "Snapshots";
2646 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2647 if (RT_FAILURE(vrc))
2648 return setErrorBoth(E_FAIL, vrc,
2649 tr("Invalid snapshot folder '%s' (%Rrc)"),
2650 strSnapshotFolder.c_str(), vrc);
2651
2652 i_setModified(IsModified_MachineData);
2653 mUserData.backup();
2654
2655 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2656
2657 return S_OK;
2658}
2659
2660HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2661{
2662 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2663
2664 aMediumAttachments.resize(mMediumAttachments->size());
2665 size_t i = 0;
2666 for (MediumAttachmentList::const_iterator
2667 it = mMediumAttachments->begin();
2668 it != mMediumAttachments->end();
2669 ++it, ++i)
2670 aMediumAttachments[i] = *it;
2671
2672 return S_OK;
2673}
2674
2675HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2676{
2677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2678
2679 Assert(!!mVRDEServer);
2680
2681 aVRDEServer = mVRDEServer;
2682
2683 return S_OK;
2684}
2685
2686HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2687{
2688 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2689
2690 aAudioAdapter = mAudioAdapter;
2691
2692 return S_OK;
2693}
2694
2695HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2696{
2697#ifdef VBOX_WITH_VUSB
2698 clearError();
2699 MultiResult rc(S_OK);
2700
2701# ifdef VBOX_WITH_USB
2702 rc = mParent->i_host()->i_checkUSBProxyService();
2703 if (FAILED(rc)) return rc;
2704# endif
2705
2706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2707
2708 aUSBControllers.resize(mUSBControllers->size());
2709 size_t i = 0;
2710 for (USBControllerList::const_iterator
2711 it = mUSBControllers->begin();
2712 it != mUSBControllers->end();
2713 ++it, ++i)
2714 aUSBControllers[i] = *it;
2715
2716 return S_OK;
2717#else
2718 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2719 * extended error info to indicate that USB is simply not available
2720 * (w/o treating it as a failure), for example, as in OSE */
2721 NOREF(aUSBControllers);
2722 ReturnComNotImplemented();
2723#endif /* VBOX_WITH_VUSB */
2724}
2725
2726HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2727{
2728#ifdef VBOX_WITH_VUSB
2729 clearError();
2730 MultiResult rc(S_OK);
2731
2732# ifdef VBOX_WITH_USB
2733 rc = mParent->i_host()->i_checkUSBProxyService();
2734 if (FAILED(rc)) return rc;
2735# endif
2736
2737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2738
2739 aUSBDeviceFilters = mUSBDeviceFilters;
2740 return rc;
2741#else
2742 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2743 * extended error info to indicate that USB is simply not available
2744 * (w/o treating it as a failure), for example, as in OSE */
2745 NOREF(aUSBDeviceFilters);
2746 ReturnComNotImplemented();
2747#endif /* VBOX_WITH_VUSB */
2748}
2749
2750HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2751{
2752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2753
2754 aSettingsFilePath = mData->m_strConfigFileFull;
2755
2756 return S_OK;
2757}
2758
2759HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2760{
2761 RT_NOREF(aSettingsFilePath);
2762 ReturnComNotImplemented();
2763}
2764
2765HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2766{
2767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2768
2769 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2770 if (FAILED(rc)) return rc;
2771
2772 if (!mData->pMachineConfigFile->fileExists())
2773 // this is a new machine, and no config file exists yet:
2774 *aSettingsModified = TRUE;
2775 else
2776 *aSettingsModified = (mData->flModifications != 0);
2777
2778 return S_OK;
2779}
2780
2781HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2782{
2783 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2784
2785 *aSessionState = mData->mSession.mState;
2786
2787 return S_OK;
2788}
2789
2790HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2791{
2792 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2793
2794 aSessionName = mData->mSession.mName;
2795
2796 return S_OK;
2797}
2798
2799HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2800{
2801 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2802
2803 *aSessionPID = mData->mSession.mPID;
2804
2805 return S_OK;
2806}
2807
2808HRESULT Machine::getState(MachineState_T *aState)
2809{
2810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2811
2812 *aState = mData->mMachineState;
2813 Assert(mData->mMachineState != MachineState_Null);
2814
2815 return S_OK;
2816}
2817
2818HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2819{
2820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2821
2822 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2823
2824 return S_OK;
2825}
2826
2827HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2828{
2829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2830
2831 aStateFilePath = mSSData->strStateFilePath;
2832
2833 return S_OK;
2834}
2835
2836HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2837{
2838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2839
2840 i_getLogFolder(aLogFolder);
2841
2842 return S_OK;
2843}
2844
2845HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2846{
2847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2848
2849 aCurrentSnapshot = mData->mCurrentSnapshot;
2850
2851 return S_OK;
2852}
2853
2854HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2855{
2856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2857
2858 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2859 ? 0
2860 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2861
2862 return S_OK;
2863}
2864
2865HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2866{
2867 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2868
2869 /* Note: for machines with no snapshots, we always return FALSE
2870 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2871 * reasons :) */
2872
2873 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2874 ? FALSE
2875 : mData->mCurrentStateModified;
2876
2877 return S_OK;
2878}
2879
2880HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2881{
2882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2883
2884 aSharedFolders.resize(mHWData->mSharedFolders.size());
2885 size_t i = 0;
2886 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2887 it = mHWData->mSharedFolders.begin();
2888 it != mHWData->mSharedFolders.end();
2889 ++it, ++i)
2890 aSharedFolders[i] = *it;
2891
2892 return S_OK;
2893}
2894
2895HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2896{
2897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2898
2899 *aClipboardMode = mHWData->mClipboardMode;
2900
2901 return S_OK;
2902}
2903
2904HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2905{
2906 HRESULT rc = S_OK;
2907
2908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2909
2910 rc = i_checkStateDependency(MutableOrRunningStateDep);
2911 if (FAILED(rc)) return rc;
2912
2913 alock.release();
2914 rc = i_onClipboardModeChange(aClipboardMode);
2915 alock.acquire();
2916 if (FAILED(rc)) return rc;
2917
2918 i_setModified(IsModified_MachineData);
2919 mHWData.backup();
2920 mHWData->mClipboardMode = aClipboardMode;
2921
2922 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2923 if (Global::IsOnline(mData->mMachineState))
2924 i_saveSettings(NULL, alock);
2925
2926 return S_OK;
2927}
2928
2929HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2930{
2931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2932
2933 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2934
2935 return S_OK;
2936}
2937
2938HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2939{
2940 HRESULT rc = S_OK;
2941
2942 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2943
2944 rc = i_checkStateDependency(MutableOrRunningStateDep);
2945 if (FAILED(rc)) return rc;
2946
2947 alock.release();
2948 rc = i_onClipboardFileTransferModeChange(aEnabled);
2949 alock.acquire();
2950 if (FAILED(rc)) return rc;
2951
2952 i_setModified(IsModified_MachineData);
2953 mHWData.backup();
2954 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2955
2956 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2957 if (Global::IsOnline(mData->mMachineState))
2958 i_saveSettings(NULL, alock);
2959
2960 return S_OK;
2961}
2962
2963HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2964{
2965 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2966
2967 *aDnDMode = mHWData->mDnDMode;
2968
2969 return S_OK;
2970}
2971
2972HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2973{
2974 HRESULT rc = S_OK;
2975
2976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2977
2978 rc = i_checkStateDependency(MutableOrRunningStateDep);
2979 if (FAILED(rc)) return rc;
2980
2981 alock.release();
2982 rc = i_onDnDModeChange(aDnDMode);
2983
2984 alock.acquire();
2985 if (FAILED(rc)) return rc;
2986
2987 i_setModified(IsModified_MachineData);
2988 mHWData.backup();
2989 mHWData->mDnDMode = aDnDMode;
2990
2991 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2992 if (Global::IsOnline(mData->mMachineState))
2993 i_saveSettings(NULL, alock);
2994
2995 return S_OK;
2996}
2997
2998HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2999{
3000 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3001
3002 aStorageControllers.resize(mStorageControllers->size());
3003 size_t i = 0;
3004 for (StorageControllerList::const_iterator
3005 it = mStorageControllers->begin();
3006 it != mStorageControllers->end();
3007 ++it, ++i)
3008 aStorageControllers[i] = *it;
3009
3010 return S_OK;
3011}
3012
3013HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
3014{
3015 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3016
3017 *aEnabled = mUserData->s.fTeleporterEnabled;
3018
3019 return S_OK;
3020}
3021
3022HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
3023{
3024 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3025
3026 /* Only allow it to be set to true when PoweredOff or Aborted.
3027 (Clearing it is always permitted.) */
3028 if ( aTeleporterEnabled
3029 && mData->mRegistered
3030 && ( !i_isSessionMachine()
3031 || ( mData->mMachineState != MachineState_PoweredOff
3032 && mData->mMachineState != MachineState_Teleported
3033 && mData->mMachineState != MachineState_Aborted
3034 )
3035 )
3036 )
3037 return setError(VBOX_E_INVALID_VM_STATE,
3038 tr("The machine is not powered off (state is %s)"),
3039 Global::stringifyMachineState(mData->mMachineState));
3040
3041 i_setModified(IsModified_MachineData);
3042 mUserData.backup();
3043 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
3044
3045 return S_OK;
3046}
3047
3048HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
3049{
3050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3051
3052 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
3053
3054 return S_OK;
3055}
3056
3057HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
3058{
3059 if (aTeleporterPort >= _64K)
3060 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
3061
3062 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3063
3064 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3065 if (FAILED(rc)) return rc;
3066
3067 i_setModified(IsModified_MachineData);
3068 mUserData.backup();
3069 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3070
3071 return S_OK;
3072}
3073
3074HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3075{
3076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3077
3078 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3079
3080 return S_OK;
3081}
3082
3083HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3084{
3085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3086
3087 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3088 if (FAILED(rc)) return rc;
3089
3090 i_setModified(IsModified_MachineData);
3091 mUserData.backup();
3092 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3093
3094 return S_OK;
3095}
3096
3097HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3098{
3099 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3100 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3101
3102 return S_OK;
3103}
3104
3105HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3106{
3107 /*
3108 * Hash the password first.
3109 */
3110 com::Utf8Str aT = aTeleporterPassword;
3111
3112 if (!aT.isEmpty())
3113 {
3114 if (VBoxIsPasswordHashed(&aT))
3115 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3116 VBoxHashPassword(&aT);
3117 }
3118
3119 /*
3120 * Do the update.
3121 */
3122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3123 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3124 if (SUCCEEDED(hrc))
3125 {
3126 i_setModified(IsModified_MachineData);
3127 mUserData.backup();
3128 mUserData->s.strTeleporterPassword = aT;
3129 }
3130
3131 return hrc;
3132}
3133
3134HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3135{
3136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3137
3138 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3139
3140 return S_OK;
3141}
3142
3143HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3144{
3145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3146
3147 /* Only allow it to be set to true when PoweredOff or Aborted.
3148 (Clearing it is always permitted.) */
3149 if ( aRTCUseUTC
3150 && mData->mRegistered
3151 && ( !i_isSessionMachine()
3152 || ( mData->mMachineState != MachineState_PoweredOff
3153 && mData->mMachineState != MachineState_Teleported
3154 && mData->mMachineState != MachineState_Aborted
3155 )
3156 )
3157 )
3158 return setError(VBOX_E_INVALID_VM_STATE,
3159 tr("The machine is not powered off (state is %s)"),
3160 Global::stringifyMachineState(mData->mMachineState));
3161
3162 i_setModified(IsModified_MachineData);
3163 mUserData.backup();
3164 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3165
3166 return S_OK;
3167}
3168
3169HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3170{
3171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3172
3173 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3174
3175 return S_OK;
3176}
3177
3178HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3179{
3180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3181
3182 HRESULT rc = i_checkStateDependency(MutableStateDep);
3183 if (FAILED(rc)) return rc;
3184
3185 i_setModified(IsModified_MachineData);
3186 mHWData.backup();
3187 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3188
3189 return S_OK;
3190}
3191
3192HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3193{
3194 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3195
3196 *aIOCacheSize = mHWData->mIOCacheSize;
3197
3198 return S_OK;
3199}
3200
3201HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3202{
3203 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3204
3205 HRESULT rc = i_checkStateDependency(MutableStateDep);
3206 if (FAILED(rc)) return rc;
3207
3208 i_setModified(IsModified_MachineData);
3209 mHWData.backup();
3210 mHWData->mIOCacheSize = aIOCacheSize;
3211
3212 return S_OK;
3213}
3214
3215HRESULT Machine::getStateKeyId(com::Utf8Str &aKeyId)
3216{
3217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3218
3219#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3220 aKeyId = mSSData->strStateKeyId;
3221#else
3222 aKeyId = com::Utf8Str::Empty;
3223#endif
3224
3225 return S_OK;
3226}
3227
3228HRESULT Machine::getStateKeyStore(com::Utf8Str &aKeyStore)
3229{
3230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3231
3232#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3233 aKeyStore = mSSData->strStateKeyStore;
3234#else
3235 aKeyStore = com::Utf8Str::Empty;
3236#endif
3237
3238 return S_OK;
3239}
3240
3241HRESULT Machine::getLogKeyId(com::Utf8Str &aKeyId)
3242{
3243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3244
3245#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3246 aKeyId = mData->mstrLogKeyId;
3247#else
3248 aKeyId = com::Utf8Str::Empty;
3249#endif
3250
3251 return S_OK;
3252}
3253
3254HRESULT Machine::getLogKeyStore(com::Utf8Str &aKeyStore)
3255{
3256 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3257
3258#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3259 aKeyStore = mData->mstrLogKeyStore;
3260#else
3261 aKeyStore = com::Utf8Str::Empty;
3262#endif
3263
3264 return S_OK;
3265}
3266
3267
3268/**
3269 * @note Locks objects!
3270 */
3271HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3272 LockType_T aLockType)
3273{
3274 /* check the session state */
3275 SessionState_T state;
3276 HRESULT rc = aSession->COMGETTER(State)(&state);
3277 if (FAILED(rc)) return rc;
3278
3279 if (state != SessionState_Unlocked)
3280 return setError(VBOX_E_INVALID_OBJECT_STATE,
3281 tr("The given session is busy"));
3282
3283 // get the client's IInternalSessionControl interface
3284 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3285 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
3286 E_INVALIDARG);
3287
3288 // session name (only used in some code paths)
3289 Utf8Str strSessionName;
3290
3291 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3292
3293 if (!mData->mRegistered)
3294 return setError(E_UNEXPECTED,
3295 tr("The machine '%s' is not registered"),
3296 mUserData->s.strName.c_str());
3297
3298 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3299
3300 SessionState_T oldState = mData->mSession.mState;
3301 /* Hack: in case the session is closing and there is a progress object
3302 * which allows waiting for the session to be closed, take the opportunity
3303 * and do a limited wait (max. 1 second). This helps a lot when the system
3304 * is busy and thus session closing can take a little while. */
3305 if ( mData->mSession.mState == SessionState_Unlocking
3306 && mData->mSession.mProgress)
3307 {
3308 alock.release();
3309 mData->mSession.mProgress->WaitForCompletion(1000);
3310 alock.acquire();
3311 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3312 }
3313
3314 // try again now
3315 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3316 // (i.e. session machine exists)
3317 && (aLockType == LockType_Shared) // caller wants a shared link to the
3318 // existing session that holds the write lock:
3319 )
3320 {
3321 // OK, share the session... we are now dealing with three processes:
3322 // 1) VBoxSVC (where this code runs);
3323 // 2) process C: the caller's client process (who wants a shared session);
3324 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3325
3326 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3327 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3328 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3329 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3330 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3331
3332 /*
3333 * Release the lock before calling the client process. It's safe here
3334 * since the only thing to do after we get the lock again is to add
3335 * the remote control to the list (which doesn't directly influence
3336 * anything).
3337 */
3338 alock.release();
3339
3340 // get the console of the session holding the write lock (this is a remote call)
3341 ComPtr<IConsole> pConsoleW;
3342 if (mData->mSession.mLockType == LockType_VM)
3343 {
3344 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3345 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3346 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3347 if (FAILED(rc))
3348 // the failure may occur w/o any error info (from RPC), so provide one
3349 return setError(VBOX_E_VM_ERROR,
3350 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3351 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3352 }
3353
3354 // share the session machine and W's console with the caller's session
3355 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3356 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3357 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3358
3359 if (FAILED(rc))
3360 // the failure may occur w/o any error info (from RPC), so provide one
3361 return setError(VBOX_E_VM_ERROR,
3362 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3363 alock.acquire();
3364
3365 // need to revalidate the state after acquiring the lock again
3366 if (mData->mSession.mState != SessionState_Locked)
3367 {
3368 pSessionControl->Uninitialize();
3369 return setError(VBOX_E_INVALID_SESSION_STATE,
3370 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3371 mUserData->s.strName.c_str());
3372 }
3373
3374 // add the caller's session to the list
3375 mData->mSession.mRemoteControls.push_back(pSessionControl);
3376 }
3377 else if ( mData->mSession.mState == SessionState_Locked
3378 || mData->mSession.mState == SessionState_Unlocking
3379 )
3380 {
3381 // sharing not permitted, or machine still unlocking:
3382 return setError(VBOX_E_INVALID_OBJECT_STATE,
3383 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3384 mUserData->s.strName.c_str());
3385 }
3386 else
3387 {
3388 // machine is not locked: then write-lock the machine (create the session machine)
3389
3390 // must not be busy
3391 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3392
3393 // get the caller's session PID
3394 RTPROCESS pid = NIL_RTPROCESS;
3395 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3396 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3397 Assert(pid != NIL_RTPROCESS);
3398
3399 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3400
3401 if (fLaunchingVMProcess)
3402 {
3403 if (mData->mSession.mPID == NIL_RTPROCESS)
3404 {
3405 // two or more clients racing for a lock, the one which set the
3406 // session state to Spawning will win, the others will get an
3407 // error as we can't decide here if waiting a little would help
3408 // (only for shared locks this would avoid an error)
3409 return setError(VBOX_E_INVALID_OBJECT_STATE,
3410 tr("The machine '%s' already has a lock request pending"),
3411 mUserData->s.strName.c_str());
3412 }
3413
3414 // this machine is awaiting for a spawning session to be opened:
3415 // then the calling process must be the one that got started by
3416 // LaunchVMProcess()
3417
3418 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3419 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3420
3421#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3422 /* Hardened windows builds spawns three processes when a VM is
3423 launched, the 3rd one is the one that will end up here. */
3424 RTPROCESS pidParent;
3425 int vrc = RTProcQueryParent(pid, &pidParent);
3426 if (RT_SUCCESS(vrc))
3427 vrc = RTProcQueryParent(pidParent, &pidParent);
3428 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3429 || vrc == VERR_ACCESS_DENIED)
3430 {
3431 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3432 mData->mSession.mPID = pid;
3433 }
3434#endif
3435
3436 if (mData->mSession.mPID != pid)
3437 return setError(E_ACCESSDENIED,
3438 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3439 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3440 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3441 }
3442
3443 // create the mutable SessionMachine from the current machine
3444 ComObjPtr<SessionMachine> sessionMachine;
3445 sessionMachine.createObject();
3446 rc = sessionMachine->init(this);
3447 AssertComRC(rc);
3448
3449 /* NOTE: doing return from this function after this point but
3450 * before the end is forbidden since it may call SessionMachine::uninit()
3451 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3452 * lock while still holding the Machine lock in alock so that a deadlock
3453 * is possible due to the wrong lock order. */
3454
3455 if (SUCCEEDED(rc))
3456 {
3457 /*
3458 * Set the session state to Spawning to protect against subsequent
3459 * attempts to open a session and to unregister the machine after
3460 * we release the lock.
3461 */
3462 SessionState_T origState = mData->mSession.mState;
3463 mData->mSession.mState = SessionState_Spawning;
3464
3465#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3466 /* Get the client token ID to be passed to the client process */
3467 Utf8Str strTokenId;
3468 sessionMachine->i_getTokenId(strTokenId);
3469 Assert(!strTokenId.isEmpty());
3470#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3471 /* Get the client token to be passed to the client process */
3472 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3473 /* The token is now "owned" by pToken, fix refcount */
3474 if (!pToken.isNull())
3475 pToken->Release();
3476#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3477
3478 /*
3479 * Release the lock before calling the client process -- it will call
3480 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3481 * because the state is Spawning, so that LaunchVMProcess() and
3482 * LockMachine() calls will fail. This method, called before we
3483 * acquire the lock again, will fail because of the wrong PID.
3484 *
3485 * Note that mData->mSession.mRemoteControls accessed outside
3486 * the lock may not be modified when state is Spawning, so it's safe.
3487 */
3488 alock.release();
3489
3490 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3491#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3492 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3493#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3494 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3495 /* Now the token is owned by the client process. */
3496 pToken.setNull();
3497#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3498 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3499
3500 /* The failure may occur w/o any error info (from RPC), so provide one */
3501 if (FAILED(rc))
3502 setError(VBOX_E_VM_ERROR,
3503 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3504
3505 // get session name, either to remember or to compare against
3506 // the already known session name.
3507 {
3508 Bstr bstrSessionName;
3509 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3510 if (SUCCEEDED(rc2))
3511 strSessionName = bstrSessionName;
3512 }
3513
3514 if ( SUCCEEDED(rc)
3515 && fLaunchingVMProcess
3516 )
3517 {
3518 /* complete the remote session initialization */
3519
3520 /* get the console from the direct session */
3521 ComPtr<IConsole> console;
3522 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3523 ComAssertComRC(rc);
3524
3525 if (SUCCEEDED(rc) && !console)
3526 {
3527 ComAssert(!!console);
3528 rc = E_FAIL;
3529 }
3530
3531 /* assign machine & console to the remote session */
3532 if (SUCCEEDED(rc))
3533 {
3534 /*
3535 * after LaunchVMProcess(), the first and the only
3536 * entry in remoteControls is that remote session
3537 */
3538 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3539 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3540 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3541
3542 /* The failure may occur w/o any error info (from RPC), so provide one */
3543 if (FAILED(rc))
3544 setError(VBOX_E_VM_ERROR,
3545 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3546 }
3547
3548 if (FAILED(rc))
3549 pSessionControl->Uninitialize();
3550 }
3551
3552 /* acquire the lock again */
3553 alock.acquire();
3554
3555 /* Restore the session state */
3556 mData->mSession.mState = origState;
3557 }
3558
3559 // finalize spawning anyway (this is why we don't return on errors above)
3560 if (fLaunchingVMProcess)
3561 {
3562 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3563 /* Note that the progress object is finalized later */
3564 /** @todo Consider checking mData->mSession.mProgress for cancellation
3565 * around here. */
3566
3567 /* We don't reset mSession.mPID here because it is necessary for
3568 * SessionMachine::uninit() to reap the child process later. */
3569
3570 if (FAILED(rc))
3571 {
3572 /* Close the remote session, remove the remote control from the list
3573 * and reset session state to Closed (@note keep the code in sync
3574 * with the relevant part in checkForSpawnFailure()). */
3575
3576 Assert(mData->mSession.mRemoteControls.size() == 1);
3577 if (mData->mSession.mRemoteControls.size() == 1)
3578 {
3579 ErrorInfoKeeper eik;
3580 mData->mSession.mRemoteControls.front()->Uninitialize();
3581 }
3582
3583 mData->mSession.mRemoteControls.clear();
3584 mData->mSession.mState = SessionState_Unlocked;
3585 }
3586 }
3587 else
3588 {
3589 /* memorize PID of the directly opened session */
3590 if (SUCCEEDED(rc))
3591 mData->mSession.mPID = pid;
3592 }
3593
3594 if (SUCCEEDED(rc))
3595 {
3596 mData->mSession.mLockType = aLockType;
3597 /* memorize the direct session control and cache IUnknown for it */
3598 mData->mSession.mDirectControl = pSessionControl;
3599 mData->mSession.mState = SessionState_Locked;
3600 if (!fLaunchingVMProcess)
3601 mData->mSession.mName = strSessionName;
3602 /* associate the SessionMachine with this Machine */
3603 mData->mSession.mMachine = sessionMachine;
3604
3605 /* request an IUnknown pointer early from the remote party for later
3606 * identity checks (it will be internally cached within mDirectControl
3607 * at least on XPCOM) */
3608 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3609 NOREF(unk);
3610 }
3611
3612 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3613 * would break the lock order */
3614 alock.release();
3615
3616 /* uninitialize the created session machine on failure */
3617 if (FAILED(rc))
3618 sessionMachine->uninit();
3619 }
3620
3621 if (SUCCEEDED(rc))
3622 {
3623 /*
3624 * tell the client watcher thread to update the set of
3625 * machines that have open sessions
3626 */
3627 mParent->i_updateClientWatcher();
3628
3629 if (oldState != SessionState_Locked)
3630 /* fire an event */
3631 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3632 }
3633
3634 return rc;
3635}
3636
3637/**
3638 * @note Locks objects!
3639 */
3640HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3641 const com::Utf8Str &aName,
3642 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3643 ComPtr<IProgress> &aProgress)
3644{
3645 Utf8Str strFrontend(aName);
3646 /* "emergencystop" doesn't need the session, so skip the checks/interface
3647 * retrieval. This code doesn't quite fit in here, but introducing a
3648 * special API method would be even more effort, and would require explicit
3649 * support by every API client. It's better to hide the feature a bit. */
3650 if (strFrontend != "emergencystop")
3651 CheckComArgNotNull(aSession);
3652
3653 HRESULT rc = S_OK;
3654 if (strFrontend.isEmpty())
3655 {
3656 Bstr bstrFrontend;
3657 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3658 if (FAILED(rc))
3659 return rc;
3660 strFrontend = bstrFrontend;
3661 if (strFrontend.isEmpty())
3662 {
3663 ComPtr<ISystemProperties> systemProperties;
3664 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3665 if (FAILED(rc))
3666 return rc;
3667 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3668 if (FAILED(rc))
3669 return rc;
3670 strFrontend = bstrFrontend;
3671 }
3672 /* paranoia - emergencystop is not a valid default */
3673 if (strFrontend == "emergencystop")
3674 strFrontend = Utf8Str::Empty;
3675 }
3676 /* default frontend: Qt GUI */
3677 if (strFrontend.isEmpty())
3678 strFrontend = "GUI/Qt";
3679
3680 if (strFrontend != "emergencystop")
3681 {
3682 /* check the session state */
3683 SessionState_T state;
3684 rc = aSession->COMGETTER(State)(&state);
3685 if (FAILED(rc))
3686 return rc;
3687
3688 if (state != SessionState_Unlocked)
3689 return setError(VBOX_E_INVALID_OBJECT_STATE,
3690 tr("The given session is busy"));
3691
3692 /* get the IInternalSessionControl interface */
3693 ComPtr<IInternalSessionControl> control(aSession);
3694 ComAssertMsgRet(!control.isNull(),
3695 ("No IInternalSessionControl interface"),
3696 E_INVALIDARG);
3697
3698 /* get the teleporter enable state for the progress object init. */
3699 BOOL fTeleporterEnabled;
3700 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3701 if (FAILED(rc))
3702 return rc;
3703
3704 /* create a progress object */
3705 ComObjPtr<ProgressProxy> progress;
3706 progress.createObject();
3707 rc = progress->init(mParent,
3708 static_cast<IMachine*>(this),
3709 Bstr(tr("Starting VM")).raw(),
3710 TRUE /* aCancelable */,
3711 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3712 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3713 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3714 2 /* uFirstOperationWeight */,
3715 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3716
3717 if (SUCCEEDED(rc))
3718 {
3719 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3720 if (SUCCEEDED(rc))
3721 {
3722 aProgress = progress;
3723
3724 /* signal the client watcher thread */
3725 mParent->i_updateClientWatcher();
3726
3727 /* fire an event */
3728 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3729 }
3730 }
3731 }
3732 else
3733 {
3734 /* no progress object - either instant success or failure */
3735 aProgress = NULL;
3736
3737 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3738
3739 if (mData->mSession.mState != SessionState_Locked)
3740 return setError(VBOX_E_INVALID_OBJECT_STATE,
3741 tr("The machine '%s' is not locked by a session"),
3742 mUserData->s.strName.c_str());
3743
3744 /* must have a VM process associated - do not kill normal API clients
3745 * with an open session */
3746 if (!Global::IsOnline(mData->mMachineState))
3747 return setError(VBOX_E_INVALID_OBJECT_STATE,
3748 tr("The machine '%s' does not have a VM process"),
3749 mUserData->s.strName.c_str());
3750
3751 /* forcibly terminate the VM process */
3752 if (mData->mSession.mPID != NIL_RTPROCESS)
3753 RTProcTerminate(mData->mSession.mPID);
3754
3755 /* signal the client watcher thread, as most likely the client has
3756 * been terminated */
3757 mParent->i_updateClientWatcher();
3758 }
3759
3760 return rc;
3761}
3762
3763HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3764{
3765 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3766 return setError(E_INVALIDARG,
3767 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3768 aPosition, SchemaDefs::MaxBootPosition);
3769
3770 if (aDevice == DeviceType_USB)
3771 return setError(E_NOTIMPL,
3772 tr("Booting from USB device is currently not supported"));
3773
3774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3775
3776 HRESULT rc = i_checkStateDependency(MutableStateDep);
3777 if (FAILED(rc)) return rc;
3778
3779 i_setModified(IsModified_MachineData);
3780 mHWData.backup();
3781 mHWData->mBootOrder[aPosition - 1] = aDevice;
3782
3783 return S_OK;
3784}
3785
3786HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3787{
3788 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3789 return setError(E_INVALIDARG,
3790 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3791 aPosition, SchemaDefs::MaxBootPosition);
3792
3793 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3794
3795 *aDevice = mHWData->mBootOrder[aPosition - 1];
3796
3797 return S_OK;
3798}
3799
3800HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3801 LONG aControllerPort,
3802 LONG aDevice,
3803 DeviceType_T aType,
3804 const ComPtr<IMedium> &aMedium)
3805{
3806 IMedium *aM = aMedium;
3807 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3808 aName.c_str(), aControllerPort, aDevice, aType, aM));
3809
3810 // request the host lock first, since might be calling Host methods for getting host drives;
3811 // next, protect the media tree all the while we're in here, as well as our member variables
3812 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3813 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3814
3815 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3816 if (FAILED(rc)) return rc;
3817
3818 /// @todo NEWMEDIA implicit machine registration
3819 if (!mData->mRegistered)
3820 return setError(VBOX_E_INVALID_OBJECT_STATE,
3821 tr("Cannot attach storage devices to an unregistered machine"));
3822
3823 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3824
3825 /* Check for an existing controller. */
3826 ComObjPtr<StorageController> ctl;
3827 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3828 if (FAILED(rc)) return rc;
3829
3830 StorageControllerType_T ctrlType;
3831 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3832 if (FAILED(rc))
3833 return setError(E_FAIL,
3834 tr("Could not get type of controller '%s'"),
3835 aName.c_str());
3836
3837 bool fSilent = false;
3838 Utf8Str strReconfig;
3839
3840 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3841 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3842 if ( mData->mMachineState == MachineState_Paused
3843 && strReconfig == "1")
3844 fSilent = true;
3845
3846 /* Check that the controller can do hot-plugging if we attach the device while the VM is running. */
3847 bool fHotplug = false;
3848 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3849 fHotplug = true;
3850
3851 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3852 return setError(VBOX_E_INVALID_VM_STATE,
3853 tr("Controller '%s' does not support hot-plugging"),
3854 aName.c_str());
3855
3856 /* Attaching a USB device when a VM is powered off should default to being marked as hot-pluggable */
3857 if (!fHotplug && !Global::IsOnlineOrTransient(mData->mMachineState) && ctrlType == StorageControllerType_USB)
3858 fHotplug = true;
3859
3860 // check that the port and device are not out of range
3861 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3862 if (FAILED(rc)) return rc;
3863
3864 /* check if the device slot is already busy */
3865 MediumAttachment *pAttachTemp;
3866 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3867 aName,
3868 aControllerPort,
3869 aDevice)))
3870 {
3871 Medium *pMedium = pAttachTemp->i_getMedium();
3872 if (pMedium)
3873 {
3874 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3875 return setError(VBOX_E_OBJECT_IN_USE,
3876 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3877 pMedium->i_getLocationFull().c_str(),
3878 aControllerPort,
3879 aDevice,
3880 aName.c_str());
3881 }
3882 else
3883 return setError(VBOX_E_OBJECT_IN_USE,
3884 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3885 aControllerPort, aDevice, aName.c_str());
3886 }
3887
3888 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3889 if (aMedium && medium.isNull())
3890 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3891
3892 AutoCaller mediumCaller(medium);
3893 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3894
3895 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3896
3897 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3898 && !medium.isNull()
3899 && ( medium->i_getType() != MediumType_Readonly
3900 || medium->i_getDeviceType() != DeviceType_DVD)
3901 )
3902 return setError(VBOX_E_OBJECT_IN_USE,
3903 tr("Medium '%s' is already attached to this virtual machine"),
3904 medium->i_getLocationFull().c_str());
3905
3906 if (!medium.isNull())
3907 {
3908 MediumType_T mtype = medium->i_getType();
3909 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3910 // For DVDs it's not written to the config file, so needs no global config
3911 // version bump. For floppies it's a new attribute "type", which is ignored
3912 // by older VirtualBox version, so needs no global config version bump either.
3913 // For hard disks this type is not accepted.
3914 if (mtype == MediumType_MultiAttach)
3915 {
3916 // This type is new with VirtualBox 4.0 and therefore requires settings
3917 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3918 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3919 // two reasons: The medium type is a property of the media registry tree, which
3920 // can reside in the global config file (for pre-4.0 media); we would therefore
3921 // possibly need to bump the global config version. We don't want to do that though
3922 // because that might make downgrading to pre-4.0 impossible.
3923 // As a result, we can only use these two new types if the medium is NOT in the
3924 // global registry:
3925 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3926 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3927 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3928 )
3929 return setError(VBOX_E_INVALID_OBJECT_STATE,
3930 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3931 "to machines that were created with VirtualBox 4.0 or later"),
3932 medium->i_getLocationFull().c_str());
3933 }
3934 }
3935
3936 bool fIndirect = false;
3937 if (!medium.isNull())
3938 fIndirect = medium->i_isReadOnly();
3939 bool associate = true;
3940
3941 do
3942 {
3943 if ( aType == DeviceType_HardDisk
3944 && mMediumAttachments.isBackedUp())
3945 {
3946 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3947
3948 /* check if the medium was attached to the VM before we started
3949 * changing attachments in which case the attachment just needs to
3950 * be restored */
3951 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3952 {
3953 AssertReturn(!fIndirect, E_FAIL);
3954
3955 /* see if it's the same bus/channel/device */
3956 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3957 {
3958 /* the simplest case: restore the whole attachment
3959 * and return, nothing else to do */
3960 mMediumAttachments->push_back(pAttachTemp);
3961
3962 /* Reattach the medium to the VM. */
3963 if (fHotplug || fSilent)
3964 {
3965 mediumLock.release();
3966 treeLock.release();
3967 alock.release();
3968
3969 MediumLockList *pMediumLockList(new MediumLockList());
3970
3971 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3972 medium /* pToLockWrite */,
3973 false /* fMediumLockWriteAll */,
3974 NULL,
3975 *pMediumLockList);
3976 alock.acquire();
3977 if (FAILED(rc))
3978 delete pMediumLockList;
3979 else
3980 {
3981 mData->mSession.mLockedMedia.Unlock();
3982 alock.release();
3983 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3984 mData->mSession.mLockedMedia.Lock();
3985 alock.acquire();
3986 }
3987 alock.release();
3988
3989 if (SUCCEEDED(rc))
3990 {
3991 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3992 /* Remove lock list in case of error. */
3993 if (FAILED(rc))
3994 {
3995 mData->mSession.mLockedMedia.Unlock();
3996 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3997 mData->mSession.mLockedMedia.Lock();
3998 }
3999 }
4000 }
4001
4002 return S_OK;
4003 }
4004
4005 /* bus/channel/device differ; we need a new attachment object,
4006 * but don't try to associate it again */
4007 associate = false;
4008 break;
4009 }
4010 }
4011
4012 /* go further only if the attachment is to be indirect */
4013 if (!fIndirect)
4014 break;
4015
4016 /* perform the so called smart attachment logic for indirect
4017 * attachments. Note that smart attachment is only applicable to base
4018 * hard disks. */
4019
4020 if (medium->i_getParent().isNull())
4021 {
4022 /* first, investigate the backup copy of the current hard disk
4023 * attachments to make it possible to re-attach existing diffs to
4024 * another device slot w/o losing their contents */
4025 if (mMediumAttachments.isBackedUp())
4026 {
4027 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4028
4029 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4030 uint32_t foundLevel = 0;
4031
4032 for (MediumAttachmentList::const_iterator
4033 it = oldAtts.begin();
4034 it != oldAtts.end();
4035 ++it)
4036 {
4037 uint32_t level = 0;
4038 MediumAttachment *pAttach = *it;
4039 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4040 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4041 if (pMedium.isNull())
4042 continue;
4043
4044 if (pMedium->i_getBase(&level) == medium)
4045 {
4046 /* skip the hard disk if its currently attached (we
4047 * cannot attach the same hard disk twice) */
4048 if (i_findAttachment(*mMediumAttachments.data(),
4049 pMedium))
4050 continue;
4051
4052 /* matched device, channel and bus (i.e. attached to the
4053 * same place) will win and immediately stop the search;
4054 * otherwise the attachment that has the youngest
4055 * descendant of medium will be used
4056 */
4057 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4058 {
4059 /* the simplest case: restore the whole attachment
4060 * and return, nothing else to do */
4061 mMediumAttachments->push_back(*it);
4062
4063 /* Reattach the medium to the VM. */
4064 if (fHotplug || fSilent)
4065 {
4066 mediumLock.release();
4067 treeLock.release();
4068 alock.release();
4069
4070 MediumLockList *pMediumLockList(new MediumLockList());
4071
4072 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4073 medium /* pToLockWrite */,
4074 false /* fMediumLockWriteAll */,
4075 NULL,
4076 *pMediumLockList);
4077 alock.acquire();
4078 if (FAILED(rc))
4079 delete pMediumLockList;
4080 else
4081 {
4082 mData->mSession.mLockedMedia.Unlock();
4083 alock.release();
4084 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4085 mData->mSession.mLockedMedia.Lock();
4086 alock.acquire();
4087 }
4088 alock.release();
4089
4090 if (SUCCEEDED(rc))
4091 {
4092 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4093 /* Remove lock list in case of error. */
4094 if (FAILED(rc))
4095 {
4096 mData->mSession.mLockedMedia.Unlock();
4097 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4098 mData->mSession.mLockedMedia.Lock();
4099 }
4100 }
4101 }
4102
4103 return S_OK;
4104 }
4105 else if ( foundIt == oldAtts.end()
4106 || level > foundLevel /* prefer younger */
4107 )
4108 {
4109 foundIt = it;
4110 foundLevel = level;
4111 }
4112 }
4113 }
4114
4115 if (foundIt != oldAtts.end())
4116 {
4117 /* use the previously attached hard disk */
4118 medium = (*foundIt)->i_getMedium();
4119 mediumCaller.attach(medium);
4120 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4121 mediumLock.attach(medium);
4122 /* not implicit, doesn't require association with this VM */
4123 fIndirect = false;
4124 associate = false;
4125 /* go right to the MediumAttachment creation */
4126 break;
4127 }
4128 }
4129
4130 /* must give up the medium lock and medium tree lock as below we
4131 * go over snapshots, which needs a lock with higher lock order. */
4132 mediumLock.release();
4133 treeLock.release();
4134
4135 /* then, search through snapshots for the best diff in the given
4136 * hard disk's chain to base the new diff on */
4137
4138 ComObjPtr<Medium> base;
4139 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4140 while (snap)
4141 {
4142 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4143
4144 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4145
4146 MediumAttachment *pAttachFound = NULL;
4147 uint32_t foundLevel = 0;
4148
4149 for (MediumAttachmentList::const_iterator
4150 it = snapAtts.begin();
4151 it != snapAtts.end();
4152 ++it)
4153 {
4154 MediumAttachment *pAttach = *it;
4155 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4156 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4157 if (pMedium.isNull())
4158 continue;
4159
4160 uint32_t level = 0;
4161 if (pMedium->i_getBase(&level) == medium)
4162 {
4163 /* matched device, channel and bus (i.e. attached to the
4164 * same place) will win and immediately stop the search;
4165 * otherwise the attachment that has the youngest
4166 * descendant of medium will be used
4167 */
4168 if ( pAttach->i_getDevice() == aDevice
4169 && pAttach->i_getPort() == aControllerPort
4170 && pAttach->i_getControllerName() == aName
4171 )
4172 {
4173 pAttachFound = pAttach;
4174 break;
4175 }
4176 else if ( !pAttachFound
4177 || level > foundLevel /* prefer younger */
4178 )
4179 {
4180 pAttachFound = pAttach;
4181 foundLevel = level;
4182 }
4183 }
4184 }
4185
4186 if (pAttachFound)
4187 {
4188 base = pAttachFound->i_getMedium();
4189 break;
4190 }
4191
4192 snap = snap->i_getParent();
4193 }
4194
4195 /* re-lock medium tree and the medium, as we need it below */
4196 treeLock.acquire();
4197 mediumLock.acquire();
4198
4199 /* found a suitable diff, use it as a base */
4200 if (!base.isNull())
4201 {
4202 medium = base;
4203 mediumCaller.attach(medium);
4204 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4205 mediumLock.attach(medium);
4206 }
4207 }
4208
4209 Utf8Str strFullSnapshotFolder;
4210 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4211
4212 ComObjPtr<Medium> diff;
4213 diff.createObject();
4214 // store this diff in the same registry as the parent
4215 Guid uuidRegistryParent;
4216 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4217 {
4218 // parent image has no registry: this can happen if we're attaching a new immutable
4219 // image that has not yet been attached (medium then points to the base and we're
4220 // creating the diff image for the immutable, and the parent is not yet registered);
4221 // put the parent in the machine registry then
4222 mediumLock.release();
4223 treeLock.release();
4224 alock.release();
4225 i_addMediumToRegistry(medium);
4226 alock.acquire();
4227 treeLock.acquire();
4228 mediumLock.acquire();
4229 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4230 }
4231 rc = diff->init(mParent,
4232 medium->i_getPreferredDiffFormat(),
4233 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4234 uuidRegistryParent,
4235 DeviceType_HardDisk);
4236 if (FAILED(rc)) return rc;
4237
4238 /* Apply the normal locking logic to the entire chain. */
4239 MediumLockList *pMediumLockList(new MediumLockList());
4240 mediumLock.release();
4241 treeLock.release();
4242 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4243 diff /* pToLockWrite */,
4244 false /* fMediumLockWriteAll */,
4245 medium,
4246 *pMediumLockList);
4247 treeLock.acquire();
4248 mediumLock.acquire();
4249 if (SUCCEEDED(rc))
4250 {
4251 mediumLock.release();
4252 treeLock.release();
4253 rc = pMediumLockList->Lock();
4254 treeLock.acquire();
4255 mediumLock.acquire();
4256 if (FAILED(rc))
4257 setError(rc,
4258 tr("Could not lock medium when creating diff '%s'"),
4259 diff->i_getLocationFull().c_str());
4260 else
4261 {
4262 /* will release the lock before the potentially lengthy
4263 * operation, so protect with the special state */
4264 MachineState_T oldState = mData->mMachineState;
4265 i_setMachineState(MachineState_SettingUp);
4266
4267 mediumLock.release();
4268 treeLock.release();
4269 alock.release();
4270
4271 rc = medium->i_createDiffStorage(diff,
4272 medium->i_getPreferredDiffVariant(),
4273 pMediumLockList,
4274 NULL /* aProgress */,
4275 true /* aWait */,
4276 false /* aNotify */);
4277
4278 alock.acquire();
4279 treeLock.acquire();
4280 mediumLock.acquire();
4281
4282 i_setMachineState(oldState);
4283 }
4284 }
4285
4286 /* Unlock the media and free the associated memory. */
4287 delete pMediumLockList;
4288
4289 if (FAILED(rc)) return rc;
4290
4291 /* use the created diff for the actual attachment */
4292 medium = diff;
4293 mediumCaller.attach(medium);
4294 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4295 mediumLock.attach(medium);
4296 }
4297 while (0);
4298
4299 ComObjPtr<MediumAttachment> attachment;
4300 attachment.createObject();
4301 rc = attachment->init(this,
4302 medium,
4303 aName,
4304 aControllerPort,
4305 aDevice,
4306 aType,
4307 fIndirect,
4308 false /* fPassthrough */,
4309 false /* fTempEject */,
4310 false /* fNonRotational */,
4311 false /* fDiscard */,
4312 fHotplug /* fHotPluggable */,
4313 Utf8Str::Empty);
4314 if (FAILED(rc)) return rc;
4315
4316 if (associate && !medium.isNull())
4317 {
4318 // as the last step, associate the medium to the VM
4319 rc = medium->i_addBackReference(mData->mUuid);
4320 // here we can fail because of Deleting, or being in process of creating a Diff
4321 if (FAILED(rc)) return rc;
4322
4323 mediumLock.release();
4324 treeLock.release();
4325 alock.release();
4326 i_addMediumToRegistry(medium);
4327 alock.acquire();
4328 treeLock.acquire();
4329 mediumLock.acquire();
4330 }
4331
4332 /* success: finally remember the attachment */
4333 i_setModified(IsModified_Storage);
4334 mMediumAttachments.backup();
4335 mMediumAttachments->push_back(attachment);
4336
4337 mediumLock.release();
4338 treeLock.release();
4339 alock.release();
4340
4341 if (fHotplug || fSilent)
4342 {
4343 if (!medium.isNull())
4344 {
4345 MediumLockList *pMediumLockList(new MediumLockList());
4346
4347 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4348 medium /* pToLockWrite */,
4349 false /* fMediumLockWriteAll */,
4350 NULL,
4351 *pMediumLockList);
4352 alock.acquire();
4353 if (FAILED(rc))
4354 delete pMediumLockList;
4355 else
4356 {
4357 mData->mSession.mLockedMedia.Unlock();
4358 alock.release();
4359 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4360 mData->mSession.mLockedMedia.Lock();
4361 alock.acquire();
4362 }
4363 alock.release();
4364 }
4365
4366 if (SUCCEEDED(rc))
4367 {
4368 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4369 /* Remove lock list in case of error. */
4370 if (FAILED(rc))
4371 {
4372 mData->mSession.mLockedMedia.Unlock();
4373 mData->mSession.mLockedMedia.Remove(attachment);
4374 mData->mSession.mLockedMedia.Lock();
4375 }
4376 }
4377 }
4378
4379 /* Save modified registries, but skip this machine as it's the caller's
4380 * job to save its settings like all other settings changes. */
4381 mParent->i_unmarkRegistryModified(i_getId());
4382 mParent->i_saveModifiedRegistries();
4383
4384 if (SUCCEEDED(rc))
4385 {
4386 if (fIndirect && medium != aM)
4387 mParent->i_onMediumConfigChanged(medium);
4388 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4389 }
4390
4391 return rc;
4392}
4393
4394HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4395 LONG aDevice)
4396{
4397 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4398 aName.c_str(), aControllerPort, aDevice));
4399
4400 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4401
4402 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4403 if (FAILED(rc)) return rc;
4404
4405 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4406
4407 /* Check for an existing controller. */
4408 ComObjPtr<StorageController> ctl;
4409 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4410 if (FAILED(rc)) return rc;
4411
4412 StorageControllerType_T ctrlType;
4413 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4414 if (FAILED(rc))
4415 return setError(E_FAIL,
4416 tr("Could not get type of controller '%s'"),
4417 aName.c_str());
4418
4419 bool fSilent = false;
4420 Utf8Str strReconfig;
4421
4422 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4423 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4424 if ( mData->mMachineState == MachineState_Paused
4425 && strReconfig == "1")
4426 fSilent = true;
4427
4428 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4429 bool fHotplug = false;
4430 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4431 fHotplug = true;
4432
4433 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4434 return setError(VBOX_E_INVALID_VM_STATE,
4435 tr("Controller '%s' does not support hot-plugging"),
4436 aName.c_str());
4437
4438 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4439 aName,
4440 aControllerPort,
4441 aDevice);
4442 if (!pAttach)
4443 return setError(VBOX_E_OBJECT_NOT_FOUND,
4444 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4445 aDevice, aControllerPort, aName.c_str());
4446
4447 if (fHotplug && !pAttach->i_getHotPluggable())
4448 return setError(VBOX_E_NOT_SUPPORTED,
4449 tr("The device slot %d on port %d of controller '%s' does not support hot-plugging"),
4450 aDevice, aControllerPort, aName.c_str());
4451
4452 /*
4453 * The VM has to detach the device before we delete any implicit diffs.
4454 * If this fails we can roll back without loosing data.
4455 */
4456 if (fHotplug || fSilent)
4457 {
4458 alock.release();
4459 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4460 alock.acquire();
4461 }
4462 if (FAILED(rc)) return rc;
4463
4464 /* If we are here everything went well and we can delete the implicit now. */
4465 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4466
4467 alock.release();
4468
4469 /* Save modified registries, but skip this machine as it's the caller's
4470 * job to save its settings like all other settings changes. */
4471 mParent->i_unmarkRegistryModified(i_getId());
4472 mParent->i_saveModifiedRegistries();
4473
4474 if (SUCCEEDED(rc))
4475 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4476
4477 return rc;
4478}
4479
4480HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4481 LONG aDevice, BOOL aPassthrough)
4482{
4483 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4484 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4485
4486 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4487
4488 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4489 if (FAILED(rc)) return rc;
4490
4491 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4492
4493 /* Check for an existing controller. */
4494 ComObjPtr<StorageController> ctl;
4495 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4496 if (FAILED(rc)) return rc;
4497
4498 StorageControllerType_T ctrlType;
4499 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4500 if (FAILED(rc))
4501 return setError(E_FAIL,
4502 tr("Could not get type of controller '%s'"),
4503 aName.c_str());
4504
4505 bool fSilent = false;
4506 Utf8Str strReconfig;
4507
4508 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4509 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4510 if ( mData->mMachineState == MachineState_Paused
4511 && strReconfig == "1")
4512 fSilent = true;
4513
4514 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4515 bool fHotplug = false;
4516 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4517 fHotplug = true;
4518
4519 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4520 return setError(VBOX_E_INVALID_VM_STATE,
4521 tr("Controller '%s' does not support hot-plugging which is required to change the passthrough setting while the VM is running"),
4522 aName.c_str());
4523
4524 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4525 aName,
4526 aControllerPort,
4527 aDevice);
4528 if (!pAttach)
4529 return setError(VBOX_E_OBJECT_NOT_FOUND,
4530 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4531 aDevice, aControllerPort, aName.c_str());
4532
4533
4534 i_setModified(IsModified_Storage);
4535 mMediumAttachments.backup();
4536
4537 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4538
4539 if (pAttach->i_getType() != DeviceType_DVD)
4540 return setError(E_INVALIDARG,
4541 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4542 aDevice, aControllerPort, aName.c_str());
4543
4544 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4545
4546 pAttach->i_updatePassthrough(!!aPassthrough);
4547
4548 attLock.release();
4549 alock.release();
4550 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4551 if (SUCCEEDED(rc) && fValueChanged)
4552 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4553
4554 return rc;
4555}
4556
4557HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4558 LONG aDevice, BOOL aTemporaryEject)
4559{
4560
4561 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4562 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4563
4564 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4565
4566 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4567 if (FAILED(rc)) return rc;
4568
4569 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4570 aName,
4571 aControllerPort,
4572 aDevice);
4573 if (!pAttach)
4574 return setError(VBOX_E_OBJECT_NOT_FOUND,
4575 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4576 aDevice, aControllerPort, aName.c_str());
4577
4578
4579 i_setModified(IsModified_Storage);
4580 mMediumAttachments.backup();
4581
4582 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4583
4584 if (pAttach->i_getType() != DeviceType_DVD)
4585 return setError(E_INVALIDARG,
4586 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4587 aDevice, aControllerPort, aName.c_str());
4588 pAttach->i_updateTempEject(!!aTemporaryEject);
4589
4590 return S_OK;
4591}
4592
4593HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4594 LONG aDevice, BOOL aNonRotational)
4595{
4596
4597 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4598 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4599
4600 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4601
4602 HRESULT rc = i_checkStateDependency(MutableStateDep);
4603 if (FAILED(rc)) return rc;
4604
4605 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4606
4607 if (Global::IsOnlineOrTransient(mData->mMachineState))
4608 return setError(VBOX_E_INVALID_VM_STATE,
4609 tr("Invalid machine state: %s"),
4610 Global::stringifyMachineState(mData->mMachineState));
4611
4612 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4613 aName,
4614 aControllerPort,
4615 aDevice);
4616 if (!pAttach)
4617 return setError(VBOX_E_OBJECT_NOT_FOUND,
4618 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4619 aDevice, aControllerPort, aName.c_str());
4620
4621
4622 i_setModified(IsModified_Storage);
4623 mMediumAttachments.backup();
4624
4625 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4626
4627 if (pAttach->i_getType() != DeviceType_HardDisk)
4628 return setError(E_INVALIDARG,
4629 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"),
4630 aDevice, aControllerPort, aName.c_str());
4631 pAttach->i_updateNonRotational(!!aNonRotational);
4632
4633 return S_OK;
4634}
4635
4636HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4637 LONG aDevice, BOOL aDiscard)
4638{
4639
4640 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4641 aName.c_str(), aControllerPort, aDevice, aDiscard));
4642
4643 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4644
4645 HRESULT rc = i_checkStateDependency(MutableStateDep);
4646 if (FAILED(rc)) return rc;
4647
4648 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4649
4650 if (Global::IsOnlineOrTransient(mData->mMachineState))
4651 return setError(VBOX_E_INVALID_VM_STATE,
4652 tr("Invalid machine state: %s"),
4653 Global::stringifyMachineState(mData->mMachineState));
4654
4655 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4656 aName,
4657 aControllerPort,
4658 aDevice);
4659 if (!pAttach)
4660 return setError(VBOX_E_OBJECT_NOT_FOUND,
4661 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4662 aDevice, aControllerPort, aName.c_str());
4663
4664
4665 i_setModified(IsModified_Storage);
4666 mMediumAttachments.backup();
4667
4668 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4669
4670 if (pAttach->i_getType() != DeviceType_HardDisk)
4671 return setError(E_INVALIDARG,
4672 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"),
4673 aDevice, aControllerPort, aName.c_str());
4674 pAttach->i_updateDiscard(!!aDiscard);
4675
4676 return S_OK;
4677}
4678
4679HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4680 LONG aDevice, BOOL aHotPluggable)
4681{
4682 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4683 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4684
4685 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4686
4687 HRESULT rc = i_checkStateDependency(MutableStateDep);
4688 if (FAILED(rc)) return rc;
4689
4690 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4691
4692 if (Global::IsOnlineOrTransient(mData->mMachineState))
4693 return setError(VBOX_E_INVALID_VM_STATE,
4694 tr("Invalid machine state: %s"),
4695 Global::stringifyMachineState(mData->mMachineState));
4696
4697 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4698 aName,
4699 aControllerPort,
4700 aDevice);
4701 if (!pAttach)
4702 return setError(VBOX_E_OBJECT_NOT_FOUND,
4703 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4704 aDevice, aControllerPort, aName.c_str());
4705
4706 /* Check for an existing controller. */
4707 ComObjPtr<StorageController> ctl;
4708 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4709 if (FAILED(rc)) return rc;
4710
4711 StorageControllerType_T ctrlType;
4712 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4713 if (FAILED(rc))
4714 return setError(E_FAIL,
4715 tr("Could not get type of controller '%s'"),
4716 aName.c_str());
4717
4718 if (!i_isControllerHotplugCapable(ctrlType))
4719 return setError(VBOX_E_NOT_SUPPORTED,
4720 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4721 aName.c_str());
4722
4723 /* silently ignore attempts to modify the hot-plug status of USB devices */
4724 if (ctrlType == StorageControllerType_USB)
4725 return S_OK;
4726
4727 i_setModified(IsModified_Storage);
4728 mMediumAttachments.backup();
4729
4730 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4731
4732 if (pAttach->i_getType() == DeviceType_Floppy)
4733 return setError(E_INVALIDARG,
4734 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"),
4735 aDevice, aControllerPort, aName.c_str());
4736 pAttach->i_updateHotPluggable(!!aHotPluggable);
4737
4738 return S_OK;
4739}
4740
4741HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4742 LONG aDevice)
4743{
4744 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4745 aName.c_str(), aControllerPort, aDevice));
4746
4747 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4748}
4749
4750HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4751 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4752{
4753 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4754 aName.c_str(), aControllerPort, aDevice));
4755
4756 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4757
4758 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4759 if (FAILED(rc)) return rc;
4760
4761 if (Global::IsOnlineOrTransient(mData->mMachineState))
4762 return setError(VBOX_E_INVALID_VM_STATE,
4763 tr("Invalid machine state: %s"),
4764 Global::stringifyMachineState(mData->mMachineState));
4765
4766 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4767 aName,
4768 aControllerPort,
4769 aDevice);
4770 if (!pAttach)
4771 return setError(VBOX_E_OBJECT_NOT_FOUND,
4772 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4773 aDevice, aControllerPort, aName.c_str());
4774
4775
4776 i_setModified(IsModified_Storage);
4777 mMediumAttachments.backup();
4778
4779 IBandwidthGroup *iB = aBandwidthGroup;
4780 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4781 if (aBandwidthGroup && group.isNull())
4782 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4783
4784 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4785
4786 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4787 if (strBandwidthGroupOld.isNotEmpty())
4788 {
4789 /* Get the bandwidth group object and release it - this must not fail. */
4790 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4791 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4792 Assert(SUCCEEDED(rc));
4793
4794 pBandwidthGroupOld->i_release();
4795 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4796 }
4797
4798 if (!group.isNull())
4799 {
4800 group->i_reference();
4801 pAttach->i_updateBandwidthGroup(group->i_getName());
4802 }
4803
4804 return S_OK;
4805}
4806
4807HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4808 LONG aControllerPort,
4809 LONG aDevice,
4810 DeviceType_T aType)
4811{
4812 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4813 aName.c_str(), aControllerPort, aDevice, aType));
4814
4815 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4816}
4817
4818
4819HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4820 LONG aControllerPort,
4821 LONG aDevice,
4822 BOOL aForce)
4823{
4824 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4825 aName.c_str(), aControllerPort, aForce));
4826
4827 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4828}
4829
4830HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4831 LONG aControllerPort,
4832 LONG aDevice,
4833 const ComPtr<IMedium> &aMedium,
4834 BOOL aForce)
4835{
4836 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4837 aName.c_str(), aControllerPort, aDevice, aForce));
4838
4839 // request the host lock first, since might be calling Host methods for getting host drives;
4840 // next, protect the media tree all the while we're in here, as well as our member variables
4841 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4842 this->lockHandle(),
4843 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4844
4845 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4846 if (FAILED(hrc)) return hrc;
4847
4848 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4849 aName,
4850 aControllerPort,
4851 aDevice);
4852 if (pAttach.isNull())
4853 return setError(VBOX_E_OBJECT_NOT_FOUND,
4854 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4855 aDevice, aControllerPort, aName.c_str());
4856
4857 /* Remember previously mounted medium. The medium before taking the
4858 * backup is not necessarily the same thing. */
4859 ComObjPtr<Medium> oldmedium;
4860 oldmedium = pAttach->i_getMedium();
4861
4862 IMedium *iM = aMedium;
4863 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4864 if (aMedium && pMedium.isNull())
4865 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4866
4867 AutoCaller mediumCaller(pMedium);
4868 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4869
4870 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4871 if (pMedium)
4872 {
4873 DeviceType_T mediumType = pAttach->i_getType();
4874 switch (mediumType)
4875 {
4876 case DeviceType_DVD:
4877 case DeviceType_Floppy:
4878 break;
4879
4880 default:
4881 return setError(VBOX_E_INVALID_OBJECT_STATE,
4882 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4883 aControllerPort,
4884 aDevice,
4885 aName.c_str());
4886 }
4887 }
4888
4889 i_setModified(IsModified_Storage);
4890 mMediumAttachments.backup();
4891
4892 {
4893 // The backup operation makes the pAttach reference point to the
4894 // old settings. Re-get the correct reference.
4895 pAttach = i_findAttachment(*mMediumAttachments.data(),
4896 aName,
4897 aControllerPort,
4898 aDevice);
4899 if (!oldmedium.isNull())
4900 oldmedium->i_removeBackReference(mData->mUuid);
4901 if (!pMedium.isNull())
4902 {
4903 pMedium->i_addBackReference(mData->mUuid);
4904
4905 mediumLock.release();
4906 multiLock.release();
4907 i_addMediumToRegistry(pMedium);
4908 multiLock.acquire();
4909 mediumLock.acquire();
4910 }
4911
4912 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4913 pAttach->i_updateMedium(pMedium);
4914 }
4915
4916 i_setModified(IsModified_Storage);
4917
4918 mediumLock.release();
4919 multiLock.release();
4920 HRESULT rc = i_onMediumChange(pAttach, aForce);
4921 multiLock.acquire();
4922 mediumLock.acquire();
4923
4924 /* On error roll back this change only. */
4925 if (FAILED(rc))
4926 {
4927 if (!pMedium.isNull())
4928 pMedium->i_removeBackReference(mData->mUuid);
4929 pAttach = i_findAttachment(*mMediumAttachments.data(),
4930 aName,
4931 aControllerPort,
4932 aDevice);
4933 /* If the attachment is gone in the meantime, bail out. */
4934 if (pAttach.isNull())
4935 return rc;
4936 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4937 if (!oldmedium.isNull())
4938 oldmedium->i_addBackReference(mData->mUuid);
4939 pAttach->i_updateMedium(oldmedium);
4940 }
4941
4942 mediumLock.release();
4943 multiLock.release();
4944
4945 /* Save modified registries, but skip this machine as it's the caller's
4946 * job to save its settings like all other settings changes. */
4947 mParent->i_unmarkRegistryModified(i_getId());
4948 mParent->i_saveModifiedRegistries();
4949
4950 return rc;
4951}
4952HRESULT Machine::getMedium(const com::Utf8Str &aName,
4953 LONG aControllerPort,
4954 LONG aDevice,
4955 ComPtr<IMedium> &aMedium)
4956{
4957 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4958 aName.c_str(), aControllerPort, aDevice));
4959
4960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4961
4962 aMedium = NULL;
4963
4964 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4965 aName,
4966 aControllerPort,
4967 aDevice);
4968 if (pAttach.isNull())
4969 return setError(VBOX_E_OBJECT_NOT_FOUND,
4970 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4971 aDevice, aControllerPort, aName.c_str());
4972
4973 aMedium = pAttach->i_getMedium();
4974
4975 return S_OK;
4976}
4977
4978HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4979{
4980 if (aSlot < RT_ELEMENTS(mSerialPorts))
4981 {
4982 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4983 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4984 return S_OK;
4985 }
4986 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4987}
4988
4989HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4990{
4991 if (aSlot < RT_ELEMENTS(mParallelPorts))
4992 {
4993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4994 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4995 return S_OK;
4996 }
4997 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4998}
4999
5000
5001HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
5002{
5003 /* Do not assert if slot is out of range, just return the advertised
5004 status. testdriver/vbox.py triggers this in logVmInfo. */
5005 if (aSlot >= mNetworkAdapters.size())
5006 return setError(E_INVALIDARG,
5007 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
5008 aSlot, mNetworkAdapters.size());
5009
5010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5011
5012 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
5013
5014 return S_OK;
5015}
5016
5017HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
5018{
5019 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5020
5021 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
5022 size_t i = 0;
5023 for (settings::StringsMap::const_iterator
5024 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5025 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5026 ++it, ++i)
5027 aKeys[i] = it->first;
5028
5029 return S_OK;
5030}
5031
5032 /**
5033 * @note Locks this object for reading.
5034 */
5035HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
5036 com::Utf8Str &aValue)
5037{
5038 /* start with nothing found */
5039 aValue = "";
5040
5041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5042
5043 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5044 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5045 // found:
5046 aValue = it->second; // source is a Utf8Str
5047
5048 /* return the result to caller (may be empty) */
5049 return S_OK;
5050}
5051
5052 /**
5053 * @note Locks mParent for writing + this object for writing.
5054 */
5055HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5056{
5057 /* Because control characters in aKey have caused problems in the settings
5058 * they are rejected unless the key should be deleted. */
5059 if (!aValue.isEmpty())
5060 {
5061 for (size_t i = 0; i < aKey.length(); ++i)
5062 {
5063 char ch = aKey[i];
5064 if (RTLocCIsCntrl(ch))
5065 return E_INVALIDARG;
5066 }
5067 }
5068
5069 Utf8Str strOldValue; // empty
5070
5071 // locking note: we only hold the read lock briefly to look up the old value,
5072 // then release it and call the onExtraCanChange callbacks. There is a small
5073 // chance of a race insofar as the callback might be called twice if two callers
5074 // change the same key at the same time, but that's a much better solution
5075 // than the deadlock we had here before. The actual changing of the extradata
5076 // is then performed under the write lock and race-free.
5077
5078 // look up the old value first; if nothing has changed then we need not do anything
5079 {
5080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5081
5082 // For snapshots don't even think about allowing changes, extradata
5083 // is global for a machine, so there is nothing snapshot specific.
5084 if (i_isSnapshotMachine())
5085 return setError(VBOX_E_INVALID_VM_STATE,
5086 tr("Cannot set extradata for a snapshot"));
5087
5088 // check if the right IMachine instance is used
5089 if (mData->mRegistered && !i_isSessionMachine())
5090 return setError(VBOX_E_INVALID_VM_STATE,
5091 tr("Cannot set extradata for an immutable machine"));
5092
5093 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5094 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5095 strOldValue = it->second;
5096 }
5097
5098 bool fChanged;
5099 if ((fChanged = (strOldValue != aValue)))
5100 {
5101 // ask for permission from all listeners outside the locks;
5102 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5103 // lock to copy the list of callbacks to invoke
5104 Bstr bstrError;
5105 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
5106 {
5107 const char *sep = bstrError.isEmpty() ? "" : ": ";
5108 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
5109 return setError(E_ACCESSDENIED,
5110 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5111 aKey.c_str(),
5112 aValue.c_str(),
5113 sep,
5114 bstrError.raw());
5115 }
5116
5117 // data is changing and change not vetoed: then write it out under the lock
5118 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5119
5120 if (aValue.isEmpty())
5121 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5122 else
5123 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5124 // creates a new key if needed
5125
5126 bool fNeedsGlobalSaveSettings = false;
5127 // This saving of settings is tricky: there is no "old state" for the
5128 // extradata items at all (unlike all other settings), so the old/new
5129 // settings comparison would give a wrong result!
5130 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
5131
5132 if (fNeedsGlobalSaveSettings)
5133 {
5134 // save the global settings; for that we should hold only the VirtualBox lock
5135 alock.release();
5136 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5137 mParent->i_saveSettings();
5138 }
5139 }
5140
5141 // fire notification outside the lock
5142 if (fChanged)
5143 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
5144
5145 return S_OK;
5146}
5147
5148HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5149{
5150 aProgress = NULL;
5151 NOREF(aSettingsFilePath);
5152 ReturnComNotImplemented();
5153}
5154
5155HRESULT Machine::saveSettings()
5156{
5157 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5158
5159 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5160 if (FAILED(rc)) return rc;
5161
5162 /* the settings file path may never be null */
5163 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5164
5165 /* save all VM data excluding snapshots */
5166 bool fNeedsGlobalSaveSettings = false;
5167 rc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
5168 mlock.release();
5169
5170 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5171 {
5172 // save the global settings; for that we should hold only the VirtualBox lock
5173 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5174 rc = mParent->i_saveSettings();
5175 }
5176
5177 return rc;
5178}
5179
5180
5181HRESULT Machine::discardSettings()
5182{
5183 /*
5184 * We need to take the machine list lock here as well as the machine one
5185 * or we'll get into trouble should any media stuff require rolling back.
5186 *
5187 * Details:
5188 *
5189 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5190 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5191 * 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]
5192 * 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
5193 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5194 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5195 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5196 * 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
5197 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5198 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5199 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5200 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5201 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5202 * 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]
5203 * 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] (*)
5204 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5205 * 0:005> k
5206 * # Child-SP RetAddr Call Site
5207 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5208 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5209 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5210 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5211 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5212 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5213 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5214 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5215 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5216 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5217 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5218 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5219 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5220 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5221 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5222 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5223 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5224 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5225 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5226 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5227 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5228 *
5229 */
5230 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5231 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5232
5233 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5234 if (FAILED(rc)) return rc;
5235
5236 /*
5237 * during this rollback, the session will be notified if data has
5238 * been actually changed
5239 */
5240 i_rollback(true /* aNotify */);
5241
5242 return S_OK;
5243}
5244
5245/** @note Locks objects! */
5246HRESULT Machine::unregister(AutoCaller &autoCaller,
5247 CleanupMode_T aCleanupMode,
5248 std::vector<ComPtr<IMedium> > &aMedia)
5249{
5250 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5251
5252 Guid id(i_getId());
5253
5254 if (mData->mSession.mState != SessionState_Unlocked)
5255 return setError(VBOX_E_INVALID_OBJECT_STATE,
5256 tr("Cannot unregister the machine '%s' while it is locked"),
5257 mUserData->s.strName.c_str());
5258
5259 // wait for state dependents to drop to zero
5260 i_ensureNoStateDependencies(alock);
5261
5262 if (!mData->mAccessible)
5263 {
5264 // inaccessible machines can only be unregistered; uninitialize ourselves
5265 // here because currently there may be no unregistered that are inaccessible
5266 // (this state combination is not supported). Note releasing the caller and
5267 // leaving the lock before calling uninit()
5268 alock.release();
5269 autoCaller.release();
5270
5271 uninit();
5272
5273 mParent->i_unregisterMachine(this, CleanupMode_UnregisterOnly, id);
5274 // calls VirtualBox::i_saveSettings()
5275
5276 return S_OK;
5277 }
5278
5279 HRESULT rc = S_OK;
5280 mData->llFilesToDelete.clear();
5281
5282 if (!mSSData->strStateFilePath.isEmpty())
5283 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5284
5285 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
5286 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
5287 mData->llFilesToDelete.push_back(strNVRAMFile);
5288
5289 // This list collects the medium objects from all medium attachments
5290 // which we will detach from the machine and its snapshots, in a specific
5291 // order which allows for closing all media without getting "media in use"
5292 // errors, simply by going through the list from the front to the back:
5293 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5294 // and must be closed before the parent media from the snapshots, or closing the parents
5295 // will fail because they still have children);
5296 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5297 // the root ("first") snapshot of the machine.
5298 MediaList llMedia;
5299
5300 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5301 && mMediumAttachments->size()
5302 )
5303 {
5304 // we have media attachments: detach them all and add the Medium objects to our list
5305 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5306 }
5307
5308 if (mData->mFirstSnapshot)
5309 {
5310 // add the media from the medium attachments of the snapshots to
5311 // llMedia as well, after the "main" machine media;
5312 // Snapshot::uninitAll() calls Machine::detachAllMedia() for each
5313 // snapshot machine, depth first.
5314
5315 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5316 MachineState_T oldState = mData->mMachineState;
5317 mData->mMachineState = MachineState_DeletingSnapshot;
5318
5319 // make a copy of the first snapshot reference so the refcount does not
5320 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
5321 // (would hang due to the AutoCaller voodoo)
5322 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5323
5324 // GO!
5325 pFirstSnapshot->i_uninitAll(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5326
5327 mData->mMachineState = oldState;
5328 }
5329
5330 if (FAILED(rc))
5331 {
5332 i_rollbackMedia();
5333 return rc;
5334 }
5335
5336 // commit all the media changes made above
5337 i_commitMedia();
5338
5339 mData->mRegistered = false;
5340
5341 // machine lock no longer needed
5342 alock.release();
5343
5344 /* Make sure that the settings of the current VM are not saved, because
5345 * they are rather crippled at this point to meet the cleanup expectations
5346 * and there's no point destroying the VM config on disk just because. */
5347 mParent->i_unmarkRegistryModified(id);
5348
5349 // return media to caller
5350 aMedia.resize(llMedia.size());
5351 size_t i = 0;
5352 for (MediaList::const_iterator
5353 it = llMedia.begin();
5354 it != llMedia.end();
5355 ++it, ++i)
5356 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5357
5358 mParent->i_unregisterMachine(this, aCleanupMode, id);
5359 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5360
5361 return S_OK;
5362}
5363
5364/**
5365 * Task record for deleting a machine config.
5366 */
5367class Machine::DeleteConfigTask
5368 : public Machine::Task
5369{
5370public:
5371 DeleteConfigTask(Machine *m,
5372 Progress *p,
5373 const Utf8Str &t,
5374 const RTCList<ComPtr<IMedium> > &llMediums,
5375 const StringsList &llFilesToDelete)
5376 : Task(m, p, t),
5377 m_llMediums(llMediums),
5378 m_llFilesToDelete(llFilesToDelete)
5379 {}
5380
5381private:
5382 void handler()
5383 {
5384 try
5385 {
5386 m_pMachine->i_deleteConfigHandler(*this);
5387 }
5388 catch (...)
5389 {
5390 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5391 }
5392 }
5393
5394 RTCList<ComPtr<IMedium> > m_llMediums;
5395 StringsList m_llFilesToDelete;
5396
5397 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5398};
5399
5400/**
5401 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5402 * SessionMachine::taskHandler().
5403 *
5404 * @note Locks this object for writing.
5405 *
5406 * @param task
5407 * @return
5408 */
5409void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5410{
5411 LogFlowThisFuncEnter();
5412
5413 AutoCaller autoCaller(this);
5414 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5415 if (FAILED(autoCaller.rc()))
5416 {
5417 /* we might have been uninitialized because the session was accidentally
5418 * closed by the client, so don't assert */
5419 HRESULT rc = setError(E_FAIL,
5420 tr("The session has been accidentally closed"));
5421 task.m_pProgress->i_notifyComplete(rc);
5422 LogFlowThisFuncLeave();
5423 return;
5424 }
5425
5426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5427
5428 HRESULT rc = S_OK;
5429
5430 try
5431 {
5432 ULONG uLogHistoryCount = 3;
5433 ComPtr<ISystemProperties> systemProperties;
5434 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5435 if (FAILED(rc)) throw rc;
5436
5437 if (!systemProperties.isNull())
5438 {
5439 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5440 if (FAILED(rc)) throw rc;
5441 }
5442
5443 MachineState_T oldState = mData->mMachineState;
5444 i_setMachineState(MachineState_SettingUp);
5445 alock.release();
5446 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5447 {
5448 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5449 {
5450 AutoCaller mac(pMedium);
5451 if (FAILED(mac.rc())) throw mac.rc();
5452 Utf8Str strLocation = pMedium->i_getLocationFull();
5453 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5454 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5455 if (FAILED(rc)) throw rc;
5456 }
5457 if (pMedium->i_isMediumFormatFile())
5458 {
5459 ComPtr<IProgress> pProgress2;
5460 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5461 if (FAILED(rc)) throw rc;
5462 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5463 if (FAILED(rc)) throw rc;
5464 }
5465
5466 /* Close the medium, deliberately without checking the return
5467 * code, and without leaving any trace in the error info, as
5468 * a failure here is a very minor issue, which shouldn't happen
5469 * as above we even managed to delete the medium. */
5470 {
5471 ErrorInfoKeeper eik;
5472 pMedium->Close();
5473 }
5474 }
5475 i_setMachineState(oldState);
5476 alock.acquire();
5477
5478 // delete the files pushed on the task list by Machine::Delete()
5479 // (this includes saved states of the machine and snapshots and
5480 // medium storage files from the IMedium list passed in, and the
5481 // machine XML file)
5482 for (StringsList::const_iterator
5483 it = task.m_llFilesToDelete.begin();
5484 it != task.m_llFilesToDelete.end();
5485 ++it)
5486 {
5487 const Utf8Str &strFile = *it;
5488 LogFunc(("Deleting file %s\n", strFile.c_str()));
5489 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5490 if (FAILED(rc)) throw rc;
5491
5492 int vrc = RTFileDelete(strFile.c_str());
5493 if (RT_FAILURE(vrc))
5494 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5495 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5496 }
5497
5498 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5499 if (FAILED(rc)) throw rc;
5500
5501 /* delete the settings only when the file actually exists */
5502 if (mData->pMachineConfigFile->fileExists())
5503 {
5504 /* Delete any backup or uncommitted XML files. Ignore failures.
5505 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5506 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5507 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5508 RTFileDelete(otherXml.c_str());
5509 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5510 RTFileDelete(otherXml.c_str());
5511
5512 /* delete the Logs folder, nothing important should be left
5513 * there (we don't check for errors because the user might have
5514 * some private files there that we don't want to delete) */
5515 Utf8Str logFolder;
5516 getLogFolder(logFolder);
5517 Assert(logFolder.length());
5518 if (RTDirExists(logFolder.c_str()))
5519 {
5520 /* Delete all VBox.log[.N] files from the Logs folder
5521 * (this must be in sync with the rotation logic in
5522 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5523 * files that may have been created by the GUI. */
5524 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5525 RTFileDelete(log.c_str());
5526 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5527 RTFileDelete(log.c_str());
5528 for (ULONG i = uLogHistoryCount; i > 0; i--)
5529 {
5530 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5531 RTFileDelete(log.c_str());
5532 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5533 RTFileDelete(log.c_str());
5534 }
5535 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5536 RTFileDelete(log.c_str());
5537#if defined(RT_OS_WINDOWS)
5538 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5539 RTFileDelete(log.c_str());
5540 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5541 RTFileDelete(log.c_str());
5542#endif
5543
5544 RTDirRemove(logFolder.c_str());
5545 }
5546
5547 /* delete the Snapshots folder, nothing important should be left
5548 * there (we don't check for errors because the user might have
5549 * some private files there that we don't want to delete) */
5550 Utf8Str strFullSnapshotFolder;
5551 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5552 Assert(!strFullSnapshotFolder.isEmpty());
5553 if (RTDirExists(strFullSnapshotFolder.c_str()))
5554 RTDirRemove(strFullSnapshotFolder.c_str());
5555
5556 // delete the directory that contains the settings file, but only
5557 // if it matches the VM name
5558 Utf8Str settingsDir;
5559 if (i_isInOwnDir(&settingsDir))
5560 RTDirRemove(settingsDir.c_str());
5561 }
5562
5563 alock.release();
5564
5565 mParent->i_saveModifiedRegistries();
5566 }
5567 catch (HRESULT aRC) { rc = aRC; }
5568
5569 task.m_pProgress->i_notifyComplete(rc);
5570
5571 LogFlowThisFuncLeave();
5572}
5573
5574HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5575{
5576 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5577
5578 HRESULT rc = i_checkStateDependency(MutableStateDep);
5579 if (FAILED(rc)) return rc;
5580
5581 if (mData->mRegistered)
5582 return setError(VBOX_E_INVALID_VM_STATE,
5583 tr("Cannot delete settings of a registered machine"));
5584
5585 // collect files to delete
5586 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5587 // machine config file
5588 if (mData->pMachineConfigFile->fileExists())
5589 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5590 // backup of machine config file
5591 Utf8Str strTmp(mData->m_strConfigFileFull);
5592 strTmp.append("-prev");
5593 if (RTFileExists(strTmp.c_str()))
5594 llFilesToDelete.push_back(strTmp);
5595
5596 RTCList<ComPtr<IMedium> > llMediums;
5597 for (size_t i = 0; i < aMedia.size(); ++i)
5598 {
5599 IMedium *pIMedium(aMedia[i]);
5600 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5601 if (pMedium.isNull())
5602 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
5603 SafeArray<BSTR> ids;
5604 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5605 if (FAILED(rc)) return rc;
5606 /* At this point the medium should not have any back references
5607 * anymore. If it has it is attached to another VM and *must* not
5608 * deleted. */
5609 if (ids.size() < 1)
5610 llMediums.append(pMedium);
5611 }
5612
5613 ComObjPtr<Progress> pProgress;
5614 pProgress.createObject();
5615 rc = pProgress->init(i_getVirtualBox(),
5616 static_cast<IMachine*>(this) /* aInitiator */,
5617 tr("Deleting files"),
5618 true /* fCancellable */,
5619 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5620 tr("Collecting file inventory"));
5621 if (FAILED(rc))
5622 return rc;
5623
5624 /* create and start the task on a separate thread (note that it will not
5625 * start working until we release alock) */
5626 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5627 rc = pTask->createThread();
5628 pTask = NULL;
5629 if (FAILED(rc))
5630 return rc;
5631
5632 pProgress.queryInterfaceTo(aProgress.asOutParam());
5633
5634 LogFlowFuncLeave();
5635
5636 return S_OK;
5637}
5638
5639HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5640{
5641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5642
5643 ComObjPtr<Snapshot> pSnapshot;
5644 HRESULT rc;
5645
5646 if (aNameOrId.isEmpty())
5647 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5648 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5649 else
5650 {
5651 Guid uuid(aNameOrId);
5652 if (uuid.isValid())
5653 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5654 else
5655 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5656 }
5657 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5658
5659 return rc;
5660}
5661
5662HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5663 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5664{
5665 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5666
5667 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5668 if (FAILED(rc)) return rc;
5669
5670 ComObjPtr<SharedFolder> sharedFolder;
5671 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5672 if (SUCCEEDED(rc))
5673 return setError(VBOX_E_OBJECT_IN_USE,
5674 tr("Shared folder named '%s' already exists"),
5675 aName.c_str());
5676
5677 sharedFolder.createObject();
5678 rc = sharedFolder->init(i_getMachine(),
5679 aName,
5680 aHostPath,
5681 !!aWritable,
5682 !!aAutomount,
5683 aAutoMountPoint,
5684 true /* fFailOnError */);
5685 if (FAILED(rc)) return rc;
5686
5687 i_setModified(IsModified_SharedFolders);
5688 mHWData.backup();
5689 mHWData->mSharedFolders.push_back(sharedFolder);
5690
5691 /* inform the direct session if any */
5692 alock.release();
5693 i_onSharedFolderChange();
5694
5695 return S_OK;
5696}
5697
5698HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5699{
5700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5701
5702 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5703 if (FAILED(rc)) return rc;
5704
5705 ComObjPtr<SharedFolder> sharedFolder;
5706 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5707 if (FAILED(rc)) return rc;
5708
5709 i_setModified(IsModified_SharedFolders);
5710 mHWData.backup();
5711 mHWData->mSharedFolders.remove(sharedFolder);
5712
5713 /* inform the direct session if any */
5714 alock.release();
5715 i_onSharedFolderChange();
5716
5717 return S_OK;
5718}
5719
5720HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5721{
5722 /* start with No */
5723 *aCanShow = FALSE;
5724
5725 ComPtr<IInternalSessionControl> directControl;
5726 {
5727 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5728
5729 if (mData->mSession.mState != SessionState_Locked)
5730 return setError(VBOX_E_INVALID_VM_STATE,
5731 tr("Machine is not locked for session (session state: %s)"),
5732 Global::stringifySessionState(mData->mSession.mState));
5733
5734 if (mData->mSession.mLockType == LockType_VM)
5735 directControl = mData->mSession.mDirectControl;
5736 }
5737
5738 /* ignore calls made after #OnSessionEnd() is called */
5739 if (!directControl)
5740 return S_OK;
5741
5742 LONG64 dummy;
5743 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5744}
5745
5746HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5747{
5748 ComPtr<IInternalSessionControl> directControl;
5749 {
5750 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5751
5752 if (mData->mSession.mState != SessionState_Locked)
5753 return setError(E_FAIL,
5754 tr("Machine is not locked for session (session state: %s)"),
5755 Global::stringifySessionState(mData->mSession.mState));
5756
5757 if (mData->mSession.mLockType == LockType_VM)
5758 directControl = mData->mSession.mDirectControl;
5759 }
5760
5761 /* ignore calls made after #OnSessionEnd() is called */
5762 if (!directControl)
5763 return S_OK;
5764
5765 BOOL dummy;
5766 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5767}
5768
5769#ifdef VBOX_WITH_GUEST_PROPS
5770/**
5771 * Look up a guest property in VBoxSVC's internal structures.
5772 */
5773HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5774 com::Utf8Str &aValue,
5775 LONG64 *aTimestamp,
5776 com::Utf8Str &aFlags) const
5777{
5778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5779
5780 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5781 if (it != mHWData->mGuestProperties.end())
5782 {
5783 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5784 aValue = it->second.strValue;
5785 *aTimestamp = it->second.mTimestamp;
5786 GuestPropWriteFlags(it->second.mFlags, szFlags);
5787 aFlags = Utf8Str(szFlags);
5788 }
5789
5790 return S_OK;
5791}
5792
5793/**
5794 * Query the VM that a guest property belongs to for the property.
5795 * @returns E_ACCESSDENIED if the VM process is not available or not
5796 * currently handling queries and the lookup should then be done in
5797 * VBoxSVC.
5798 */
5799HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5800 com::Utf8Str &aValue,
5801 LONG64 *aTimestamp,
5802 com::Utf8Str &aFlags) const
5803{
5804 HRESULT rc = S_OK;
5805 Bstr bstrValue;
5806 Bstr bstrFlags;
5807
5808 ComPtr<IInternalSessionControl> directControl;
5809 {
5810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5811 if (mData->mSession.mLockType == LockType_VM)
5812 directControl = mData->mSession.mDirectControl;
5813 }
5814
5815 /* ignore calls made after #OnSessionEnd() is called */
5816 if (!directControl)
5817 rc = E_ACCESSDENIED;
5818 else
5819 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5820 0 /* accessMode */,
5821 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5822
5823 aValue = bstrValue;
5824 aFlags = bstrFlags;
5825
5826 return rc;
5827}
5828#endif // VBOX_WITH_GUEST_PROPS
5829
5830HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5831 com::Utf8Str &aValue,
5832 LONG64 *aTimestamp,
5833 com::Utf8Str &aFlags)
5834{
5835#ifndef VBOX_WITH_GUEST_PROPS
5836 ReturnComNotImplemented();
5837#else // VBOX_WITH_GUEST_PROPS
5838
5839 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5840
5841 if (rc == E_ACCESSDENIED)
5842 /* The VM is not running or the service is not (yet) accessible */
5843 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5844 return rc;
5845#endif // VBOX_WITH_GUEST_PROPS
5846}
5847
5848HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5849{
5850 LONG64 dummyTimestamp;
5851 com::Utf8Str dummyFlags;
5852 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5853 return rc;
5854
5855}
5856HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5857{
5858 com::Utf8Str dummyFlags;
5859 com::Utf8Str dummyValue;
5860 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5861 return rc;
5862}
5863
5864#ifdef VBOX_WITH_GUEST_PROPS
5865/**
5866 * Set a guest property in VBoxSVC's internal structures.
5867 */
5868HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5869 const com::Utf8Str &aFlags, bool fDelete)
5870{
5871 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5872 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5873 if (FAILED(rc)) return rc;
5874
5875 try
5876 {
5877 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5878 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5879 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5880
5881 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
5882 return setError(E_INVALIDARG, tr("Properties with TRANSIENT or TRANSRESET flag cannot be set or modified if VM is not running"));
5883
5884 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5885 if (it == mHWData->mGuestProperties.end())
5886 {
5887 if (!fDelete)
5888 {
5889 i_setModified(IsModified_MachineData);
5890 mHWData.backupEx();
5891
5892 RTTIMESPEC time;
5893 HWData::GuestProperty prop;
5894 prop.strValue = aValue;
5895 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5896 prop.mFlags = fFlags;
5897 mHWData->mGuestProperties[aName] = prop;
5898 }
5899 }
5900 else
5901 {
5902 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5903 {
5904 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5905 }
5906 else
5907 {
5908 i_setModified(IsModified_MachineData);
5909 mHWData.backupEx();
5910
5911 /* The backupEx() operation invalidates our iterator,
5912 * so get a new one. */
5913 it = mHWData->mGuestProperties.find(aName);
5914 Assert(it != mHWData->mGuestProperties.end());
5915
5916 if (!fDelete)
5917 {
5918 RTTIMESPEC time;
5919 it->second.strValue = aValue;
5920 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5921 it->second.mFlags = fFlags;
5922 }
5923 else
5924 mHWData->mGuestProperties.erase(it);
5925 }
5926 }
5927
5928 if (SUCCEEDED(rc))
5929 {
5930 alock.release();
5931
5932 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fDelete);
5933 }
5934 }
5935 catch (std::bad_alloc &)
5936 {
5937 rc = E_OUTOFMEMORY;
5938 }
5939
5940 return rc;
5941}
5942
5943/**
5944 * Set a property on the VM that that property belongs to.
5945 * @returns E_ACCESSDENIED if the VM process is not available or not
5946 * currently handling queries and the setting should then be done in
5947 * VBoxSVC.
5948 */
5949HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5950 const com::Utf8Str &aFlags, bool fDelete)
5951{
5952 HRESULT rc;
5953
5954 try
5955 {
5956 ComPtr<IInternalSessionControl> directControl;
5957 {
5958 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5959 if (mData->mSession.mLockType == LockType_VM)
5960 directControl = mData->mSession.mDirectControl;
5961 }
5962
5963 Bstr dummy1; /* will not be changed (setter) */
5964 Bstr dummy2; /* will not be changed (setter) */
5965 LONG64 dummy64;
5966 if (!directControl)
5967 rc = E_ACCESSDENIED;
5968 else
5969 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5970 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5971 fDelete ? 2 : 1 /* accessMode */,
5972 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5973 }
5974 catch (std::bad_alloc &)
5975 {
5976 rc = E_OUTOFMEMORY;
5977 }
5978
5979 return rc;
5980}
5981#endif // VBOX_WITH_GUEST_PROPS
5982
5983HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5984 const com::Utf8Str &aFlags)
5985{
5986#ifndef VBOX_WITH_GUEST_PROPS
5987 ReturnComNotImplemented();
5988#else // VBOX_WITH_GUEST_PROPS
5989 AssertReturn(RT_SUCCESS(GuestPropValidateName(aProperty.c_str(), (uint32_t)aProperty.length() + 1 /* '\0' */)), E_INVALIDARG);
5990 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValue.c_str(), (uint32_t)aValue.length() + 1 /* '\0' */)), E_INVALIDARG);
5991
5992 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5993 if (rc == E_ACCESSDENIED)
5994 /* The VM is not running or the service is not (yet) accessible */
5995 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5996 return rc;
5997#endif // VBOX_WITH_GUEST_PROPS
5998}
5999
6000HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
6001{
6002 return setGuestProperty(aProperty, aValue, "");
6003}
6004
6005HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
6006{
6007#ifndef VBOX_WITH_GUEST_PROPS
6008 ReturnComNotImplemented();
6009#else // VBOX_WITH_GUEST_PROPS
6010 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
6011 if (rc == E_ACCESSDENIED)
6012 /* The VM is not running or the service is not (yet) accessible */
6013 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
6014 return rc;
6015#endif // VBOX_WITH_GUEST_PROPS
6016}
6017
6018#ifdef VBOX_WITH_GUEST_PROPS
6019/**
6020 * Enumerate the guest properties in VBoxSVC's internal structures.
6021 */
6022HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
6023 std::vector<com::Utf8Str> &aNames,
6024 std::vector<com::Utf8Str> &aValues,
6025 std::vector<LONG64> &aTimestamps,
6026 std::vector<com::Utf8Str> &aFlags)
6027{
6028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6029 Utf8Str strPatterns(aPatterns);
6030
6031 /*
6032 * Look for matching patterns and build up a list.
6033 */
6034 HWData::GuestPropertyMap propMap;
6035 for (HWData::GuestPropertyMap::const_iterator
6036 it = mHWData->mGuestProperties.begin();
6037 it != mHWData->mGuestProperties.end();
6038 ++it)
6039 {
6040 if ( strPatterns.isEmpty()
6041 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6042 RTSTR_MAX,
6043 it->first.c_str(),
6044 RTSTR_MAX,
6045 NULL)
6046 )
6047 propMap.insert(*it);
6048 }
6049
6050 alock.release();
6051
6052 /*
6053 * And build up the arrays for returning the property information.
6054 */
6055 size_t cEntries = propMap.size();
6056
6057 aNames.resize(cEntries);
6058 aValues.resize(cEntries);
6059 aTimestamps.resize(cEntries);
6060 aFlags.resize(cEntries);
6061
6062 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6063 size_t i = 0;
6064 for (HWData::GuestPropertyMap::const_iterator
6065 it = propMap.begin();
6066 it != propMap.end();
6067 ++it, ++i)
6068 {
6069 aNames[i] = it->first;
6070 aValues[i] = it->second.strValue;
6071 aTimestamps[i] = it->second.mTimestamp;
6072 GuestPropWriteFlags(it->second.mFlags, szFlags);
6073 aFlags[i] = Utf8Str(szFlags);
6074
6075 AssertReturn(RT_SUCCESS(GuestPropValidateName(aNames[i].c_str(), (uint32_t)aNames[i].length() + 1 /* '\0' */)), E_INVALIDARG);
6076 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValues[i].c_str(), (uint32_t)aValues[i].length() + 1 /* '\0' */)), E_INVALIDARG);
6077 }
6078
6079 return S_OK;
6080}
6081
6082/**
6083 * Enumerate the properties managed by a VM.
6084 * @returns E_ACCESSDENIED if the VM process is not available or not
6085 * currently handling queries and the setting should then be done in
6086 * VBoxSVC.
6087 */
6088HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6089 std::vector<com::Utf8Str> &aNames,
6090 std::vector<com::Utf8Str> &aValues,
6091 std::vector<LONG64> &aTimestamps,
6092 std::vector<com::Utf8Str> &aFlags)
6093{
6094 HRESULT rc;
6095 ComPtr<IInternalSessionControl> directControl;
6096 {
6097 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6098 if (mData->mSession.mLockType == LockType_VM)
6099 directControl = mData->mSession.mDirectControl;
6100 }
6101
6102 com::SafeArray<BSTR> bNames;
6103 com::SafeArray<BSTR> bValues;
6104 com::SafeArray<LONG64> bTimestamps;
6105 com::SafeArray<BSTR> bFlags;
6106
6107 if (!directControl)
6108 rc = E_ACCESSDENIED;
6109 else
6110 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6111 ComSafeArrayAsOutParam(bNames),
6112 ComSafeArrayAsOutParam(bValues),
6113 ComSafeArrayAsOutParam(bTimestamps),
6114 ComSafeArrayAsOutParam(bFlags));
6115 size_t i;
6116 aNames.resize(bNames.size());
6117 for (i = 0; i < bNames.size(); ++i)
6118 aNames[i] = Utf8Str(bNames[i]);
6119 aValues.resize(bValues.size());
6120 for (i = 0; i < bValues.size(); ++i)
6121 aValues[i] = Utf8Str(bValues[i]);
6122 aTimestamps.resize(bTimestamps.size());
6123 for (i = 0; i < bTimestamps.size(); ++i)
6124 aTimestamps[i] = bTimestamps[i];
6125 aFlags.resize(bFlags.size());
6126 for (i = 0; i < bFlags.size(); ++i)
6127 aFlags[i] = Utf8Str(bFlags[i]);
6128
6129 return rc;
6130}
6131#endif // VBOX_WITH_GUEST_PROPS
6132HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6133 std::vector<com::Utf8Str> &aNames,
6134 std::vector<com::Utf8Str> &aValues,
6135 std::vector<LONG64> &aTimestamps,
6136 std::vector<com::Utf8Str> &aFlags)
6137{
6138#ifndef VBOX_WITH_GUEST_PROPS
6139 ReturnComNotImplemented();
6140#else // VBOX_WITH_GUEST_PROPS
6141
6142 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6143
6144 if (rc == E_ACCESSDENIED)
6145 /* The VM is not running or the service is not (yet) accessible */
6146 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6147 return rc;
6148#endif // VBOX_WITH_GUEST_PROPS
6149}
6150
6151HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6152 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6153{
6154 MediumAttachmentList atts;
6155
6156 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6157 if (FAILED(rc)) return rc;
6158
6159 aMediumAttachments.resize(atts.size());
6160 size_t i = 0;
6161 for (MediumAttachmentList::const_iterator
6162 it = atts.begin();
6163 it != atts.end();
6164 ++it, ++i)
6165 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6166
6167 return S_OK;
6168}
6169
6170HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6171 LONG aControllerPort,
6172 LONG aDevice,
6173 ComPtr<IMediumAttachment> &aAttachment)
6174{
6175 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6176 aName.c_str(), aControllerPort, aDevice));
6177
6178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6179
6180 aAttachment = NULL;
6181
6182 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6183 aName,
6184 aControllerPort,
6185 aDevice);
6186 if (pAttach.isNull())
6187 return setError(VBOX_E_OBJECT_NOT_FOUND,
6188 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6189 aDevice, aControllerPort, aName.c_str());
6190
6191 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6192
6193 return S_OK;
6194}
6195
6196
6197HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6198 StorageBus_T aConnectionType,
6199 ComPtr<IStorageController> &aController)
6200{
6201 if ( (aConnectionType <= StorageBus_Null)
6202 || (aConnectionType > StorageBus_VirtioSCSI))
6203 return setError(E_INVALIDARG,
6204 tr("Invalid connection type: %d"),
6205 aConnectionType);
6206
6207 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6208
6209 HRESULT rc = i_checkStateDependency(MutableStateDep);
6210 if (FAILED(rc)) return rc;
6211
6212 /* try to find one with the name first. */
6213 ComObjPtr<StorageController> ctrl;
6214
6215 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6216 if (SUCCEEDED(rc))
6217 return setError(VBOX_E_OBJECT_IN_USE,
6218 tr("Storage controller named '%s' already exists"),
6219 aName.c_str());
6220
6221 ctrl.createObject();
6222
6223 /* get a new instance number for the storage controller */
6224 ULONG ulInstance = 0;
6225 bool fBootable = true;
6226 for (StorageControllerList::const_iterator
6227 it = mStorageControllers->begin();
6228 it != mStorageControllers->end();
6229 ++it)
6230 {
6231 if ((*it)->i_getStorageBus() == aConnectionType)
6232 {
6233 ULONG ulCurInst = (*it)->i_getInstance();
6234
6235 if (ulCurInst >= ulInstance)
6236 ulInstance = ulCurInst + 1;
6237
6238 /* Only one controller of each type can be marked as bootable. */
6239 if ((*it)->i_getBootable())
6240 fBootable = false;
6241 }
6242 }
6243
6244 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6245 if (FAILED(rc)) return rc;
6246
6247 i_setModified(IsModified_Storage);
6248 mStorageControllers.backup();
6249 mStorageControllers->push_back(ctrl);
6250
6251 ctrl.queryInterfaceTo(aController.asOutParam());
6252
6253 /* inform the direct session if any */
6254 alock.release();
6255 i_onStorageControllerChange(i_getId(), aName);
6256
6257 return S_OK;
6258}
6259
6260HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6261 ComPtr<IStorageController> &aStorageController)
6262{
6263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6264
6265 ComObjPtr<StorageController> ctrl;
6266
6267 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6268 if (SUCCEEDED(rc))
6269 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6270
6271 return rc;
6272}
6273
6274HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6275 ULONG aInstance,
6276 ComPtr<IStorageController> &aStorageController)
6277{
6278 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6279
6280 for (StorageControllerList::const_iterator
6281 it = mStorageControllers->begin();
6282 it != mStorageControllers->end();
6283 ++it)
6284 {
6285 if ( (*it)->i_getStorageBus() == aConnectionType
6286 && (*it)->i_getInstance() == aInstance)
6287 {
6288 (*it).queryInterfaceTo(aStorageController.asOutParam());
6289 return S_OK;
6290 }
6291 }
6292
6293 return setError(VBOX_E_OBJECT_NOT_FOUND,
6294 tr("Could not find a storage controller with instance number '%lu'"),
6295 aInstance);
6296}
6297
6298HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6299{
6300 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6301
6302 HRESULT rc = i_checkStateDependency(MutableStateDep);
6303 if (FAILED(rc)) return rc;
6304
6305 ComObjPtr<StorageController> ctrl;
6306
6307 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6308 if (SUCCEEDED(rc))
6309 {
6310 /* Ensure that only one controller of each type is marked as bootable. */
6311 if (aBootable == TRUE)
6312 {
6313 for (StorageControllerList::const_iterator
6314 it = mStorageControllers->begin();
6315 it != mStorageControllers->end();
6316 ++it)
6317 {
6318 ComObjPtr<StorageController> aCtrl = (*it);
6319
6320 if ( (aCtrl->i_getName() != aName)
6321 && aCtrl->i_getBootable() == TRUE
6322 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6323 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6324 {
6325 aCtrl->i_setBootable(FALSE);
6326 break;
6327 }
6328 }
6329 }
6330
6331 if (SUCCEEDED(rc))
6332 {
6333 ctrl->i_setBootable(aBootable);
6334 i_setModified(IsModified_Storage);
6335 }
6336 }
6337
6338 if (SUCCEEDED(rc))
6339 {
6340 /* inform the direct session if any */
6341 alock.release();
6342 i_onStorageControllerChange(i_getId(), aName);
6343 }
6344
6345 return rc;
6346}
6347
6348HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6349{
6350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6351
6352 HRESULT rc = i_checkStateDependency(MutableStateDep);
6353 if (FAILED(rc)) return rc;
6354
6355 ComObjPtr<StorageController> ctrl;
6356 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6357 if (FAILED(rc)) return rc;
6358
6359 MediumAttachmentList llDetachedAttachments;
6360 {
6361 /* find all attached devices to the appropriate storage controller and detach them all */
6362 // make a temporary list because detachDevice invalidates iterators into
6363 // mMediumAttachments
6364 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6365
6366 for (MediumAttachmentList::const_iterator
6367 it = llAttachments2.begin();
6368 it != llAttachments2.end();
6369 ++it)
6370 {
6371 MediumAttachment *pAttachTemp = *it;
6372
6373 AutoCaller localAutoCaller(pAttachTemp);
6374 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6375
6376 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6377
6378 if (pAttachTemp->i_getControllerName() == aName)
6379 {
6380 llDetachedAttachments.push_back(pAttachTemp);
6381 rc = i_detachDevice(pAttachTemp, alock, NULL);
6382 if (FAILED(rc)) return rc;
6383 }
6384 }
6385 }
6386
6387 /* send event about detached devices before removing parent controller */
6388 for (MediumAttachmentList::const_iterator
6389 it = llDetachedAttachments.begin();
6390 it != llDetachedAttachments.end();
6391 ++it)
6392 {
6393 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6394 }
6395
6396 /* We can remove it now. */
6397 i_setModified(IsModified_Storage);
6398 mStorageControllers.backup();
6399
6400 ctrl->i_unshare();
6401
6402 mStorageControllers->remove(ctrl);
6403
6404 /* inform the direct session if any */
6405 alock.release();
6406 i_onStorageControllerChange(i_getId(), aName);
6407
6408 return S_OK;
6409}
6410
6411HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6412 ComPtr<IUSBController> &aController)
6413{
6414 if ( (aType <= USBControllerType_Null)
6415 || (aType >= USBControllerType_Last))
6416 return setError(E_INVALIDARG,
6417 tr("Invalid USB controller type: %d"),
6418 aType);
6419
6420 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6421
6422 HRESULT rc = i_checkStateDependency(MutableStateDep);
6423 if (FAILED(rc)) return rc;
6424
6425 /* try to find one with the same type first. */
6426 ComObjPtr<USBController> ctrl;
6427
6428 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6429 if (SUCCEEDED(rc))
6430 return setError(VBOX_E_OBJECT_IN_USE,
6431 tr("USB controller named '%s' already exists"),
6432 aName.c_str());
6433
6434 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6435 ULONG maxInstances;
6436 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6437 if (FAILED(rc))
6438 return rc;
6439
6440 ULONG cInstances = i_getUSBControllerCountByType(aType);
6441 if (cInstances >= maxInstances)
6442 return setError(E_INVALIDARG,
6443 tr("Too many USB controllers of this type"));
6444
6445 ctrl.createObject();
6446
6447 rc = ctrl->init(this, aName, aType);
6448 if (FAILED(rc)) return rc;
6449
6450 i_setModified(IsModified_USB);
6451 mUSBControllers.backup();
6452 mUSBControllers->push_back(ctrl);
6453
6454 ctrl.queryInterfaceTo(aController.asOutParam());
6455
6456 /* inform the direct session if any */
6457 alock.release();
6458 i_onUSBControllerChange();
6459
6460 return S_OK;
6461}
6462
6463HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6464{
6465 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6466
6467 ComObjPtr<USBController> ctrl;
6468
6469 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6470 if (SUCCEEDED(rc))
6471 ctrl.queryInterfaceTo(aController.asOutParam());
6472
6473 return rc;
6474}
6475
6476HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6477 ULONG *aControllers)
6478{
6479 if ( (aType <= USBControllerType_Null)
6480 || (aType >= USBControllerType_Last))
6481 return setError(E_INVALIDARG,
6482 tr("Invalid USB controller type: %d"),
6483 aType);
6484
6485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6486
6487 ComObjPtr<USBController> ctrl;
6488
6489 *aControllers = i_getUSBControllerCountByType(aType);
6490
6491 return S_OK;
6492}
6493
6494HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6495{
6496
6497 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6498
6499 HRESULT rc = i_checkStateDependency(MutableStateDep);
6500 if (FAILED(rc)) return rc;
6501
6502 ComObjPtr<USBController> ctrl;
6503 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6504 if (FAILED(rc)) return rc;
6505
6506 i_setModified(IsModified_USB);
6507 mUSBControllers.backup();
6508
6509 ctrl->i_unshare();
6510
6511 mUSBControllers->remove(ctrl);
6512
6513 /* inform the direct session if any */
6514 alock.release();
6515 i_onUSBControllerChange();
6516
6517 return S_OK;
6518}
6519
6520HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6521 ULONG *aOriginX,
6522 ULONG *aOriginY,
6523 ULONG *aWidth,
6524 ULONG *aHeight,
6525 BOOL *aEnabled)
6526{
6527 uint32_t u32OriginX= 0;
6528 uint32_t u32OriginY= 0;
6529 uint32_t u32Width = 0;
6530 uint32_t u32Height = 0;
6531 uint16_t u16Flags = 0;
6532
6533#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6534 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6535#else
6536 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6537#endif
6538 int vrc = readSavedGuestScreenInfo(SavedStateStream, mSSData->strStateFilePath, aScreenId,
6539 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6540 if (RT_FAILURE(vrc))
6541 {
6542#ifdef RT_OS_WINDOWS
6543 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6544 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6545 * So just assign fEnable to TRUE again.
6546 * The right fix would be to change GUI API wrappers to make sure that parameters
6547 * are changed only if API succeeds.
6548 */
6549 *aEnabled = TRUE;
6550#endif
6551 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6552 tr("Saved guest size is not available (%Rrc)"),
6553 vrc);
6554 }
6555
6556 *aOriginX = u32OriginX;
6557 *aOriginY = u32OriginY;
6558 *aWidth = u32Width;
6559 *aHeight = u32Height;
6560 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6561
6562 return S_OK;
6563}
6564
6565HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6566 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6567{
6568 if (aScreenId != 0)
6569 return E_NOTIMPL;
6570
6571 if ( aBitmapFormat != BitmapFormat_BGR0
6572 && aBitmapFormat != BitmapFormat_BGRA
6573 && aBitmapFormat != BitmapFormat_RGBA
6574 && aBitmapFormat != BitmapFormat_PNG)
6575 return setError(E_NOTIMPL,
6576 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6577
6578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6579
6580 uint8_t *pu8Data = NULL;
6581 uint32_t cbData = 0;
6582 uint32_t u32Width = 0;
6583 uint32_t u32Height = 0;
6584
6585#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6586 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6587#else
6588 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6589#endif
6590 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 0 /* u32Type */,
6591 &pu8Data, &cbData, &u32Width, &u32Height);
6592 if (RT_FAILURE(vrc))
6593 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6594 tr("Saved thumbnail data is not available (%Rrc)"),
6595 vrc);
6596
6597 HRESULT hr = S_OK;
6598
6599 *aWidth = u32Width;
6600 *aHeight = u32Height;
6601
6602 if (cbData > 0)
6603 {
6604 /* Convert pixels to the format expected by the API caller. */
6605 if (aBitmapFormat == BitmapFormat_BGR0)
6606 {
6607 /* [0] B, [1] G, [2] R, [3] 0. */
6608 aData.resize(cbData);
6609 memcpy(&aData.front(), pu8Data, cbData);
6610 }
6611 else if (aBitmapFormat == BitmapFormat_BGRA)
6612 {
6613 /* [0] B, [1] G, [2] R, [3] A. */
6614 aData.resize(cbData);
6615 for (uint32_t i = 0; i < cbData; i += 4)
6616 {
6617 aData[i] = pu8Data[i];
6618 aData[i + 1] = pu8Data[i + 1];
6619 aData[i + 2] = pu8Data[i + 2];
6620 aData[i + 3] = 0xff;
6621 }
6622 }
6623 else if (aBitmapFormat == BitmapFormat_RGBA)
6624 {
6625 /* [0] R, [1] G, [2] B, [3] A. */
6626 aData.resize(cbData);
6627 for (uint32_t i = 0; i < cbData; i += 4)
6628 {
6629 aData[i] = pu8Data[i + 2];
6630 aData[i + 1] = pu8Data[i + 1];
6631 aData[i + 2] = pu8Data[i];
6632 aData[i + 3] = 0xff;
6633 }
6634 }
6635 else if (aBitmapFormat == BitmapFormat_PNG)
6636 {
6637 uint8_t *pu8PNG = NULL;
6638 uint32_t cbPNG = 0;
6639 uint32_t cxPNG = 0;
6640 uint32_t cyPNG = 0;
6641
6642 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6643
6644 if (RT_SUCCESS(vrc))
6645 {
6646 aData.resize(cbPNG);
6647 if (cbPNG)
6648 memcpy(&aData.front(), pu8PNG, cbPNG);
6649 }
6650 else
6651 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6652 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6653 vrc);
6654
6655 RTMemFree(pu8PNG);
6656 }
6657 }
6658
6659 freeSavedDisplayScreenshot(pu8Data);
6660
6661 return hr;
6662}
6663
6664HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6665 ULONG *aWidth,
6666 ULONG *aHeight,
6667 std::vector<BitmapFormat_T> &aBitmapFormats)
6668{
6669 if (aScreenId != 0)
6670 return E_NOTIMPL;
6671
6672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6673
6674 uint8_t *pu8Data = NULL;
6675 uint32_t cbData = 0;
6676 uint32_t u32Width = 0;
6677 uint32_t u32Height = 0;
6678
6679#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6680 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6681#else
6682 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6683#endif
6684 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6685 &pu8Data, &cbData, &u32Width, &u32Height);
6686
6687 if (RT_FAILURE(vrc))
6688 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6689 tr("Saved screenshot data is not available (%Rrc)"),
6690 vrc);
6691
6692 *aWidth = u32Width;
6693 *aHeight = u32Height;
6694 aBitmapFormats.resize(1);
6695 aBitmapFormats[0] = BitmapFormat_PNG;
6696
6697 freeSavedDisplayScreenshot(pu8Data);
6698
6699 return S_OK;
6700}
6701
6702HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6703 BitmapFormat_T aBitmapFormat,
6704 ULONG *aWidth,
6705 ULONG *aHeight,
6706 std::vector<BYTE> &aData)
6707{
6708 if (aScreenId != 0)
6709 return E_NOTIMPL;
6710
6711 if (aBitmapFormat != BitmapFormat_PNG)
6712 return E_NOTIMPL;
6713
6714 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6715
6716 uint8_t *pu8Data = NULL;
6717 uint32_t cbData = 0;
6718 uint32_t u32Width = 0;
6719 uint32_t u32Height = 0;
6720
6721#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6722 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6723#else
6724 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6725#endif
6726 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6727 &pu8Data, &cbData, &u32Width, &u32Height);
6728
6729 if (RT_FAILURE(vrc))
6730 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6731 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6732 vrc);
6733
6734 *aWidth = u32Width;
6735 *aHeight = u32Height;
6736
6737 aData.resize(cbData);
6738 if (cbData)
6739 memcpy(&aData.front(), pu8Data, cbData);
6740
6741 freeSavedDisplayScreenshot(pu8Data);
6742
6743 return S_OK;
6744}
6745
6746HRESULT Machine::hotPlugCPU(ULONG aCpu)
6747{
6748 HRESULT rc = S_OK;
6749 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6750
6751 if (!mHWData->mCPUHotPlugEnabled)
6752 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6753
6754 if (aCpu >= mHWData->mCPUCount)
6755 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6756
6757 if (mHWData->mCPUAttached[aCpu])
6758 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6759
6760 rc = i_checkStateDependency(MutableOrRunningStateDep);
6761 if (FAILED(rc)) return rc;
6762
6763 alock.release();
6764 rc = i_onCPUChange(aCpu, false);
6765 alock.acquire();
6766 if (FAILED(rc)) return rc;
6767
6768 i_setModified(IsModified_MachineData);
6769 mHWData.backup();
6770 mHWData->mCPUAttached[aCpu] = true;
6771
6772 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6773 if (Global::IsOnline(mData->mMachineState))
6774 i_saveSettings(NULL, alock);
6775
6776 return S_OK;
6777}
6778
6779HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6780{
6781 HRESULT rc = S_OK;
6782
6783 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6784
6785 if (!mHWData->mCPUHotPlugEnabled)
6786 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6787
6788 if (aCpu >= SchemaDefs::MaxCPUCount)
6789 return setError(E_INVALIDARG,
6790 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6791 SchemaDefs::MaxCPUCount);
6792
6793 if (!mHWData->mCPUAttached[aCpu])
6794 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6795
6796 /* CPU 0 can't be detached */
6797 if (aCpu == 0)
6798 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6799
6800 rc = i_checkStateDependency(MutableOrRunningStateDep);
6801 if (FAILED(rc)) return rc;
6802
6803 alock.release();
6804 rc = i_onCPUChange(aCpu, true);
6805 alock.acquire();
6806 if (FAILED(rc)) return rc;
6807
6808 i_setModified(IsModified_MachineData);
6809 mHWData.backup();
6810 mHWData->mCPUAttached[aCpu] = false;
6811
6812 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6813 if (Global::IsOnline(mData->mMachineState))
6814 i_saveSettings(NULL, alock);
6815
6816 return S_OK;
6817}
6818
6819HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6820{
6821 *aAttached = false;
6822
6823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6824
6825 /* If hotplug is enabled the CPU is always enabled. */
6826 if (!mHWData->mCPUHotPlugEnabled)
6827 {
6828 if (aCpu < mHWData->mCPUCount)
6829 *aAttached = true;
6830 }
6831 else
6832 {
6833 if (aCpu < SchemaDefs::MaxCPUCount)
6834 *aAttached = mHWData->mCPUAttached[aCpu];
6835 }
6836
6837 return S_OK;
6838}
6839
6840HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6841{
6842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6843
6844 Utf8Str log = i_getLogFilename(aIdx);
6845 if (!RTFileExists(log.c_str()))
6846 log.setNull();
6847 aFilename = log;
6848
6849 return S_OK;
6850}
6851
6852HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6853{
6854 if (aSize < 0)
6855 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6856
6857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6858
6859 HRESULT rc = S_OK;
6860 Utf8Str log = i_getLogFilename(aIdx);
6861
6862 /* do not unnecessarily hold the lock while doing something which does
6863 * not need the lock and potentially takes a long time. */
6864 alock.release();
6865
6866 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6867 * keeps the SOAP reply size under 1M for the webservice (we're using
6868 * base64 encoded strings for binary data for years now, avoiding the
6869 * expansion of each byte array element to approx. 25 bytes of XML. */
6870 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6871 aData.resize(cbData);
6872
6873 RTFILE LogFile;
6874 int vrc = RTFileOpen(&LogFile, log.c_str(),
6875 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6876 if (RT_SUCCESS(vrc))
6877 {
6878 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6879 if (RT_SUCCESS(vrc))
6880 aData.resize(cbData);
6881 else
6882 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6883 tr("Could not read log file '%s' (%Rrc)"),
6884 log.c_str(), vrc);
6885 RTFileClose(LogFile);
6886 }
6887 else
6888 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6889 tr("Could not open log file '%s' (%Rrc)"),
6890 log.c_str(), vrc);
6891
6892 if (FAILED(rc))
6893 aData.resize(0);
6894
6895 return rc;
6896}
6897
6898
6899/**
6900 * Currently this method doesn't attach device to the running VM,
6901 * just makes sure it's plugged on next VM start.
6902 */
6903HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6904{
6905 // lock scope
6906 {
6907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6908
6909 HRESULT rc = i_checkStateDependency(MutableStateDep);
6910 if (FAILED(rc)) return rc;
6911
6912 ChipsetType_T aChipset = ChipsetType_PIIX3;
6913 COMGETTER(ChipsetType)(&aChipset);
6914
6915 if (aChipset != ChipsetType_ICH9)
6916 {
6917 return setError(E_INVALIDARG,
6918 tr("Host PCI attachment only supported with ICH9 chipset"));
6919 }
6920
6921 // check if device with this host PCI address already attached
6922 for (HWData::PCIDeviceAssignmentList::const_iterator
6923 it = mHWData->mPCIDeviceAssignments.begin();
6924 it != mHWData->mPCIDeviceAssignments.end();
6925 ++it)
6926 {
6927 LONG iHostAddress = -1;
6928 ComPtr<PCIDeviceAttachment> pAttach;
6929 pAttach = *it;
6930 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6931 if (iHostAddress == aHostAddress)
6932 return setError(E_INVALIDARG,
6933 tr("Device with host PCI address already attached to this VM"));
6934 }
6935
6936 ComObjPtr<PCIDeviceAttachment> pda;
6937 char name[32];
6938
6939 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6940 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6941 pda.createObject();
6942 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6943 i_setModified(IsModified_MachineData);
6944 mHWData.backup();
6945 mHWData->mPCIDeviceAssignments.push_back(pda);
6946 }
6947
6948 return S_OK;
6949}
6950
6951/**
6952 * Currently this method doesn't detach device from the running VM,
6953 * just makes sure it's not plugged on next VM start.
6954 */
6955HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6956{
6957 ComObjPtr<PCIDeviceAttachment> pAttach;
6958 bool fRemoved = false;
6959 HRESULT rc;
6960
6961 // lock scope
6962 {
6963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6964
6965 rc = i_checkStateDependency(MutableStateDep);
6966 if (FAILED(rc)) return rc;
6967
6968 for (HWData::PCIDeviceAssignmentList::const_iterator
6969 it = mHWData->mPCIDeviceAssignments.begin();
6970 it != mHWData->mPCIDeviceAssignments.end();
6971 ++it)
6972 {
6973 LONG iHostAddress = -1;
6974 pAttach = *it;
6975 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6976 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6977 {
6978 i_setModified(IsModified_MachineData);
6979 mHWData.backup();
6980 mHWData->mPCIDeviceAssignments.remove(pAttach);
6981 fRemoved = true;
6982 break;
6983 }
6984 }
6985 }
6986
6987
6988 /* Fire event outside of the lock */
6989 if (fRemoved)
6990 {
6991 Assert(!pAttach.isNull());
6992 ComPtr<IEventSource> es;
6993 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6994 Assert(SUCCEEDED(rc));
6995 Bstr mid;
6996 rc = this->COMGETTER(Id)(mid.asOutParam());
6997 Assert(SUCCEEDED(rc));
6998 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6999 }
7000
7001 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7002 tr("No host PCI device %08x attached"),
7003 aHostAddress
7004 );
7005}
7006
7007HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
7008{
7009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7010
7011 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
7012 size_t i = 0;
7013 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
7014 it = mHWData->mPCIDeviceAssignments.begin();
7015 it != mHWData->mPCIDeviceAssignments.end();
7016 ++it, ++i)
7017 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
7018
7019 return S_OK;
7020}
7021
7022HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
7023{
7024 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
7025
7026 return S_OK;
7027}
7028
7029HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
7030{
7031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7032
7033 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
7034
7035 return S_OK;
7036}
7037
7038HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
7039{
7040 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7041 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7042 if (SUCCEEDED(hrc))
7043 {
7044 hrc = mHWData.backupEx();
7045 if (SUCCEEDED(hrc))
7046 {
7047 i_setModified(IsModified_MachineData);
7048 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
7049 }
7050 }
7051 return hrc;
7052}
7053
7054HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
7055{
7056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7057 aTracingConfig = mHWData->mDebugging.strTracingConfig;
7058 return S_OK;
7059}
7060
7061HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
7062{
7063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7064 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7065 if (SUCCEEDED(hrc))
7066 {
7067 hrc = mHWData.backupEx();
7068 if (SUCCEEDED(hrc))
7069 {
7070 mHWData->mDebugging.strTracingConfig = aTracingConfig;
7071 if (SUCCEEDED(hrc))
7072 i_setModified(IsModified_MachineData);
7073 }
7074 }
7075 return hrc;
7076}
7077
7078HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
7079{
7080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7081
7082 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
7083
7084 return S_OK;
7085}
7086
7087HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
7088{
7089 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7090 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7091 if (SUCCEEDED(hrc))
7092 {
7093 hrc = mHWData.backupEx();
7094 if (SUCCEEDED(hrc))
7095 {
7096 i_setModified(IsModified_MachineData);
7097 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
7098 }
7099 }
7100 return hrc;
7101}
7102
7103HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7104{
7105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7106
7107 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7108
7109 return S_OK;
7110}
7111
7112HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7113{
7114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7115
7116 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7117 if ( SUCCEEDED(hrc)
7118 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7119 {
7120 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7121 int vrc;
7122
7123 if (aAutostartEnabled)
7124 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7125 else
7126 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7127
7128 if (RT_SUCCESS(vrc))
7129 {
7130 hrc = mHWData.backupEx();
7131 if (SUCCEEDED(hrc))
7132 {
7133 i_setModified(IsModified_MachineData);
7134 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7135 }
7136 }
7137 else if (vrc == VERR_NOT_SUPPORTED)
7138 hrc = setError(VBOX_E_NOT_SUPPORTED,
7139 tr("The VM autostart feature is not supported on this platform"));
7140 else if (vrc == VERR_PATH_NOT_FOUND)
7141 hrc = setError(E_FAIL,
7142 tr("The path to the autostart database is not set"));
7143 else
7144 hrc = setError(E_UNEXPECTED,
7145 aAutostartEnabled ?
7146 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
7147 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
7148 mUserData->s.strName.c_str(), vrc);
7149 }
7150 return hrc;
7151}
7152
7153HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7154{
7155 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7156
7157 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7158
7159 return S_OK;
7160}
7161
7162HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7163{
7164 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7165 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7166 if (SUCCEEDED(hrc))
7167 {
7168 hrc = mHWData.backupEx();
7169 if (SUCCEEDED(hrc))
7170 {
7171 i_setModified(IsModified_MachineData);
7172 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7173 }
7174 }
7175 return hrc;
7176}
7177
7178HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7179{
7180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7181
7182 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7183
7184 return S_OK;
7185}
7186
7187HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7188{
7189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7190 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7191 if ( SUCCEEDED(hrc)
7192 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7193 {
7194 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7195 int vrc;
7196
7197 if (aAutostopType != AutostopType_Disabled)
7198 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7199 else
7200 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7201
7202 if (RT_SUCCESS(vrc))
7203 {
7204 hrc = mHWData.backupEx();
7205 if (SUCCEEDED(hrc))
7206 {
7207 i_setModified(IsModified_MachineData);
7208 mHWData->mAutostart.enmAutostopType = aAutostopType;
7209 }
7210 }
7211 else if (vrc == VERR_NOT_SUPPORTED)
7212 hrc = setError(VBOX_E_NOT_SUPPORTED,
7213 tr("The VM autostop feature is not supported on this platform"));
7214 else if (vrc == VERR_PATH_NOT_FOUND)
7215 hrc = setError(E_FAIL,
7216 tr("The path to the autostart database is not set"));
7217 else
7218 hrc = setError(E_UNEXPECTED,
7219 aAutostopType != AutostopType_Disabled ?
7220 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
7221 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
7222 mUserData->s.strName.c_str(), vrc);
7223 }
7224 return hrc;
7225}
7226
7227HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7228{
7229 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7230
7231 aDefaultFrontend = mHWData->mDefaultFrontend;
7232
7233 return S_OK;
7234}
7235
7236HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7237{
7238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7239 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7240 if (SUCCEEDED(hrc))
7241 {
7242 hrc = mHWData.backupEx();
7243 if (SUCCEEDED(hrc))
7244 {
7245 i_setModified(IsModified_MachineData);
7246 mHWData->mDefaultFrontend = aDefaultFrontend;
7247 }
7248 }
7249 return hrc;
7250}
7251
7252HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7253{
7254 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7255 size_t cbIcon = mUserData->s.ovIcon.size();
7256 aIcon.resize(cbIcon);
7257 if (cbIcon)
7258 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7259 return S_OK;
7260}
7261
7262HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7263{
7264 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7265 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7266 if (SUCCEEDED(hrc))
7267 {
7268 i_setModified(IsModified_MachineData);
7269 mUserData.backup();
7270 size_t cbIcon = aIcon.size();
7271 mUserData->s.ovIcon.resize(cbIcon);
7272 if (cbIcon)
7273 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7274 }
7275 return hrc;
7276}
7277
7278HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7279{
7280#ifdef VBOX_WITH_USB
7281 *aUSBProxyAvailable = true;
7282#else
7283 *aUSBProxyAvailable = false;
7284#endif
7285 return S_OK;
7286}
7287
7288HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
7289{
7290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7291
7292 *aVMProcessPriority = mUserData->s.enmVMPriority;
7293
7294 return S_OK;
7295}
7296
7297HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
7298{
7299 RT_NOREF(aVMProcessPriority);
7300 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7301 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7302 if (SUCCEEDED(hrc))
7303 {
7304 hrc = mUserData.backupEx();
7305 if (SUCCEEDED(hrc))
7306 {
7307 i_setModified(IsModified_MachineData);
7308 mUserData->s.enmVMPriority = aVMProcessPriority;
7309 }
7310 }
7311 alock.release();
7312 if (SUCCEEDED(hrc))
7313 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
7314 return hrc;
7315}
7316
7317HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7318 ComPtr<IProgress> &aProgress)
7319{
7320 ComObjPtr<Progress> pP;
7321 Progress *ppP = pP;
7322 IProgress *iP = static_cast<IProgress *>(ppP);
7323 IProgress **pProgress = &iP;
7324
7325 IMachine *pTarget = aTarget;
7326
7327 /* Convert the options. */
7328 RTCList<CloneOptions_T> optList;
7329 if (aOptions.size())
7330 for (size_t i = 0; i < aOptions.size(); ++i)
7331 optList.append(aOptions[i]);
7332
7333 if (optList.contains(CloneOptions_Link))
7334 {
7335 if (!i_isSnapshotMachine())
7336 return setError(E_INVALIDARG,
7337 tr("Linked clone can only be created from a snapshot"));
7338 if (aMode != CloneMode_MachineState)
7339 return setError(E_INVALIDARG,
7340 tr("Linked clone can only be created for a single machine state"));
7341 }
7342 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7343
7344 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7345
7346 HRESULT rc = pWorker->start(pProgress);
7347
7348 pP = static_cast<Progress *>(*pProgress);
7349 pP.queryInterfaceTo(aProgress.asOutParam());
7350
7351 return rc;
7352
7353}
7354
7355HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7356 const com::Utf8Str &aType,
7357 ComPtr<IProgress> &aProgress)
7358{
7359 LogFlowThisFuncEnter();
7360
7361 ComObjPtr<Progress> ptrProgress;
7362 HRESULT hrc = ptrProgress.createObject();
7363 if (SUCCEEDED(hrc))
7364 {
7365 com::Utf8Str strDefaultPath;
7366 if (aTargetPath.isEmpty())
7367 i_calculateFullPath(".", strDefaultPath);
7368
7369 /* Initialize our worker task */
7370 MachineMoveVM *pTask = NULL;
7371 try
7372 {
7373 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7374 }
7375 catch (std::bad_alloc &)
7376 {
7377 return E_OUTOFMEMORY;
7378 }
7379
7380 hrc = pTask->init();//no exceptions are thrown
7381
7382 if (SUCCEEDED(hrc))
7383 {
7384 hrc = pTask->createThread();
7385 pTask = NULL; /* Consumed by createThread(). */
7386 if (SUCCEEDED(hrc))
7387 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7388 else
7389 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7390 }
7391 else
7392 delete pTask;
7393 }
7394
7395 LogFlowThisFuncLeave();
7396 return hrc;
7397
7398}
7399
7400HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7401{
7402 NOREF(aProgress);
7403 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7404
7405 // This check should always fail.
7406 HRESULT rc = i_checkStateDependency(MutableStateDep);
7407 if (FAILED(rc)) return rc;
7408
7409 AssertFailedReturn(E_NOTIMPL);
7410}
7411
7412HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7413{
7414 NOREF(aSavedStateFile);
7415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7416
7417 // This check should always fail.
7418 HRESULT rc = i_checkStateDependency(MutableStateDep);
7419 if (FAILED(rc)) return rc;
7420
7421 AssertFailedReturn(E_NOTIMPL);
7422}
7423
7424HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7425{
7426 NOREF(aFRemoveFile);
7427 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7428
7429 // This check should always fail.
7430 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7431 if (FAILED(rc)) return rc;
7432
7433 AssertFailedReturn(E_NOTIMPL);
7434}
7435
7436// public methods for internal purposes
7437/////////////////////////////////////////////////////////////////////////////
7438
7439/**
7440 * Adds the given IsModified_* flag to the dirty flags of the machine.
7441 * This must be called either during i_loadSettings or under the machine write lock.
7442 * @param fl Flag
7443 * @param fAllowStateModification If state modifications are allowed.
7444 */
7445void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7446{
7447 mData->flModifications |= fl;
7448 if (fAllowStateModification && i_isStateModificationAllowed())
7449 mData->mCurrentStateModified = true;
7450}
7451
7452/**
7453 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7454 * care of the write locking.
7455 *
7456 * @param fModification The flag to add.
7457 * @param fAllowStateModification If state modifications are allowed.
7458 */
7459void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7460{
7461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7462 i_setModified(fModification, fAllowStateModification);
7463}
7464
7465/**
7466 * Saves the registry entry of this machine to the given configuration node.
7467 *
7468 * @param data Machine registry data.
7469 *
7470 * @note locks this object for reading.
7471 */
7472HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7473{
7474 AutoLimitedCaller autoCaller(this);
7475 AssertComRCReturnRC(autoCaller.rc());
7476
7477 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7478
7479 data.uuid = mData->mUuid;
7480 data.strSettingsFile = mData->m_strConfigFile;
7481
7482 return S_OK;
7483}
7484
7485/**
7486 * Calculates the absolute path of the given path taking the directory of the
7487 * machine settings file as the current directory.
7488 *
7489 * @param strPath Path to calculate the absolute path for.
7490 * @param aResult Where to put the result (used only on success, can be the
7491 * same Utf8Str instance as passed in @a aPath).
7492 * @return IPRT result.
7493 *
7494 * @note Locks this object for reading.
7495 */
7496int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7497{
7498 AutoCaller autoCaller(this);
7499 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7500
7501 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7502
7503 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7504
7505 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7506
7507 strSettingsDir.stripFilename();
7508 char szFolder[RTPATH_MAX];
7509 size_t cbFolder = sizeof(szFolder);
7510 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7511 if (RT_SUCCESS(vrc))
7512 aResult = szFolder;
7513
7514 return vrc;
7515}
7516
7517/**
7518 * Copies strSource to strTarget, making it relative to the machine folder
7519 * if it is a subdirectory thereof, or simply copying it otherwise.
7520 *
7521 * @param strSource Path to evaluate and copy.
7522 * @param strTarget Buffer to receive target path.
7523 *
7524 * @note Locks this object for reading.
7525 */
7526void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7527 Utf8Str &strTarget)
7528{
7529 AutoCaller autoCaller(this);
7530 AssertComRCReturn(autoCaller.rc(), (void)0);
7531
7532 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7533
7534 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7535 // use strTarget as a temporary buffer to hold the machine settings dir
7536 strTarget = mData->m_strConfigFileFull;
7537 strTarget.stripFilename();
7538 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7539 {
7540 // is relative: then append what's left
7541 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7542 // for empty paths (only possible for subdirs) use "." to avoid
7543 // triggering default settings for not present config attributes.
7544 if (strTarget.isEmpty())
7545 strTarget = ".";
7546 }
7547 else
7548 // is not relative: then overwrite
7549 strTarget = strSource;
7550}
7551
7552/**
7553 * Returns the full path to the machine's log folder in the
7554 * \a aLogFolder argument.
7555 */
7556void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7557{
7558 AutoCaller autoCaller(this);
7559 AssertComRCReturnVoid(autoCaller.rc());
7560
7561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7562
7563 char szTmp[RTPATH_MAX];
7564 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7565 if (RT_SUCCESS(vrc))
7566 {
7567 if (szTmp[0] && !mUserData.isNull())
7568 {
7569 char szTmp2[RTPATH_MAX];
7570 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7571 if (RT_SUCCESS(vrc))
7572 aLogFolder.printf("%s%c%s",
7573 szTmp2,
7574 RTPATH_DELIMITER,
7575 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7576 }
7577 else
7578 vrc = VERR_PATH_IS_RELATIVE;
7579 }
7580
7581 if (RT_FAILURE(vrc))
7582 {
7583 // fallback if VBOX_USER_LOGHOME is not set or invalid
7584 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7585 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7586 aLogFolder.append(RTPATH_DELIMITER);
7587 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7588 }
7589}
7590
7591/**
7592 * Returns the full path to the machine's log file for an given index.
7593 */
7594Utf8Str Machine::i_getLogFilename(ULONG idx)
7595{
7596 Utf8Str logFolder;
7597 getLogFolder(logFolder);
7598 Assert(logFolder.length());
7599
7600 Utf8Str log;
7601 if (idx == 0)
7602 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7603#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7604 else if (idx == 1)
7605 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7606 else
7607 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7608#else
7609 else
7610 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7611#endif
7612 return log;
7613}
7614
7615/**
7616 * Returns the full path to the machine's hardened log file.
7617 */
7618Utf8Str Machine::i_getHardeningLogFilename(void)
7619{
7620 Utf8Str strFilename;
7621 getLogFolder(strFilename);
7622 Assert(strFilename.length());
7623 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7624 return strFilename;
7625}
7626
7627/**
7628 * Returns the default NVRAM filename based on the location of the VM config.
7629 * Note that this is a relative path.
7630 */
7631Utf8Str Machine::i_getDefaultNVRAMFilename()
7632{
7633 AutoCaller autoCaller(this);
7634 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7635
7636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7637
7638 if (i_isSnapshotMachine())
7639 return Utf8Str::Empty;
7640
7641 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7642 strNVRAMFilePath.stripPath();
7643 strNVRAMFilePath.stripSuffix();
7644 strNVRAMFilePath += ".nvram";
7645
7646 return strNVRAMFilePath;
7647}
7648
7649/**
7650 * Returns the NVRAM filename for a new snapshot. This intentionally works
7651 * similarly to the saved state file naming. Note that this is usually
7652 * a relative path, unless the snapshot folder is absolute.
7653 */
7654Utf8Str Machine::i_getSnapshotNVRAMFilename()
7655{
7656 AutoCaller autoCaller(this);
7657 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7658
7659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7660
7661 RTTIMESPEC ts;
7662 RTTimeNow(&ts);
7663 RTTIME time;
7664 RTTimeExplode(&time, &ts);
7665
7666 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7667 strNVRAMFilePath += RTPATH_DELIMITER;
7668 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7669 time.i32Year, time.u8Month, time.u8MonthDay,
7670 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7671
7672 return strNVRAMFilePath;
7673}
7674
7675/**
7676 * Returns the version of the settings file.
7677 */
7678SettingsVersion_T Machine::i_getSettingsVersion(void)
7679{
7680 AutoCaller autoCaller(this);
7681 AssertComRCReturn(autoCaller.rc(), SettingsVersion_Null);
7682
7683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7684
7685 return mData->pMachineConfigFile->getSettingsVersion();
7686}
7687
7688/**
7689 * Composes a unique saved state filename based on the current system time. The filename is
7690 * granular to the second so this will work so long as no more than one snapshot is taken on
7691 * a machine per second.
7692 *
7693 * Before version 4.1, we used this formula for saved state files:
7694 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7695 * which no longer works because saved state files can now be shared between the saved state of the
7696 * "saved" machine and an online snapshot, and the following would cause problems:
7697 * 1) save machine
7698 * 2) create online snapshot from that machine state --> reusing saved state file
7699 * 3) save machine again --> filename would be reused, breaking the online snapshot
7700 *
7701 * So instead we now use a timestamp.
7702 *
7703 * @param strStateFilePath
7704 */
7705
7706void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7707{
7708 AutoCaller autoCaller(this);
7709 AssertComRCReturnVoid(autoCaller.rc());
7710
7711 {
7712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7713 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7714 }
7715
7716 RTTIMESPEC ts;
7717 RTTimeNow(&ts);
7718 RTTIME time;
7719 RTTimeExplode(&time, &ts);
7720
7721 strStateFilePath += RTPATH_DELIMITER;
7722 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7723 time.i32Year, time.u8Month, time.u8MonthDay,
7724 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7725}
7726
7727/**
7728 * Returns whether at least one USB controller is present for the VM.
7729 */
7730bool Machine::i_isUSBControllerPresent()
7731{
7732 AutoCaller autoCaller(this);
7733 AssertComRCReturn(autoCaller.rc(), false);
7734
7735 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7736
7737 return (mUSBControllers->size() > 0);
7738}
7739
7740
7741/**
7742 * @note Locks this object for writing, calls the client process
7743 * (inside the lock).
7744 */
7745HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7746 const Utf8Str &strFrontend,
7747 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7748 ProgressProxy *aProgress)
7749{
7750 LogFlowThisFuncEnter();
7751
7752 AssertReturn(aControl, E_FAIL);
7753 AssertReturn(aProgress, E_FAIL);
7754 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7755
7756 AutoCaller autoCaller(this);
7757 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7758
7759 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7760
7761 if (!mData->mRegistered)
7762 return setError(E_UNEXPECTED,
7763 tr("The machine '%s' is not registered"),
7764 mUserData->s.strName.c_str());
7765
7766 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7767
7768 /* The process started when launching a VM with separate UI/VM processes is always
7769 * the UI process, i.e. needs special handling as it won't claim the session. */
7770 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7771
7772 if (fSeparate)
7773 {
7774 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7775 return setError(VBOX_E_INVALID_OBJECT_STATE,
7776 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7777 mUserData->s.strName.c_str());
7778 }
7779 else
7780 {
7781 if ( mData->mSession.mState == SessionState_Locked
7782 || mData->mSession.mState == SessionState_Spawning
7783 || mData->mSession.mState == SessionState_Unlocking)
7784 return setError(VBOX_E_INVALID_OBJECT_STATE,
7785 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7786 mUserData->s.strName.c_str());
7787
7788 /* may not be busy */
7789 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7790 }
7791
7792 /* Hardening logging */
7793#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7794 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7795 {
7796 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7797 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7798 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7799 {
7800 Utf8Str strStartupLogDir = strHardeningLogFile;
7801 strStartupLogDir.stripFilename();
7802 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7803 file without stripping the file. */
7804 }
7805 strSupHardeningLogArg.append(strHardeningLogFile);
7806
7807 /* Remove legacy log filename to avoid confusion. */
7808 Utf8Str strOldStartupLogFile;
7809 getLogFolder(strOldStartupLogFile);
7810 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7811 RTFileDelete(strOldStartupLogFile.c_str());
7812 }
7813#else
7814 Utf8Str strSupHardeningLogArg;
7815#endif
7816
7817 Utf8Str strAppOverride;
7818#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7819 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7820#endif
7821
7822 bool fUseVBoxSDS = false;
7823 Utf8Str strCanonicalName;
7824 if (false)
7825 { }
7826#ifdef VBOX_WITH_QTGUI
7827 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7828 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7829 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7830 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7831 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7832 {
7833 strCanonicalName = "GUI/Qt";
7834 fUseVBoxSDS = true;
7835 }
7836#endif
7837#ifdef VBOX_WITH_VBOXSDL
7838 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7839 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7840 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7841 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7842 {
7843 strCanonicalName = "GUI/SDL";
7844 fUseVBoxSDS = true;
7845 }
7846#endif
7847#ifdef VBOX_WITH_HEADLESS
7848 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7849 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7850 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7851 {
7852 strCanonicalName = "headless";
7853 }
7854#endif
7855 else
7856 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7857
7858 Utf8Str idStr = mData->mUuid.toString();
7859 Utf8Str const &strMachineName = mUserData->s.strName;
7860 RTPROCESS pid = NIL_RTPROCESS;
7861
7862#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7863 RT_NOREF(fUseVBoxSDS);
7864#else
7865 DWORD idCallerSession = ~(DWORD)0;
7866 if (fUseVBoxSDS)
7867 {
7868 /*
7869 * The VBoxSDS should be used for process launching the VM with
7870 * GUI only if the caller and the VBoxSDS are in different Windows
7871 * sessions and the caller in the interactive one.
7872 */
7873 fUseVBoxSDS = false;
7874
7875 /* Get windows session of the current process. The process token used
7876 due to several reasons:
7877 1. The token is absent for the current thread except someone set it
7878 for us.
7879 2. Needs to get the id of the session where the process is started.
7880 We only need to do this once, though. */
7881 static DWORD s_idCurrentSession = ~(DWORD)0;
7882 DWORD idCurrentSession = s_idCurrentSession;
7883 if (idCurrentSession == ~(DWORD)0)
7884 {
7885 HANDLE hCurrentProcessToken = NULL;
7886 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7887 {
7888 DWORD cbIgn = 0;
7889 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7890 s_idCurrentSession = idCurrentSession;
7891 else
7892 {
7893 idCurrentSession = ~(DWORD)0;
7894 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7895 }
7896 CloseHandle(hCurrentProcessToken);
7897 }
7898 else
7899 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7900 }
7901
7902 /* get the caller's session */
7903 HRESULT hrc = CoImpersonateClient();
7904 if (SUCCEEDED(hrc))
7905 {
7906 HANDLE hCallerThreadToken;
7907 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7908 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7909 &hCallerThreadToken))
7910 {
7911 SetLastError(NO_ERROR);
7912 DWORD cbIgn = 0;
7913 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7914 {
7915 /* Only need to use SDS if the session ID differs: */
7916 if (idCurrentSession != idCallerSession)
7917 {
7918 fUseVBoxSDS = false;
7919
7920 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7921 DWORD cbTokenGroups = 0;
7922 PTOKEN_GROUPS pTokenGroups = NULL;
7923 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7924 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7925 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7926 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7927 {
7928 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7929 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7930 PSID pInteractiveSid = NULL;
7931 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7932 {
7933 /* Iterate over the groups looking for the interactive SID: */
7934 fUseVBoxSDS = false;
7935 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7936 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7937 {
7938 fUseVBoxSDS = true;
7939 break;
7940 }
7941 FreeSid(pInteractiveSid);
7942 }
7943 }
7944 else
7945 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7946 RTMemTmpFree(pTokenGroups);
7947 }
7948 }
7949 else
7950 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7951 CloseHandle(hCallerThreadToken);
7952 }
7953 else
7954 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7955 CoRevertToSelf();
7956 }
7957 else
7958 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7959 }
7960 if (fUseVBoxSDS)
7961 {
7962 /* connect to VBoxSDS */
7963 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7964 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7965 if (FAILED(rc))
7966 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7967 strMachineName.c_str());
7968
7969 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7970 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7971 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7972 service to access the files. */
7973 rc = CoSetProxyBlanket(pVBoxSDS,
7974 RPC_C_AUTHN_DEFAULT,
7975 RPC_C_AUTHZ_DEFAULT,
7976 COLE_DEFAULT_PRINCIPAL,
7977 RPC_C_AUTHN_LEVEL_DEFAULT,
7978 RPC_C_IMP_LEVEL_IMPERSONATE,
7979 NULL,
7980 EOAC_DEFAULT);
7981 if (FAILED(rc))
7982 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7983
7984 size_t const cEnvVars = aEnvironmentChanges.size();
7985 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7986 for (size_t i = 0; i < cEnvVars; i++)
7987 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7988
7989 ULONG uPid = 0;
7990 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7991 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7992 idCallerSession, &uPid);
7993 if (FAILED(rc))
7994 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7995 pid = (RTPROCESS)uPid;
7996 }
7997 else
7998#endif /* VBOX_WITH_SDS && RT_OS_WINDOWS */
7999 {
8000 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
8001 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
8002 if (RT_FAILURE(vrc))
8003 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
8004 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
8005 }
8006
8007 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
8008 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
8009
8010 if (!fSeparate)
8011 {
8012 /*
8013 * Note that we don't release the lock here before calling the client,
8014 * because it doesn't need to call us back if called with a NULL argument.
8015 * Releasing the lock here is dangerous because we didn't prepare the
8016 * launch data yet, but the client we've just started may happen to be
8017 * too fast and call LockMachine() that will fail (because of PID, etc.),
8018 * so that the Machine will never get out of the Spawning session state.
8019 */
8020
8021 /* inform the session that it will be a remote one */
8022 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8023#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8024 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8025#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8026 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8027#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8028 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8029
8030 if (FAILED(rc))
8031 {
8032 /* restore the session state */
8033 mData->mSession.mState = SessionState_Unlocked;
8034 alock.release();
8035 mParent->i_addProcessToReap(pid);
8036 /* The failure may occur w/o any error info (from RPC), so provide one */
8037 return setError(VBOX_E_VM_ERROR,
8038 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8039 }
8040
8041 /* attach launch data to the machine */
8042 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8043 mData->mSession.mRemoteControls.push_back(aControl);
8044 mData->mSession.mProgress = aProgress;
8045 mData->mSession.mPID = pid;
8046 mData->mSession.mState = SessionState_Spawning;
8047 Assert(strCanonicalName.isNotEmpty());
8048 mData->mSession.mName = strCanonicalName;
8049 }
8050 else
8051 {
8052 /* For separate UI process we declare the launch as completed instantly, as the
8053 * actual headless VM start may or may not come. No point in remembering anything
8054 * yet, as what matters for us is when the headless VM gets started. */
8055 aProgress->i_notifyComplete(S_OK);
8056 }
8057
8058 alock.release();
8059 mParent->i_addProcessToReap(pid);
8060
8061 LogFlowThisFuncLeave();
8062 return S_OK;
8063}
8064
8065/**
8066 * Returns @c true if the given session machine instance has an open direct
8067 * session (and optionally also for direct sessions which are closing) and
8068 * returns the session control machine instance if so.
8069 *
8070 * Note that when the method returns @c false, the arguments remain unchanged.
8071 *
8072 * @param aMachine Session machine object.
8073 * @param aControl Direct session control object (optional).
8074 * @param aRequireVM If true then only allow VM sessions.
8075 * @param aAllowClosing If true then additionally a session which is currently
8076 * being closed will also be allowed.
8077 *
8078 * @note locks this object for reading.
8079 */
8080bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8081 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8082 bool aRequireVM /*= false*/,
8083 bool aAllowClosing /*= false*/)
8084{
8085 AutoLimitedCaller autoCaller(this);
8086 AssertComRCReturn(autoCaller.rc(), false);
8087
8088 /* just return false for inaccessible machines */
8089 if (getObjectState().getState() != ObjectState::Ready)
8090 return false;
8091
8092 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8093
8094 if ( ( mData->mSession.mState == SessionState_Locked
8095 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
8096 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8097 )
8098 {
8099 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8100
8101 aMachine = mData->mSession.mMachine;
8102
8103 if (aControl != NULL)
8104 *aControl = mData->mSession.mDirectControl;
8105
8106 return true;
8107 }
8108
8109 return false;
8110}
8111
8112/**
8113 * Returns @c true if the given machine has an spawning direct session.
8114 *
8115 * @note locks this object for reading.
8116 */
8117bool Machine::i_isSessionSpawning()
8118{
8119 AutoLimitedCaller autoCaller(this);
8120 AssertComRCReturn(autoCaller.rc(), false);
8121
8122 /* just return false for inaccessible machines */
8123 if (getObjectState().getState() != ObjectState::Ready)
8124 return false;
8125
8126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8127
8128 if (mData->mSession.mState == SessionState_Spawning)
8129 return true;
8130
8131 return false;
8132}
8133
8134/**
8135 * Called from the client watcher thread to check for unexpected client process
8136 * death during Session_Spawning state (e.g. before it successfully opened a
8137 * direct session).
8138 *
8139 * On Win32 and on OS/2, this method is called only when we've got the
8140 * direct client's process termination notification, so it always returns @c
8141 * true.
8142 *
8143 * On other platforms, this method returns @c true if the client process is
8144 * terminated and @c false if it's still alive.
8145 *
8146 * @note Locks this object for writing.
8147 */
8148bool Machine::i_checkForSpawnFailure()
8149{
8150 AutoCaller autoCaller(this);
8151 if (!autoCaller.isOk())
8152 {
8153 /* nothing to do */
8154 LogFlowThisFunc(("Already uninitialized!\n"));
8155 return true;
8156 }
8157
8158 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8159
8160 if (mData->mSession.mState != SessionState_Spawning)
8161 {
8162 /* nothing to do */
8163 LogFlowThisFunc(("Not spawning any more!\n"));
8164 return true;
8165 }
8166
8167 HRESULT rc = S_OK;
8168
8169 /* PID not yet initialized, skip check. */
8170 if (mData->mSession.mPID == NIL_RTPROCESS)
8171 return false;
8172
8173 RTPROCSTATUS status;
8174 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8175
8176 if (vrc != VERR_PROCESS_RUNNING)
8177 {
8178 Utf8Str strExtraInfo;
8179
8180#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8181 /* If the startup logfile exists and is of non-zero length, tell the
8182 user to look there for more details to encourage them to attach it
8183 when reporting startup issues. */
8184 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8185 uint64_t cbStartupLogFile = 0;
8186 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
8187 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8188 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
8189#endif
8190
8191 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8192 rc = setError(E_FAIL,
8193 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8194 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8195 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8196 rc = setError(E_FAIL,
8197 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8198 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8199 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8200 rc = setError(E_FAIL,
8201 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8202 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8203 else
8204 rc = setErrorBoth(E_FAIL, vrc,
8205 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8206 i_getName().c_str(), vrc, strExtraInfo.c_str());
8207 }
8208
8209 if (FAILED(rc))
8210 {
8211 /* Close the remote session, remove the remote control from the list
8212 * and reset session state to Closed (@note keep the code in sync with
8213 * the relevant part in LockMachine()). */
8214
8215 Assert(mData->mSession.mRemoteControls.size() == 1);
8216 if (mData->mSession.mRemoteControls.size() == 1)
8217 {
8218 ErrorInfoKeeper eik;
8219 mData->mSession.mRemoteControls.front()->Uninitialize();
8220 }
8221
8222 mData->mSession.mRemoteControls.clear();
8223 mData->mSession.mState = SessionState_Unlocked;
8224
8225 /* finalize the progress after setting the state */
8226 if (!mData->mSession.mProgress.isNull())
8227 {
8228 mData->mSession.mProgress->notifyComplete(rc);
8229 mData->mSession.mProgress.setNull();
8230 }
8231
8232 mData->mSession.mPID = NIL_RTPROCESS;
8233
8234 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
8235 return true;
8236 }
8237
8238 return false;
8239}
8240
8241/**
8242 * Checks whether the machine can be registered. If so, commits and saves
8243 * all settings.
8244 *
8245 * @note Must be called from mParent's write lock. Locks this object and
8246 * children for writing.
8247 */
8248HRESULT Machine::i_prepareRegister()
8249{
8250 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8251
8252 AutoLimitedCaller autoCaller(this);
8253 AssertComRCReturnRC(autoCaller.rc());
8254
8255 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8256
8257 /* wait for state dependents to drop to zero */
8258 i_ensureNoStateDependencies(alock);
8259
8260 if (!mData->mAccessible)
8261 return setError(VBOX_E_INVALID_OBJECT_STATE,
8262 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8263 mUserData->s.strName.c_str(),
8264 mData->mUuid.toString().c_str());
8265
8266 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8267
8268 if (mData->mRegistered)
8269 return setError(VBOX_E_INVALID_OBJECT_STATE,
8270 tr("The machine '%s' with UUID {%s} is already registered"),
8271 mUserData->s.strName.c_str(),
8272 mData->mUuid.toString().c_str());
8273
8274 HRESULT rc = S_OK;
8275
8276 // Ensure the settings are saved. If we are going to be registered and
8277 // no config file exists yet, create it by calling i_saveSettings() too.
8278 if ( (mData->flModifications)
8279 || (!mData->pMachineConfigFile->fileExists())
8280 )
8281 {
8282 rc = i_saveSettings(NULL, alock);
8283 // no need to check whether VirtualBox.xml needs saving too since
8284 // we can't have a machine XML file rename pending
8285 if (FAILED(rc)) return rc;
8286 }
8287
8288 /* more config checking goes here */
8289
8290 if (SUCCEEDED(rc))
8291 {
8292 /* we may have had implicit modifications we want to fix on success */
8293 i_commit();
8294
8295 mData->mRegistered = true;
8296 }
8297 else
8298 {
8299 /* we may have had implicit modifications we want to cancel on failure*/
8300 i_rollback(false /* aNotify */);
8301 }
8302
8303 return rc;
8304}
8305
8306/**
8307 * Increases the number of objects dependent on the machine state or on the
8308 * registered state. Guarantees that these two states will not change at least
8309 * until #i_releaseStateDependency() is called.
8310 *
8311 * Depending on the @a aDepType value, additional state checks may be made.
8312 * These checks will set extended error info on failure. See
8313 * #i_checkStateDependency() for more info.
8314 *
8315 * If this method returns a failure, the dependency is not added and the caller
8316 * is not allowed to rely on any particular machine state or registration state
8317 * value and may return the failed result code to the upper level.
8318 *
8319 * @param aDepType Dependency type to add.
8320 * @param aState Current machine state (NULL if not interested).
8321 * @param aRegistered Current registered state (NULL if not interested).
8322 *
8323 * @note Locks this object for writing.
8324 */
8325HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8326 MachineState_T *aState /* = NULL */,
8327 BOOL *aRegistered /* = NULL */)
8328{
8329 AutoCaller autoCaller(this);
8330 AssertComRCReturnRC(autoCaller.rc());
8331
8332 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8333
8334 HRESULT rc = i_checkStateDependency(aDepType);
8335 if (FAILED(rc)) return rc;
8336
8337 {
8338 if (mData->mMachineStateChangePending != 0)
8339 {
8340 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8341 * drop to zero so don't add more. It may make sense to wait a bit
8342 * and retry before reporting an error (since the pending state
8343 * transition should be really quick) but let's just assert for
8344 * now to see if it ever happens on practice. */
8345
8346 AssertFailed();
8347
8348 return setError(E_ACCESSDENIED,
8349 tr("Machine state change is in progress. Please retry the operation later."));
8350 }
8351
8352 ++mData->mMachineStateDeps;
8353 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8354 }
8355
8356 if (aState)
8357 *aState = mData->mMachineState;
8358 if (aRegistered)
8359 *aRegistered = mData->mRegistered;
8360
8361 return S_OK;
8362}
8363
8364/**
8365 * Decreases the number of objects dependent on the machine state.
8366 * Must always complete the #i_addStateDependency() call after the state
8367 * dependency is no more necessary.
8368 */
8369void Machine::i_releaseStateDependency()
8370{
8371 AutoCaller autoCaller(this);
8372 AssertComRCReturnVoid(autoCaller.rc());
8373
8374 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8375
8376 /* releaseStateDependency() w/o addStateDependency()? */
8377 AssertReturnVoid(mData->mMachineStateDeps != 0);
8378 -- mData->mMachineStateDeps;
8379
8380 if (mData->mMachineStateDeps == 0)
8381 {
8382 /* inform i_ensureNoStateDependencies() that there are no more deps */
8383 if (mData->mMachineStateChangePending != 0)
8384 {
8385 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8386 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8387 }
8388 }
8389}
8390
8391Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8392{
8393 /* start with nothing found */
8394 Utf8Str strResult("");
8395
8396 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8397
8398 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8399 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8400 // found:
8401 strResult = it->second; // source is a Utf8Str
8402
8403 return strResult;
8404}
8405
8406// protected methods
8407/////////////////////////////////////////////////////////////////////////////
8408
8409/**
8410 * Performs machine state checks based on the @a aDepType value. If a check
8411 * fails, this method will set extended error info, otherwise it will return
8412 * S_OK. It is supposed, that on failure, the caller will immediately return
8413 * the return value of this method to the upper level.
8414 *
8415 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8416 *
8417 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8418 * current state of this machine object allows to change settings of the
8419 * machine (i.e. the machine is not registered, or registered but not running
8420 * and not saved). It is useful to call this method from Machine setters
8421 * before performing any change.
8422 *
8423 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8424 * as for MutableStateDep except that if the machine is saved, S_OK is also
8425 * returned. This is useful in setters which allow changing machine
8426 * properties when it is in the saved state.
8427 *
8428 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8429 * if the current state of this machine object allows to change runtime
8430 * changeable settings of the machine (i.e. the machine is not registered, or
8431 * registered but either running or not running and not saved). It is useful
8432 * to call this method from Machine setters before performing any changes to
8433 * runtime changeable settings.
8434 *
8435 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8436 * the same as for MutableOrRunningStateDep except that if the machine is
8437 * saved, S_OK is also returned. This is useful in setters which allow
8438 * changing runtime and saved state changeable machine properties.
8439 *
8440 * @param aDepType Dependency type to check.
8441 *
8442 * @note Non Machine based classes should use #i_addStateDependency() and
8443 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8444 * template.
8445 *
8446 * @note This method must be called from under this object's read or write
8447 * lock.
8448 */
8449HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8450{
8451 switch (aDepType)
8452 {
8453 case AnyStateDep:
8454 {
8455 break;
8456 }
8457 case MutableStateDep:
8458 {
8459 if ( mData->mRegistered
8460 && ( !i_isSessionMachine()
8461 || ( mData->mMachineState != MachineState_Aborted
8462 && mData->mMachineState != MachineState_Teleported
8463 && mData->mMachineState != MachineState_PoweredOff
8464 )
8465 )
8466 )
8467 return setError(VBOX_E_INVALID_VM_STATE,
8468 tr("The machine is not mutable (state is %s)"),
8469 Global::stringifyMachineState(mData->mMachineState));
8470 break;
8471 }
8472 case MutableOrSavedStateDep:
8473 {
8474 if ( mData->mRegistered
8475 && ( !i_isSessionMachine()
8476 || ( mData->mMachineState != MachineState_Aborted
8477 && mData->mMachineState != MachineState_Teleported
8478 && mData->mMachineState != MachineState_Saved
8479 && mData->mMachineState != MachineState_AbortedSaved
8480 && mData->mMachineState != MachineState_PoweredOff
8481 )
8482 )
8483 )
8484 return setError(VBOX_E_INVALID_VM_STATE,
8485 tr("The machine is not mutable or saved (state is %s)"),
8486 Global::stringifyMachineState(mData->mMachineState));
8487 break;
8488 }
8489 case MutableOrRunningStateDep:
8490 {
8491 if ( mData->mRegistered
8492 && ( !i_isSessionMachine()
8493 || ( mData->mMachineState != MachineState_Aborted
8494 && mData->mMachineState != MachineState_Teleported
8495 && mData->mMachineState != MachineState_PoweredOff
8496 && !Global::IsOnline(mData->mMachineState)
8497 )
8498 )
8499 )
8500 return setError(VBOX_E_INVALID_VM_STATE,
8501 tr("The machine is not mutable or running (state is %s)"),
8502 Global::stringifyMachineState(mData->mMachineState));
8503 break;
8504 }
8505 case MutableOrSavedOrRunningStateDep:
8506 {
8507 if ( mData->mRegistered
8508 && ( !i_isSessionMachine()
8509 || ( mData->mMachineState != MachineState_Aborted
8510 && mData->mMachineState != MachineState_Teleported
8511 && mData->mMachineState != MachineState_Saved
8512 && mData->mMachineState != MachineState_AbortedSaved
8513 && mData->mMachineState != MachineState_PoweredOff
8514 && !Global::IsOnline(mData->mMachineState)
8515 )
8516 )
8517 )
8518 return setError(VBOX_E_INVALID_VM_STATE,
8519 tr("The machine is not mutable, saved or running (state is %s)"),
8520 Global::stringifyMachineState(mData->mMachineState));
8521 break;
8522 }
8523 }
8524
8525 return S_OK;
8526}
8527
8528/**
8529 * Helper to initialize all associated child objects and allocate data
8530 * structures.
8531 *
8532 * This method must be called as a part of the object's initialization procedure
8533 * (usually done in the #init() method).
8534 *
8535 * @note Must be called only from #init() or from #i_registeredInit().
8536 */
8537HRESULT Machine::initDataAndChildObjects()
8538{
8539 AutoCaller autoCaller(this);
8540 AssertComRCReturnRC(autoCaller.rc());
8541 AssertReturn( getObjectState().getState() == ObjectState::InInit
8542 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8543
8544 AssertReturn(!mData->mAccessible, E_FAIL);
8545
8546 /* allocate data structures */
8547 mSSData.allocate();
8548 mUserData.allocate();
8549 mHWData.allocate();
8550 mMediumAttachments.allocate();
8551 mStorageControllers.allocate();
8552 mUSBControllers.allocate();
8553
8554 /* initialize mOSTypeId */
8555 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8556
8557/** @todo r=bird: init() methods never fails, right? Why don't we make them
8558 * return void then! */
8559
8560 /* create associated BIOS settings object */
8561 unconst(mBIOSSettings).createObject();
8562 mBIOSSettings->init(this);
8563
8564 /* create associated trusted platform module object */
8565 unconst(mTrustedPlatformModule).createObject();
8566 mTrustedPlatformModule->init(this);
8567
8568 /* create associated NVRAM store object */
8569 unconst(mNvramStore).createObject();
8570 mNvramStore->init(this);
8571
8572 /* create associated record settings object */
8573 unconst(mRecordingSettings).createObject();
8574 mRecordingSettings->init(this);
8575
8576 /* create the graphics adapter object (always present) */
8577 unconst(mGraphicsAdapter).createObject();
8578 mGraphicsAdapter->init(this);
8579
8580 /* create an associated VRDE object (default is disabled) */
8581 unconst(mVRDEServer).createObject();
8582 mVRDEServer->init(this);
8583
8584 /* create associated serial port objects */
8585 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8586 {
8587 unconst(mSerialPorts[slot]).createObject();
8588 mSerialPorts[slot]->init(this, slot);
8589 }
8590
8591 /* create associated parallel port objects */
8592 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8593 {
8594 unconst(mParallelPorts[slot]).createObject();
8595 mParallelPorts[slot]->init(this, slot);
8596 }
8597
8598 /* create the audio adapter object (always present, default is disabled) */
8599 unconst(mAudioAdapter).createObject();
8600 mAudioAdapter->init(this);
8601
8602 /* create the USB device filters object (always present) */
8603 unconst(mUSBDeviceFilters).createObject();
8604 mUSBDeviceFilters->init(this);
8605
8606 /* create associated network adapter objects */
8607 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8608 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8609 {
8610 unconst(mNetworkAdapters[slot]).createObject();
8611 mNetworkAdapters[slot]->init(this, slot);
8612 }
8613
8614 /* create the bandwidth control */
8615 unconst(mBandwidthControl).createObject();
8616 mBandwidthControl->init(this);
8617
8618 return S_OK;
8619}
8620
8621/**
8622 * Helper to uninitialize all associated child objects and to free all data
8623 * structures.
8624 *
8625 * This method must be called as a part of the object's uninitialization
8626 * procedure (usually done in the #uninit() method).
8627 *
8628 * @note Must be called only from #uninit() or from #i_registeredInit().
8629 */
8630void Machine::uninitDataAndChildObjects()
8631{
8632 AutoCaller autoCaller(this);
8633 AssertComRCReturnVoid(autoCaller.rc());
8634 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8635 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8636 || getObjectState().getState() == ObjectState::InUninit
8637 || getObjectState().getState() == ObjectState::Limited);
8638
8639 /* tell all our other child objects we've been uninitialized */
8640 if (mBandwidthControl)
8641 {
8642 mBandwidthControl->uninit();
8643 unconst(mBandwidthControl).setNull();
8644 }
8645
8646 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8647 {
8648 if (mNetworkAdapters[slot])
8649 {
8650 mNetworkAdapters[slot]->uninit();
8651 unconst(mNetworkAdapters[slot]).setNull();
8652 }
8653 }
8654
8655 if (mUSBDeviceFilters)
8656 {
8657 mUSBDeviceFilters->uninit();
8658 unconst(mUSBDeviceFilters).setNull();
8659 }
8660
8661 if (mAudioAdapter)
8662 {
8663 mAudioAdapter->uninit();
8664 unconst(mAudioAdapter).setNull();
8665 }
8666
8667 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8668 {
8669 if (mParallelPorts[slot])
8670 {
8671 mParallelPorts[slot]->uninit();
8672 unconst(mParallelPorts[slot]).setNull();
8673 }
8674 }
8675
8676 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8677 {
8678 if (mSerialPorts[slot])
8679 {
8680 mSerialPorts[slot]->uninit();
8681 unconst(mSerialPorts[slot]).setNull();
8682 }
8683 }
8684
8685 if (mVRDEServer)
8686 {
8687 mVRDEServer->uninit();
8688 unconst(mVRDEServer).setNull();
8689 }
8690
8691 if (mGraphicsAdapter)
8692 {
8693 mGraphicsAdapter->uninit();
8694 unconst(mGraphicsAdapter).setNull();
8695 }
8696
8697 if (mBIOSSettings)
8698 {
8699 mBIOSSettings->uninit();
8700 unconst(mBIOSSettings).setNull();
8701 }
8702
8703 if (mTrustedPlatformModule)
8704 {
8705 mTrustedPlatformModule->uninit();
8706 unconst(mTrustedPlatformModule).setNull();
8707 }
8708
8709 if (mNvramStore)
8710 {
8711 mNvramStore->uninit();
8712 unconst(mNvramStore).setNull();
8713 }
8714
8715 if (mRecordingSettings)
8716 {
8717 mRecordingSettings->uninit();
8718 unconst(mRecordingSettings).setNull();
8719 }
8720
8721 /* Deassociate media (only when a real Machine or a SnapshotMachine
8722 * instance is uninitialized; SessionMachine instances refer to real
8723 * Machine media). This is necessary for a clean re-initialization of
8724 * the VM after successfully re-checking the accessibility state. Note
8725 * that in case of normal Machine or SnapshotMachine uninitialization (as
8726 * a result of unregistering or deleting the snapshot), outdated media
8727 * attachments will already be uninitialized and deleted, so this
8728 * code will not affect them. */
8729 if ( !mMediumAttachments.isNull()
8730 && !i_isSessionMachine()
8731 )
8732 {
8733 for (MediumAttachmentList::const_iterator
8734 it = mMediumAttachments->begin();
8735 it != mMediumAttachments->end();
8736 ++it)
8737 {
8738 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8739 if (pMedium.isNull())
8740 continue;
8741 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8742 AssertComRC(rc);
8743 }
8744 }
8745
8746 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8747 {
8748 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children)
8749 if (mData->mFirstSnapshot)
8750 {
8751 // Snapshots tree is protected by machine write lock.
8752 // Otherwise we assert in Snapshot::uninit()
8753 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8754 mData->mFirstSnapshot->uninit();
8755 mData->mFirstSnapshot.setNull();
8756 }
8757
8758 mData->mCurrentSnapshot.setNull();
8759 }
8760
8761 /* free data structures (the essential mData structure is not freed here
8762 * since it may be still in use) */
8763 mMediumAttachments.free();
8764 mStorageControllers.free();
8765 mUSBControllers.free();
8766 mHWData.free();
8767 mUserData.free();
8768 mSSData.free();
8769}
8770
8771/**
8772 * Returns a pointer to the Machine object for this machine that acts like a
8773 * parent for complex machine data objects such as shared folders, etc.
8774 *
8775 * For primary Machine objects and for SnapshotMachine objects, returns this
8776 * object's pointer itself. For SessionMachine objects, returns the peer
8777 * (primary) machine pointer.
8778 */
8779Machine *Machine::i_getMachine()
8780{
8781 if (i_isSessionMachine())
8782 return (Machine*)mPeer;
8783 return this;
8784}
8785
8786/**
8787 * Makes sure that there are no machine state dependents. If necessary, waits
8788 * for the number of dependents to drop to zero.
8789 *
8790 * Make sure this method is called from under this object's write lock to
8791 * guarantee that no new dependents may be added when this method returns
8792 * control to the caller.
8793 *
8794 * @note Receives a lock to this object for writing. The lock will be released
8795 * while waiting (if necessary).
8796 *
8797 * @warning To be used only in methods that change the machine state!
8798 */
8799void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8800{
8801 AssertReturnVoid(isWriteLockOnCurrentThread());
8802
8803 /* Wait for all state dependents if necessary */
8804 if (mData->mMachineStateDeps != 0)
8805 {
8806 /* lazy semaphore creation */
8807 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8808 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8809
8810 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8811 mData->mMachineStateDeps));
8812
8813 ++mData->mMachineStateChangePending;
8814
8815 /* reset the semaphore before waiting, the last dependent will signal
8816 * it */
8817 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8818
8819 alock.release();
8820
8821 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8822
8823 alock.acquire();
8824
8825 -- mData->mMachineStateChangePending;
8826 }
8827}
8828
8829/**
8830 * Changes the machine state and informs callbacks.
8831 *
8832 * This method is not intended to fail so it either returns S_OK or asserts (and
8833 * returns a failure).
8834 *
8835 * @note Locks this object for writing.
8836 */
8837HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8838{
8839 LogFlowThisFuncEnter();
8840 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8841 Assert(aMachineState != MachineState_Null);
8842
8843 AutoCaller autoCaller(this);
8844 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8845
8846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8847
8848 /* wait for state dependents to drop to zero */
8849 i_ensureNoStateDependencies(alock);
8850
8851 MachineState_T const enmOldState = mData->mMachineState;
8852 if (enmOldState != aMachineState)
8853 {
8854 mData->mMachineState = aMachineState;
8855 RTTimeNow(&mData->mLastStateChange);
8856
8857#ifdef VBOX_WITH_DTRACE_R3_MAIN
8858 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8859#endif
8860 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8861 }
8862
8863 LogFlowThisFuncLeave();
8864 return S_OK;
8865}
8866
8867/**
8868 * Searches for a shared folder with the given logical name
8869 * in the collection of shared folders.
8870 *
8871 * @param aName logical name of the shared folder
8872 * @param aSharedFolder where to return the found object
8873 * @param aSetError whether to set the error info if the folder is
8874 * not found
8875 * @return
8876 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8877 *
8878 * @note
8879 * must be called from under the object's lock!
8880 */
8881HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8882 ComObjPtr<SharedFolder> &aSharedFolder,
8883 bool aSetError /* = false */)
8884{
8885 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8886 for (HWData::SharedFolderList::const_iterator
8887 it = mHWData->mSharedFolders.begin();
8888 it != mHWData->mSharedFolders.end();
8889 ++it)
8890 {
8891 SharedFolder *pSF = *it;
8892 AutoCaller autoCaller(pSF);
8893 if (pSF->i_getName() == aName)
8894 {
8895 aSharedFolder = pSF;
8896 rc = S_OK;
8897 break;
8898 }
8899 }
8900
8901 if (aSetError && FAILED(rc))
8902 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8903
8904 return rc;
8905}
8906
8907/**
8908 * Initializes all machine instance data from the given settings structures
8909 * from XML. The exception is the machine UUID which needs special handling
8910 * depending on the caller's use case, so the caller needs to set that herself.
8911 *
8912 * This gets called in several contexts during machine initialization:
8913 *
8914 * -- When machine XML exists on disk already and needs to be loaded into memory,
8915 * for example, from #i_registeredInit() to load all registered machines on
8916 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8917 * attached to the machine should be part of some media registry already.
8918 *
8919 * -- During OVF import, when a machine config has been constructed from an
8920 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8921 * ensure that the media listed as attachments in the config (which have
8922 * been imported from the OVF) receive the correct registry ID.
8923 *
8924 * -- During VM cloning.
8925 *
8926 * @param config Machine settings from XML.
8927 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8928 * for each attached medium in the config.
8929 * @return
8930 */
8931HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8932 const Guid *puuidRegistry)
8933{
8934 // copy name, description, OS type, teleporter, UTC etc.
8935 mUserData->s = config.machineUserData;
8936
8937 // look up the object by Id to check it is valid
8938 ComObjPtr<GuestOSType> pGuestOSType;
8939 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8940 if (!pGuestOSType.isNull())
8941 mUserData->s.strOsType = pGuestOSType->i_id();
8942
8943#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
8944 // stateFile encryption (optional)
8945 mSSData->strStateKeyId = config.strStateKeyId;
8946 mSSData->strStateKeyStore = config.strStateKeyStore;
8947 mData->mstrLogKeyId = config.strLogKeyId;
8948 mData->mstrLogKeyStore = config.strLogKeyStore;
8949#endif
8950
8951 // stateFile (optional)
8952 if (config.strStateFile.isEmpty())
8953 mSSData->strStateFilePath.setNull();
8954 else
8955 {
8956 Utf8Str stateFilePathFull(config.strStateFile);
8957 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8958 if (RT_FAILURE(vrc))
8959 return setErrorBoth(E_FAIL, vrc,
8960 tr("Invalid saved state file path '%s' (%Rrc)"),
8961 config.strStateFile.c_str(),
8962 vrc);
8963 mSSData->strStateFilePath = stateFilePathFull;
8964 }
8965
8966 // snapshot folder needs special processing so set it again
8967 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8968 if (FAILED(rc)) return rc;
8969
8970 /* Copy the extra data items (config may or may not be the same as
8971 * mData->pMachineConfigFile) if necessary. When loading the XML files
8972 * from disk they are the same, but not for OVF import. */
8973 if (mData->pMachineConfigFile != &config)
8974 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8975
8976 /* currentStateModified (optional, default is true) */
8977 mData->mCurrentStateModified = config.fCurrentStateModified;
8978
8979 mData->mLastStateChange = config.timeLastStateChange;
8980
8981 /*
8982 * note: all mUserData members must be assigned prior this point because
8983 * we need to commit changes in order to let mUserData be shared by all
8984 * snapshot machine instances.
8985 */
8986 mUserData.commitCopy();
8987
8988 // machine registry, if present (must be loaded before snapshots)
8989 if (config.canHaveOwnMediaRegistry())
8990 {
8991 // determine machine folder
8992 Utf8Str strMachineFolder = i_getSettingsFileFull();
8993 strMachineFolder.stripFilename();
8994 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8995 config.mediaRegistry,
8996 strMachineFolder);
8997 if (FAILED(rc)) return rc;
8998 }
8999
9000 /* Snapshot node (optional) */
9001 size_t cRootSnapshots;
9002 if ((cRootSnapshots = config.llFirstSnapshot.size()))
9003 {
9004 // there must be only one root snapshot
9005 Assert(cRootSnapshots == 1);
9006 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9007
9008 rc = i_loadSnapshot(snap,
9009 config.uuidCurrentSnapshot);
9010 if (FAILED(rc)) return rc;
9011 }
9012
9013 // hardware data
9014 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
9015 if (FAILED(rc)) return rc;
9016
9017 /*
9018 * NOTE: the assignment below must be the last thing to do,
9019 * otherwise it will be not possible to change the settings
9020 * somewhere in the code above because all setters will be
9021 * blocked by i_checkStateDependency(MutableStateDep).
9022 */
9023
9024 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
9025 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
9026 {
9027 /* no need to use i_setMachineState() during init() */
9028 mData->mMachineState = MachineState_AbortedSaved;
9029 }
9030 else if (config.fAborted)
9031 {
9032 mSSData->strStateFilePath.setNull();
9033
9034 /* no need to use i_setMachineState() during init() */
9035 mData->mMachineState = MachineState_Aborted;
9036 }
9037 else if (!mSSData->strStateFilePath.isEmpty())
9038 {
9039 /* no need to use i_setMachineState() during init() */
9040 mData->mMachineState = MachineState_Saved;
9041 }
9042
9043 // after loading settings, we are no longer different from the XML on disk
9044 mData->flModifications = 0;
9045
9046 return S_OK;
9047}
9048
9049/**
9050 * Loads all snapshots starting from the given settings.
9051 *
9052 * @param data snapshot settings.
9053 * @param aCurSnapshotId Current snapshot ID from the settings file.
9054 */
9055HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
9056 const Guid &aCurSnapshotId)
9057{
9058 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
9059 AssertReturn(!i_isSessionMachine(), E_FAIL);
9060
9061 HRESULT rc = S_OK;
9062
9063 std::list<const settings::Snapshot *> llSettingsTodo;
9064 llSettingsTodo.push_back(&data);
9065 std::list<Snapshot *> llParentsTodo;
9066 llParentsTodo.push_back(NULL);
9067
9068 while (llSettingsTodo.size() > 0)
9069 {
9070 const settings::Snapshot *current = llSettingsTodo.front();
9071 llSettingsTodo.pop_front();
9072 Snapshot *pParent = llParentsTodo.front();
9073 llParentsTodo.pop_front();
9074
9075 Utf8Str strStateFile;
9076 if (!current->strStateFile.isEmpty())
9077 {
9078 /* optional */
9079 strStateFile = current->strStateFile;
9080 int vrc = i_calculateFullPath(strStateFile, strStateFile);
9081 if (RT_FAILURE(vrc))
9082 {
9083 setErrorBoth(E_FAIL, vrc,
9084 tr("Invalid saved state file path '%s' (%Rrc)"),
9085 strStateFile.c_str(), vrc);
9086 }
9087 }
9088
9089 /* create a snapshot machine object */
9090 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9091 pSnapshotMachine.createObject();
9092 rc = pSnapshotMachine->initFromSettings(this,
9093 current->hardware,
9094 &current->debugging,
9095 &current->autostart,
9096 current->uuid.ref(),
9097 strStateFile);
9098 if (FAILED(rc)) break;
9099
9100 /* create a snapshot object */
9101 ComObjPtr<Snapshot> pSnapshot;
9102 pSnapshot.createObject();
9103 /* initialize the snapshot */
9104 rc = pSnapshot->init(mParent, // VirtualBox object
9105 current->uuid,
9106 current->strName,
9107 current->strDescription,
9108 current->timestamp,
9109 pSnapshotMachine,
9110 pParent);
9111 if (FAILED(rc)) break;
9112
9113 /* memorize the first snapshot if necessary */
9114 if (!mData->mFirstSnapshot)
9115 {
9116 Assert(pParent == NULL);
9117 mData->mFirstSnapshot = pSnapshot;
9118 }
9119
9120 /* memorize the current snapshot when appropriate */
9121 if ( !mData->mCurrentSnapshot
9122 && pSnapshot->i_getId() == aCurSnapshotId
9123 )
9124 mData->mCurrentSnapshot = pSnapshot;
9125
9126 /* create all children */
9127 std::list<settings::Snapshot>::const_iterator itBegin = current->llChildSnapshots.begin();
9128 std::list<settings::Snapshot>::const_iterator itEnd = current->llChildSnapshots.end();
9129 for (std::list<settings::Snapshot>::const_iterator it = itBegin; it != itEnd; ++it)
9130 {
9131 llSettingsTodo.push_back(&*it);
9132 llParentsTodo.push_back(pSnapshot);
9133 }
9134 }
9135
9136 return rc;
9137}
9138
9139/**
9140 * Loads settings into mHWData.
9141 *
9142 * @param puuidRegistry Registry ID.
9143 * @param puuidSnapshot Snapshot ID
9144 * @param data Reference to the hardware settings.
9145 * @param pDbg Pointer to the debugging settings.
9146 * @param pAutostart Pointer to the autostart settings.
9147 */
9148HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
9149 const Guid *puuidSnapshot,
9150 const settings::Hardware &data,
9151 const settings::Debugging *pDbg,
9152 const settings::Autostart *pAutostart)
9153{
9154 AssertReturn(!i_isSessionMachine(), E_FAIL);
9155
9156 HRESULT rc = S_OK;
9157
9158 try
9159 {
9160 ComObjPtr<GuestOSType> pGuestOSType;
9161 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9162
9163 /* The hardware version attribute (optional). */
9164 mHWData->mHWVersion = data.strVersion;
9165 mHWData->mHardwareUUID = data.uuid;
9166
9167 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9168 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9169 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9170 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9171 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9172 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9173 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
9174 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
9175 mHWData->mPAEEnabled = data.fPAE;
9176 mHWData->mLongMode = data.enmLongMode;
9177 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9178 mHWData->mAPIC = data.fAPIC;
9179 mHWData->mX2APIC = data.fX2APIC;
9180 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9181 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9182 mHWData->mSpecCtrl = data.fSpecCtrl;
9183 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
9184 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
9185 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
9186 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
9187 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
9188 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9189 mHWData->mCPUCount = data.cCPUs;
9190 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9191 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9192 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9193 mHWData->mCpuProfile = data.strCpuProfile;
9194
9195 // cpu
9196 if (mHWData->mCPUHotPlugEnabled)
9197 {
9198 for (settings::CpuList::const_iterator
9199 it = data.llCpus.begin();
9200 it != data.llCpus.end();
9201 ++it)
9202 {
9203 const settings::Cpu &cpu = *it;
9204
9205 mHWData->mCPUAttached[cpu.ulId] = true;
9206 }
9207 }
9208
9209 // cpuid leafs
9210 for (settings::CpuIdLeafsList::const_iterator
9211 it = data.llCpuIdLeafs.begin();
9212 it != data.llCpuIdLeafs.end();
9213 ++it)
9214 {
9215 const settings::CpuIdLeaf &rLeaf= *it;
9216 if ( rLeaf.idx < UINT32_C(0x20)
9217 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9218 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9219 mHWData->mCpuIdLeafList.push_back(rLeaf);
9220 /* else: just ignore */
9221 }
9222
9223 mHWData->mMemorySize = data.ulMemorySizeMB;
9224 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9225
9226 // boot order
9227 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9228 {
9229 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9230 if (it == data.mapBootOrder.end())
9231 mHWData->mBootOrder[i] = DeviceType_Null;
9232 else
9233 mHWData->mBootOrder[i] = it->second;
9234 }
9235
9236 mHWData->mFirmwareType = data.firmwareType;
9237 mHWData->mPointingHIDType = data.pointingHIDType;
9238 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9239 mHWData->mChipsetType = data.chipsetType;
9240 mHWData->mIommuType = data.iommuType;
9241 mHWData->mParavirtProvider = data.paravirtProvider;
9242 mHWData->mParavirtDebug = data.strParavirtDebug;
9243 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9244 mHWData->mHPETEnabled = data.fHPETEnabled;
9245
9246 /* GraphicsAdapter */
9247 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
9248 if (FAILED(rc)) return rc;
9249
9250 /* VRDEServer */
9251 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9252 if (FAILED(rc)) return rc;
9253
9254 /* BIOS */
9255 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9256 if (FAILED(rc)) return rc;
9257
9258 /* Trusted Platform Module */
9259 rc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
9260 if (FAILED(rc)) return rc;
9261
9262 rc = mNvramStore->i_loadSettings(data.nvramSettings);
9263 if (FAILED(rc)) return rc;
9264
9265 /* Recording settings */
9266 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
9267 if (FAILED(rc)) return rc;
9268
9269 // Bandwidth control (must come before network adapters)
9270 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9271 if (FAILED(rc)) return rc;
9272
9273 /* USB controllers */
9274 for (settings::USBControllerList::const_iterator
9275 it = data.usbSettings.llUSBControllers.begin();
9276 it != data.usbSettings.llUSBControllers.end();
9277 ++it)
9278 {
9279 const settings::USBController &settingsCtrl = *it;
9280 ComObjPtr<USBController> newCtrl;
9281
9282 newCtrl.createObject();
9283 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9284 mUSBControllers->push_back(newCtrl);
9285 }
9286
9287 /* USB device filters */
9288 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9289 if (FAILED(rc)) return rc;
9290
9291 // network adapters (establish array size first and apply defaults, to
9292 // ensure reading the same settings as we saved, since the list skips
9293 // adapters having defaults)
9294 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9295 size_t oldCount = mNetworkAdapters.size();
9296 if (newCount > oldCount)
9297 {
9298 mNetworkAdapters.resize(newCount);
9299 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9300 {
9301 unconst(mNetworkAdapters[slot]).createObject();
9302 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9303 }
9304 }
9305 else if (newCount < oldCount)
9306 mNetworkAdapters.resize(newCount);
9307 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9308 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9309 for (settings::NetworkAdaptersList::const_iterator
9310 it = data.llNetworkAdapters.begin();
9311 it != data.llNetworkAdapters.end();
9312 ++it)
9313 {
9314 const settings::NetworkAdapter &nic = *it;
9315
9316 /* slot uniqueness is guaranteed by XML Schema */
9317 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9318 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9319 if (FAILED(rc)) return rc;
9320 }
9321
9322 // serial ports (establish defaults first, to ensure reading the same
9323 // settings as we saved, since the list skips ports having defaults)
9324 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9325 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9326 for (settings::SerialPortsList::const_iterator
9327 it = data.llSerialPorts.begin();
9328 it != data.llSerialPorts.end();
9329 ++it)
9330 {
9331 const settings::SerialPort &s = *it;
9332
9333 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9334 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9335 if (FAILED(rc)) return rc;
9336 }
9337
9338 // parallel ports (establish defaults first, to ensure reading the same
9339 // settings as we saved, since the list skips ports having defaults)
9340 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9341 mParallelPorts[i]->i_applyDefaults();
9342 for (settings::ParallelPortsList::const_iterator
9343 it = data.llParallelPorts.begin();
9344 it != data.llParallelPorts.end();
9345 ++it)
9346 {
9347 const settings::ParallelPort &p = *it;
9348
9349 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9350 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9351 if (FAILED(rc)) return rc;
9352 }
9353
9354 /* AudioAdapter */
9355 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9356 if (FAILED(rc)) return rc;
9357
9358 /* storage controllers */
9359 rc = i_loadStorageControllers(data.storage,
9360 puuidRegistry,
9361 puuidSnapshot);
9362 if (FAILED(rc)) return rc;
9363
9364 /* Shared folders */
9365 for (settings::SharedFoldersList::const_iterator
9366 it = data.llSharedFolders.begin();
9367 it != data.llSharedFolders.end();
9368 ++it)
9369 {
9370 const settings::SharedFolder &sf = *it;
9371
9372 ComObjPtr<SharedFolder> sharedFolder;
9373 /* Check for double entries. Not allowed! */
9374 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9375 if (SUCCEEDED(rc))
9376 return setError(VBOX_E_OBJECT_IN_USE,
9377 tr("Shared folder named '%s' already exists"),
9378 sf.strName.c_str());
9379
9380 /* Create the new shared folder. Don't break on error. This will be
9381 * reported when the machine starts. */
9382 sharedFolder.createObject();
9383 rc = sharedFolder->init(i_getMachine(),
9384 sf.strName,
9385 sf.strHostPath,
9386 RT_BOOL(sf.fWritable),
9387 RT_BOOL(sf.fAutoMount),
9388 sf.strAutoMountPoint,
9389 false /* fFailOnError */);
9390 if (FAILED(rc)) return rc;
9391 mHWData->mSharedFolders.push_back(sharedFolder);
9392 }
9393
9394 // Clipboard
9395 mHWData->mClipboardMode = data.clipboardMode;
9396 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9397
9398 // drag'n'drop
9399 mHWData->mDnDMode = data.dndMode;
9400
9401 // guest settings
9402 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9403
9404 // IO settings
9405 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9406 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9407
9408 // Host PCI devices
9409 for (settings::HostPCIDeviceAttachmentList::const_iterator
9410 it = data.pciAttachments.begin();
9411 it != data.pciAttachments.end();
9412 ++it)
9413 {
9414 const settings::HostPCIDeviceAttachment &hpda = *it;
9415 ComObjPtr<PCIDeviceAttachment> pda;
9416
9417 pda.createObject();
9418 pda->i_loadSettings(this, hpda);
9419 mHWData->mPCIDeviceAssignments.push_back(pda);
9420 }
9421
9422 /*
9423 * (The following isn't really real hardware, but it lives in HWData
9424 * for reasons of convenience.)
9425 */
9426
9427#ifdef VBOX_WITH_GUEST_PROPS
9428 /* Guest properties (optional) */
9429
9430 /* Only load transient guest properties for configs which have saved
9431 * state, because there shouldn't be any for powered off VMs. The same
9432 * logic applies for snapshots, as offline snapshots shouldn't have
9433 * any such properties. They confuse the code in various places.
9434 * Note: can't rely on the machine state, as it isn't set yet. */
9435 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9436 /* apologies for the hacky unconst() usage, but this needs hacking
9437 * actually inconsistent settings into consistency, otherwise there
9438 * will be some corner cases where the inconsistency survives
9439 * surprisingly long without getting fixed, especially for snapshots
9440 * as there are no config changes. */
9441 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9442 for (settings::GuestPropertiesList::iterator
9443 it = llGuestProperties.begin();
9444 it != llGuestProperties.end();
9445 /*nothing*/)
9446 {
9447 const settings::GuestProperty &prop = *it;
9448 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9449 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9450 if ( fSkipTransientGuestProperties
9451 && ( fFlags & GUEST_PROP_F_TRANSIENT
9452 || fFlags & GUEST_PROP_F_TRANSRESET))
9453 {
9454 it = llGuestProperties.erase(it);
9455 continue;
9456 }
9457 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9458 mHWData->mGuestProperties[prop.strName] = property;
9459 ++it;
9460 }
9461#endif /* VBOX_WITH_GUEST_PROPS defined */
9462
9463 rc = i_loadDebugging(pDbg);
9464 if (FAILED(rc))
9465 return rc;
9466
9467 mHWData->mAutostart = *pAutostart;
9468
9469 /* default frontend */
9470 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9471 }
9472 catch (std::bad_alloc &)
9473 {
9474 return E_OUTOFMEMORY;
9475 }
9476
9477 AssertComRC(rc);
9478 return rc;
9479}
9480
9481/**
9482 * Called from i_loadHardware() to load the debugging settings of the
9483 * machine.
9484 *
9485 * @param pDbg Pointer to the settings.
9486 */
9487HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9488{
9489 mHWData->mDebugging = *pDbg;
9490 /* no more processing currently required, this will probably change. */
9491 return S_OK;
9492}
9493
9494/**
9495 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9496 *
9497 * @param data storage settings.
9498 * @param puuidRegistry media registry ID to set media to or NULL;
9499 * see Machine::i_loadMachineDataFromSettings()
9500 * @param puuidSnapshot snapshot ID
9501 * @return
9502 */
9503HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9504 const Guid *puuidRegistry,
9505 const Guid *puuidSnapshot)
9506{
9507 AssertReturn(!i_isSessionMachine(), E_FAIL);
9508
9509 HRESULT rc = S_OK;
9510
9511 for (settings::StorageControllersList::const_iterator
9512 it = data.llStorageControllers.begin();
9513 it != data.llStorageControllers.end();
9514 ++it)
9515 {
9516 const settings::StorageController &ctlData = *it;
9517
9518 ComObjPtr<StorageController> pCtl;
9519 /* Try to find one with the name first. */
9520 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9521 if (SUCCEEDED(rc))
9522 return setError(VBOX_E_OBJECT_IN_USE,
9523 tr("Storage controller named '%s' already exists"),
9524 ctlData.strName.c_str());
9525
9526 pCtl.createObject();
9527 rc = pCtl->init(this,
9528 ctlData.strName,
9529 ctlData.storageBus,
9530 ctlData.ulInstance,
9531 ctlData.fBootable);
9532 if (FAILED(rc)) return rc;
9533
9534 mStorageControllers->push_back(pCtl);
9535
9536 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9537 if (FAILED(rc)) return rc;
9538
9539 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9540 if (FAILED(rc)) return rc;
9541
9542 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9543 if (FAILED(rc)) return rc;
9544
9545 /* Load the attached devices now. */
9546 rc = i_loadStorageDevices(pCtl,
9547 ctlData,
9548 puuidRegistry,
9549 puuidSnapshot);
9550 if (FAILED(rc)) return rc;
9551 }
9552
9553 return S_OK;
9554}
9555
9556/**
9557 * Called from i_loadStorageControllers for a controller's devices.
9558 *
9559 * @param aStorageController
9560 * @param data
9561 * @param puuidRegistry media registry ID to set media to or NULL; see
9562 * Machine::i_loadMachineDataFromSettings()
9563 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9564 * @return
9565 */
9566HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9567 const settings::StorageController &data,
9568 const Guid *puuidRegistry,
9569 const Guid *puuidSnapshot)
9570{
9571 HRESULT rc = S_OK;
9572
9573 /* paranoia: detect duplicate attachments */
9574 for (settings::AttachedDevicesList::const_iterator
9575 it = data.llAttachedDevices.begin();
9576 it != data.llAttachedDevices.end();
9577 ++it)
9578 {
9579 const settings::AttachedDevice &ad = *it;
9580
9581 for (settings::AttachedDevicesList::const_iterator it2 = it;
9582 it2 != data.llAttachedDevices.end();
9583 ++it2)
9584 {
9585 if (it == it2)
9586 continue;
9587
9588 const settings::AttachedDevice &ad2 = *it2;
9589
9590 if ( ad.lPort == ad2.lPort
9591 && ad.lDevice == ad2.lDevice)
9592 {
9593 return setError(E_FAIL,
9594 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9595 aStorageController->i_getName().c_str(),
9596 ad.lPort,
9597 ad.lDevice,
9598 mUserData->s.strName.c_str());
9599 }
9600 }
9601 }
9602
9603 for (settings::AttachedDevicesList::const_iterator
9604 it = data.llAttachedDevices.begin();
9605 it != data.llAttachedDevices.end();
9606 ++it)
9607 {
9608 const settings::AttachedDevice &dev = *it;
9609 ComObjPtr<Medium> medium;
9610
9611 switch (dev.deviceType)
9612 {
9613 case DeviceType_Floppy:
9614 case DeviceType_DVD:
9615 if (dev.strHostDriveSrc.isNotEmpty())
9616 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9617 false /* fRefresh */, medium);
9618 else
9619 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9620 dev.uuid,
9621 false /* fRefresh */,
9622 false /* aSetError */,
9623 medium);
9624 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9625 // This is not an error. The host drive or UUID might have vanished, so just go
9626 // ahead without this removeable medium attachment
9627 rc = S_OK;
9628 break;
9629
9630 case DeviceType_HardDisk:
9631 {
9632 /* find a hard disk by UUID */
9633 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9634 if (FAILED(rc))
9635 {
9636 if (i_isSnapshotMachine())
9637 {
9638 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9639 // so the user knows that the bad disk is in a snapshot somewhere
9640 com::ErrorInfo info;
9641 return setError(E_FAIL,
9642 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9643 puuidSnapshot->raw(),
9644 info.getText().raw());
9645 }
9646 else
9647 return rc;
9648 }
9649
9650 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9651
9652 if (medium->i_getType() == MediumType_Immutable)
9653 {
9654 if (i_isSnapshotMachine())
9655 return setError(E_FAIL,
9656 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9657 "of the virtual machine '%s' ('%s')"),
9658 medium->i_getLocationFull().c_str(),
9659 dev.uuid.raw(),
9660 puuidSnapshot->raw(),
9661 mUserData->s.strName.c_str(),
9662 mData->m_strConfigFileFull.c_str());
9663
9664 return setError(E_FAIL,
9665 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9666 medium->i_getLocationFull().c_str(),
9667 dev.uuid.raw(),
9668 mUserData->s.strName.c_str(),
9669 mData->m_strConfigFileFull.c_str());
9670 }
9671
9672 if (medium->i_getType() == MediumType_MultiAttach)
9673 {
9674 if (i_isSnapshotMachine())
9675 return setError(E_FAIL,
9676 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9677 "of the virtual machine '%s' ('%s')"),
9678 medium->i_getLocationFull().c_str(),
9679 dev.uuid.raw(),
9680 puuidSnapshot->raw(),
9681 mUserData->s.strName.c_str(),
9682 mData->m_strConfigFileFull.c_str());
9683
9684 return setError(E_FAIL,
9685 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9686 medium->i_getLocationFull().c_str(),
9687 dev.uuid.raw(),
9688 mUserData->s.strName.c_str(),
9689 mData->m_strConfigFileFull.c_str());
9690 }
9691
9692 if ( !i_isSnapshotMachine()
9693 && medium->i_getChildren().size() != 0
9694 )
9695 return setError(E_FAIL,
9696 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9697 "because it has %d differencing child hard disks"),
9698 medium->i_getLocationFull().c_str(),
9699 dev.uuid.raw(),
9700 mUserData->s.strName.c_str(),
9701 mData->m_strConfigFileFull.c_str(),
9702 medium->i_getChildren().size());
9703
9704 if (i_findAttachment(*mMediumAttachments.data(),
9705 medium))
9706 return setError(E_FAIL,
9707 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9708 medium->i_getLocationFull().c_str(),
9709 dev.uuid.raw(),
9710 mUserData->s.strName.c_str(),
9711 mData->m_strConfigFileFull.c_str());
9712
9713 break;
9714 }
9715
9716 default:
9717 return setError(E_FAIL,
9718 tr("Controller '%s' port %u unit %u has device with unknown type (%d) - virtual machine '%s' ('%s')"),
9719 data.strName.c_str(), dev.lPort, dev.lDevice, dev.deviceType,
9720 mUserData->s.strName.c_str(), mData->m_strConfigFileFull.c_str());
9721 }
9722
9723 if (FAILED(rc))
9724 break;
9725
9726 /* Bandwidth groups are loaded at this point. */
9727 ComObjPtr<BandwidthGroup> pBwGroup;
9728
9729 if (!dev.strBwGroup.isEmpty())
9730 {
9731 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9732 if (FAILED(rc))
9733 return setError(E_FAIL,
9734 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9735 medium->i_getLocationFull().c_str(),
9736 dev.strBwGroup.c_str(),
9737 mUserData->s.strName.c_str(),
9738 mData->m_strConfigFileFull.c_str());
9739 pBwGroup->i_reference();
9740 }
9741
9742 const Utf8Str controllerName = aStorageController->i_getName();
9743 ComObjPtr<MediumAttachment> pAttachment;
9744 pAttachment.createObject();
9745 rc = pAttachment->init(this,
9746 medium,
9747 controllerName,
9748 dev.lPort,
9749 dev.lDevice,
9750 dev.deviceType,
9751 false,
9752 dev.fPassThrough,
9753 dev.fTempEject,
9754 dev.fNonRotational,
9755 dev.fDiscard,
9756 dev.fHotPluggable,
9757 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9758 if (FAILED(rc)) break;
9759
9760 /* associate the medium with this machine and snapshot */
9761 if (!medium.isNull())
9762 {
9763 AutoCaller medCaller(medium);
9764 if (FAILED(medCaller.rc())) return medCaller.rc();
9765 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9766
9767 if (i_isSnapshotMachine())
9768 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9769 else
9770 rc = medium->i_addBackReference(mData->mUuid);
9771 /* If the medium->addBackReference fails it sets an appropriate
9772 * error message, so no need to do any guesswork here. */
9773
9774 if (puuidRegistry)
9775 // caller wants registry ID to be set on all attached media (OVF import case)
9776 medium->i_addRegistry(*puuidRegistry);
9777 }
9778
9779 if (FAILED(rc))
9780 break;
9781
9782 /* back up mMediumAttachments to let registeredInit() properly rollback
9783 * on failure (= limited accessibility) */
9784 i_setModified(IsModified_Storage);
9785 mMediumAttachments.backup();
9786 mMediumAttachments->push_back(pAttachment);
9787 }
9788
9789 return rc;
9790}
9791
9792/**
9793 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9794 *
9795 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9796 * @param aSnapshot where to return the found snapshot
9797 * @param aSetError true to set extended error info on failure
9798 */
9799HRESULT Machine::i_findSnapshotById(const Guid &aId,
9800 ComObjPtr<Snapshot> &aSnapshot,
9801 bool aSetError /* = false */)
9802{
9803 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9804
9805 if (!mData->mFirstSnapshot)
9806 {
9807 if (aSetError)
9808 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9809 return E_FAIL;
9810 }
9811
9812 if (aId.isZero())
9813 aSnapshot = mData->mFirstSnapshot;
9814 else
9815 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9816
9817 if (!aSnapshot)
9818 {
9819 if (aSetError)
9820 return setError(E_FAIL,
9821 tr("Could not find a snapshot with UUID {%s}"),
9822 aId.toString().c_str());
9823 return E_FAIL;
9824 }
9825
9826 return S_OK;
9827}
9828
9829/**
9830 * Returns the snapshot with the given name or fails of no such snapshot.
9831 *
9832 * @param strName snapshot name to find
9833 * @param aSnapshot where to return the found snapshot
9834 * @param aSetError true to set extended error info on failure
9835 */
9836HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9837 ComObjPtr<Snapshot> &aSnapshot,
9838 bool aSetError /* = false */)
9839{
9840 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9841
9842 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9843
9844 if (!mData->mFirstSnapshot)
9845 {
9846 if (aSetError)
9847 return setError(VBOX_E_OBJECT_NOT_FOUND,
9848 tr("This machine does not have any snapshots"));
9849 return VBOX_E_OBJECT_NOT_FOUND;
9850 }
9851
9852 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9853
9854 if (!aSnapshot)
9855 {
9856 if (aSetError)
9857 return setError(VBOX_E_OBJECT_NOT_FOUND,
9858 tr("Could not find a snapshot named '%s'"), strName.c_str());
9859 return VBOX_E_OBJECT_NOT_FOUND;
9860 }
9861
9862 return S_OK;
9863}
9864
9865/**
9866 * Returns a storage controller object with the given name.
9867 *
9868 * @param aName storage controller name to find
9869 * @param aStorageController where to return the found storage controller
9870 * @param aSetError true to set extended error info on failure
9871 */
9872HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9873 ComObjPtr<StorageController> &aStorageController,
9874 bool aSetError /* = false */)
9875{
9876 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9877
9878 for (StorageControllerList::const_iterator
9879 it = mStorageControllers->begin();
9880 it != mStorageControllers->end();
9881 ++it)
9882 {
9883 if ((*it)->i_getName() == aName)
9884 {
9885 aStorageController = (*it);
9886 return S_OK;
9887 }
9888 }
9889
9890 if (aSetError)
9891 return setError(VBOX_E_OBJECT_NOT_FOUND,
9892 tr("Could not find a storage controller named '%s'"),
9893 aName.c_str());
9894 return VBOX_E_OBJECT_NOT_FOUND;
9895}
9896
9897/**
9898 * Returns a USB controller object with the given name.
9899 *
9900 * @param aName USB controller name to find
9901 * @param aUSBController where to return the found USB controller
9902 * @param aSetError true to set extended error info on failure
9903 */
9904HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9905 ComObjPtr<USBController> &aUSBController,
9906 bool aSetError /* = false */)
9907{
9908 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9909
9910 for (USBControllerList::const_iterator
9911 it = mUSBControllers->begin();
9912 it != mUSBControllers->end();
9913 ++it)
9914 {
9915 if ((*it)->i_getName() == aName)
9916 {
9917 aUSBController = (*it);
9918 return S_OK;
9919 }
9920 }
9921
9922 if (aSetError)
9923 return setError(VBOX_E_OBJECT_NOT_FOUND,
9924 tr("Could not find a storage controller named '%s'"),
9925 aName.c_str());
9926 return VBOX_E_OBJECT_NOT_FOUND;
9927}
9928
9929/**
9930 * Returns the number of USB controller instance of the given type.
9931 *
9932 * @param enmType USB controller type.
9933 */
9934ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9935{
9936 ULONG cCtrls = 0;
9937
9938 for (USBControllerList::const_iterator
9939 it = mUSBControllers->begin();
9940 it != mUSBControllers->end();
9941 ++it)
9942 {
9943 if ((*it)->i_getControllerType() == enmType)
9944 cCtrls++;
9945 }
9946
9947 return cCtrls;
9948}
9949
9950HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9951 MediumAttachmentList &atts)
9952{
9953 AutoCaller autoCaller(this);
9954 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9955
9956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9957
9958 for (MediumAttachmentList::const_iterator
9959 it = mMediumAttachments->begin();
9960 it != mMediumAttachments->end();
9961 ++it)
9962 {
9963 const ComObjPtr<MediumAttachment> &pAtt = *it;
9964 // should never happen, but deal with NULL pointers in the list.
9965 AssertContinue(!pAtt.isNull());
9966
9967 // getControllerName() needs caller+read lock
9968 AutoCaller autoAttCaller(pAtt);
9969 if (FAILED(autoAttCaller.rc()))
9970 {
9971 atts.clear();
9972 return autoAttCaller.rc();
9973 }
9974 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9975
9976 if (pAtt->i_getControllerName() == aName)
9977 atts.push_back(pAtt);
9978 }
9979
9980 return S_OK;
9981}
9982
9983
9984/**
9985 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9986 * file if the machine name was changed and about creating a new settings file
9987 * if this is a new machine.
9988 *
9989 * @note Must be never called directly but only from #saveSettings().
9990 */
9991HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
9992 bool *pfSettingsFileIsNew)
9993{
9994 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9995
9996 HRESULT rc = S_OK;
9997
9998 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9999 /// @todo need to handle primary group change, too
10000
10001 /* attempt to rename the settings file if machine name is changed */
10002 if ( mUserData->s.fNameSync
10003 && mUserData.isBackedUp()
10004 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
10005 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
10006 )
10007 {
10008 bool dirRenamed = false;
10009 bool fileRenamed = false;
10010
10011 Utf8Str configFile, newConfigFile;
10012 Utf8Str configFilePrev, newConfigFilePrev;
10013 Utf8Str NVRAMFile, newNVRAMFile;
10014 Utf8Str configDir, newConfigDir;
10015
10016 do
10017 {
10018 int vrc = VINF_SUCCESS;
10019
10020 Utf8Str name = mUserData.backedUpData()->s.strName;
10021 Utf8Str newName = mUserData->s.strName;
10022 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
10023 if (group == "/")
10024 group.setNull();
10025 Utf8Str newGroup = mUserData->s.llGroups.front();
10026 if (newGroup == "/")
10027 newGroup.setNull();
10028
10029 configFile = mData->m_strConfigFileFull;
10030
10031 /* first, rename the directory if it matches the group and machine name */
10032 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
10033 /** @todo hack, make somehow use of ComposeMachineFilename */
10034 if (mUserData->s.fDirectoryIncludesUUID)
10035 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10036 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
10037 /** @todo hack, make somehow use of ComposeMachineFilename */
10038 if (mUserData->s.fDirectoryIncludesUUID)
10039 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10040 configDir = configFile;
10041 configDir.stripFilename();
10042 newConfigDir = configDir;
10043 if ( configDir.length() >= groupPlusName.length()
10044 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
10045 groupPlusName.c_str()))
10046 {
10047 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10048 Utf8Str newConfigBaseDir(newConfigDir);
10049 newConfigDir.append(newGroupPlusName);
10050 /* consistency: use \ if appropriate on the platform */
10051 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10052 /* new dir and old dir cannot be equal here because of 'if'
10053 * above and because name != newName */
10054 Assert(configDir != newConfigDir);
10055 if (!fSettingsFileIsNew)
10056 {
10057 /* perform real rename only if the machine is not new */
10058 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10059 if ( vrc == VERR_FILE_NOT_FOUND
10060 || vrc == VERR_PATH_NOT_FOUND)
10061 {
10062 /* create the parent directory, then retry renaming */
10063 Utf8Str parent(newConfigDir);
10064 parent.stripFilename();
10065 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10066 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10067 }
10068 if (RT_FAILURE(vrc))
10069 {
10070 rc = setErrorBoth(E_FAIL, vrc,
10071 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10072 configDir.c_str(),
10073 newConfigDir.c_str(),
10074 vrc);
10075 break;
10076 }
10077 /* delete subdirectories which are no longer needed */
10078 Utf8Str dir(configDir);
10079 dir.stripFilename();
10080 while (dir != newConfigBaseDir && dir != ".")
10081 {
10082 vrc = RTDirRemove(dir.c_str());
10083 if (RT_FAILURE(vrc))
10084 break;
10085 dir.stripFilename();
10086 }
10087 dirRenamed = true;
10088 }
10089 }
10090
10091 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10092
10093 /* then try to rename the settings file itself */
10094 if (newConfigFile != configFile)
10095 {
10096 /* get the path to old settings file in renamed directory */
10097 Assert(mData->m_strConfigFileFull == configFile);
10098 configFile.printf("%s%c%s",
10099 newConfigDir.c_str(),
10100 RTPATH_DELIMITER,
10101 RTPathFilename(mData->m_strConfigFileFull.c_str()));
10102 if (!fSettingsFileIsNew)
10103 {
10104 /* perform real rename only if the machine is not new */
10105 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10106 if (RT_FAILURE(vrc))
10107 {
10108 rc = setErrorBoth(E_FAIL, vrc,
10109 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10110 configFile.c_str(),
10111 newConfigFile.c_str(),
10112 vrc);
10113 break;
10114 }
10115 fileRenamed = true;
10116 configFilePrev = configFile;
10117 configFilePrev += "-prev";
10118 newConfigFilePrev = newConfigFile;
10119 newConfigFilePrev += "-prev";
10120 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10121 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
10122 if (NVRAMFile.isNotEmpty())
10123 {
10124 // in the NVRAM file path, replace the old directory with the new directory
10125 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
10126 {
10127 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
10128 NVRAMFile = newConfigDir + strNVRAMFile;
10129 }
10130 newNVRAMFile = newConfigFile;
10131 newNVRAMFile.stripSuffix();
10132 newNVRAMFile += ".nvram";
10133 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
10134 }
10135 }
10136 }
10137
10138 // update m_strConfigFileFull amd mConfigFile
10139 mData->m_strConfigFileFull = newConfigFile;
10140 // compute the relative path too
10141 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10142
10143 // store the old and new so that VirtualBox::i_saveSettings() can update
10144 // the media registry
10145 if ( mData->mRegistered
10146 && (configDir != newConfigDir || configFile != newConfigFile))
10147 {
10148 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10149
10150 if (pfNeedsGlobalSaveSettings)
10151 *pfNeedsGlobalSaveSettings = true;
10152 }
10153
10154 // in the saved state file path, replace the old directory with the new directory
10155 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10156 {
10157 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
10158 mSSData->strStateFilePath = newConfigDir + strStateFileName;
10159 }
10160 if (newNVRAMFile.isNotEmpty())
10161 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
10162
10163 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
10164 if (mData->mFirstSnapshot)
10165 {
10166 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10167 newConfigDir.c_str());
10168 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
10169 newConfigDir.c_str());
10170 }
10171 }
10172 while (0);
10173
10174 if (FAILED(rc))
10175 {
10176 /* silently try to rename everything back */
10177 if (fileRenamed)
10178 {
10179 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10180 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10181 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
10182 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
10183 }
10184 if (dirRenamed)
10185 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10186 }
10187
10188 if (FAILED(rc)) return rc;
10189 }
10190
10191 if (fSettingsFileIsNew)
10192 {
10193 /* create a virgin config file */
10194 int vrc = VINF_SUCCESS;
10195
10196 /* ensure the settings directory exists */
10197 Utf8Str path(mData->m_strConfigFileFull);
10198 path.stripFilename();
10199 if (!RTDirExists(path.c_str()))
10200 {
10201 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10202 if (RT_FAILURE(vrc))
10203 {
10204 return setErrorBoth(E_FAIL, vrc,
10205 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10206 path.c_str(),
10207 vrc);
10208 }
10209 }
10210
10211 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10212 path = mData->m_strConfigFileFull;
10213 RTFILE f = NIL_RTFILE;
10214 vrc = RTFileOpen(&f, path.c_str(),
10215 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10216 if (RT_FAILURE(vrc))
10217 return setErrorBoth(E_FAIL, vrc,
10218 tr("Could not create the settings file '%s' (%Rrc)"),
10219 path.c_str(),
10220 vrc);
10221 RTFileClose(f);
10222 }
10223 if (pfSettingsFileIsNew)
10224 *pfSettingsFileIsNew = fSettingsFileIsNew;
10225
10226 return rc;
10227}
10228
10229/**
10230 * Saves and commits machine data, user data and hardware data.
10231 *
10232 * Note that on failure, the data remains uncommitted.
10233 *
10234 * @a aFlags may combine the following flags:
10235 *
10236 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10237 * Used when saving settings after an operation that makes them 100%
10238 * correspond to the settings from the current snapshot.
10239 * - SaveS_Force: settings will be saved without doing a deep compare of the
10240 * settings structures. This is used when this is called because snapshots
10241 * have changed to avoid the overhead of the deep compare.
10242 *
10243 * @note Must be called from under this object's write lock. Locks children for
10244 * writing.
10245 *
10246 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10247 * initialized to false and that will be set to true by this function if
10248 * the caller must invoke VirtualBox::i_saveSettings() because the global
10249 * settings have changed. This will happen if a machine rename has been
10250 * saved and the global machine and media registries will therefore need
10251 * updating.
10252 * @param alock Reference to the lock for this machine object.
10253 * @param aFlags Flags.
10254 */
10255HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10256 AutoWriteLock &alock,
10257 int aFlags /*= 0*/)
10258{
10259 LogFlowThisFuncEnter();
10260
10261 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10262
10263 /* make sure child objects are unable to modify the settings while we are
10264 * saving them */
10265 i_ensureNoStateDependencies(alock);
10266
10267 AssertReturn(!i_isSnapshotMachine(),
10268 E_FAIL);
10269
10270 if (!mData->mAccessible)
10271 return setError(VBOX_E_INVALID_VM_STATE,
10272 tr("The machine is not accessible, so cannot save settings"));
10273
10274 HRESULT rc = S_OK;
10275 PCVBOXCRYPTOIF pCryptoIf = NULL;
10276 const char *pszPassword = NULL;
10277 SecretKey *pKey = NULL;
10278
10279#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10280 if (mData->mstrKeyId.isNotEmpty())
10281 {
10282 /* VM is going to be encrypted. */
10283 alock.release(); /** @todo Revise the locking. */
10284 rc = mParent->i_retainCryptoIf(&pCryptoIf);
10285 alock.acquire();
10286 if (FAILED(rc)) return rc; /* Error is set. */
10287
10288 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
10289 if (RT_SUCCESS(vrc))
10290 pszPassword = (const char *)pKey->getKeyBuffer();
10291 else
10292 {
10293 mParent->i_releaseCryptoIf(pCryptoIf);
10294 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
10295 tr("Failed to retain VM encryption password using ID '%s' with %Rrc"),
10296 mData->mstrKeyId.c_str(), vrc);
10297 }
10298 }
10299#else
10300 RT_NOREF(pKey);
10301#endif
10302
10303 bool fNeedsWrite = false;
10304 bool fSettingsFileIsNew = false;
10305
10306 /* First, prepare to save settings. It will care about renaming the
10307 * settings directory and file if the machine name was changed and about
10308 * creating a new settings file if this is a new machine. */
10309 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings,
10310 &fSettingsFileIsNew);
10311 if (FAILED(rc))
10312 {
10313#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10314 if (pCryptoIf)
10315 {
10316 alock.release(); /** @todo Revise the locking. */
10317 mParent->i_releaseCryptoIf(pCryptoIf);
10318 alock.acquire();
10319 }
10320 if (pKey)
10321 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10322#endif
10323 return rc;
10324 }
10325
10326 // keep a pointer to the current settings structures
10327 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10328 settings::MachineConfigFile *pNewConfig = NULL;
10329
10330 try
10331 {
10332 // make a fresh one to have everyone write stuff into
10333 pNewConfig = new settings::MachineConfigFile(NULL);
10334 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10335#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10336 pNewConfig->strKeyId = mData->mstrKeyId;
10337 pNewConfig->strKeyStore = mData->mstrKeyStore;
10338#endif
10339
10340 // now go and copy all the settings data from COM to the settings structures
10341 // (this calls i_saveSettings() on all the COM objects in the machine)
10342 i_copyMachineDataToSettings(*pNewConfig);
10343
10344 if (aFlags & SaveS_ResetCurStateModified)
10345 {
10346 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10347 mData->mCurrentStateModified = FALSE;
10348 fNeedsWrite = true; // always, no need to compare
10349 }
10350 else if (aFlags & SaveS_Force)
10351 {
10352 fNeedsWrite = true; // always, no need to compare
10353 }
10354 else
10355 {
10356 if (!mData->mCurrentStateModified)
10357 {
10358 // do a deep compare of the settings that we just saved with the settings
10359 // previously stored in the config file; this invokes MachineConfigFile::operator==
10360 // which does a deep compare of all the settings, which is expensive but less expensive
10361 // than writing out XML in vain
10362 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10363
10364 // could still be modified if any settings changed
10365 mData->mCurrentStateModified = fAnySettingsChanged;
10366
10367 fNeedsWrite = fAnySettingsChanged;
10368 }
10369 else
10370 fNeedsWrite = true;
10371 }
10372
10373 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10374
10375 if (fNeedsWrite)
10376 {
10377 // now spit it all out!
10378 pNewConfig->write(mData->m_strConfigFileFull, pCryptoIf, pszPassword);
10379 if (aFlags & SaveS_RemoveBackup)
10380 RTFileDelete((mData->m_strConfigFileFull + "-prev").c_str());
10381 }
10382
10383 mData->pMachineConfigFile = pNewConfig;
10384 delete pOldConfig;
10385 i_commit();
10386
10387 // after saving settings, we are no longer different from the XML on disk
10388 mData->flModifications = 0;
10389 }
10390 catch (HRESULT err)
10391 {
10392 // we assume that error info is set by the thrower
10393 rc = err;
10394
10395 // delete any newly created settings file
10396 if (fSettingsFileIsNew)
10397 RTFileDelete(mData->m_strConfigFileFull.c_str());
10398
10399 // restore old config
10400 delete pNewConfig;
10401 mData->pMachineConfigFile = pOldConfig;
10402 }
10403 catch (...)
10404 {
10405 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10406 }
10407
10408#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10409 if (pCryptoIf)
10410 {
10411 alock.release(); /** @todo Revise the locking. */
10412 mParent->i_releaseCryptoIf(pCryptoIf);
10413 alock.acquire();
10414 }
10415 if (pKey)
10416 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10417#endif
10418
10419 if (fNeedsWrite)
10420 {
10421 /* Fire the data change event, even on failure (since we've already
10422 * committed all data). This is done only for SessionMachines because
10423 * mutable Machine instances are always not registered (i.e. private
10424 * to the client process that creates them) and thus don't need to
10425 * inform callbacks. */
10426 if (i_isSessionMachine())
10427 mParent->i_onMachineDataChanged(mData->mUuid);
10428 }
10429
10430 LogFlowThisFunc(("rc=%08X\n", rc));
10431 LogFlowThisFuncLeave();
10432 return rc;
10433}
10434
10435/**
10436 * Implementation for saving the machine settings into the given
10437 * settings::MachineConfigFile instance. This copies machine extradata
10438 * from the previous machine config file in the instance data, if any.
10439 *
10440 * This gets called from two locations:
10441 *
10442 * -- Machine::i_saveSettings(), during the regular XML writing;
10443 *
10444 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10445 * exported to OVF and we write the VirtualBox proprietary XML
10446 * into a <vbox:Machine> tag.
10447 *
10448 * This routine fills all the fields in there, including snapshots, *except*
10449 * for the following:
10450 *
10451 * -- fCurrentStateModified. There is some special logic associated with that.
10452 *
10453 * The caller can then call MachineConfigFile::write() or do something else
10454 * with it.
10455 *
10456 * Caller must hold the machine lock!
10457 *
10458 * This throws XML errors and HRESULT, so the caller must have a catch block!
10459 */
10460void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10461{
10462 // deep copy extradata, being extra careful with self assignment (the STL
10463 // map assignment on Mac OS X clang based Xcode isn't checking)
10464 if (&config != mData->pMachineConfigFile)
10465 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10466
10467 config.uuid = mData->mUuid;
10468
10469 // copy name, description, OS type, teleport, UTC etc.
10470 config.machineUserData = mUserData->s;
10471
10472#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10473 config.strStateKeyId = mSSData->strStateKeyId;
10474 config.strStateKeyStore = mSSData->strStateKeyStore;
10475 config.strLogKeyId = mData->mstrLogKeyId;
10476 config.strLogKeyStore = mData->mstrLogKeyStore;
10477#endif
10478
10479 if ( mData->mMachineState == MachineState_Saved
10480 || mData->mMachineState == MachineState_AbortedSaved
10481 || mData->mMachineState == MachineState_Restoring
10482 // when doing certain snapshot operations we may or may not have
10483 // a saved state in the current state, so keep everything as is
10484 || ( ( mData->mMachineState == MachineState_Snapshotting
10485 || mData->mMachineState == MachineState_DeletingSnapshot
10486 || mData->mMachineState == MachineState_RestoringSnapshot)
10487 && (!mSSData->strStateFilePath.isEmpty())
10488 )
10489 )
10490 {
10491 Assert(!mSSData->strStateFilePath.isEmpty());
10492 /* try to make the file name relative to the settings file dir */
10493 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10494 }
10495 else
10496 {
10497 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10498 config.strStateFile.setNull();
10499 }
10500
10501 if (mData->mCurrentSnapshot)
10502 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10503 else
10504 config.uuidCurrentSnapshot.clear();
10505
10506 config.timeLastStateChange = mData->mLastStateChange;
10507 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10508 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10509
10510 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10511 if (FAILED(rc)) throw rc;
10512
10513 // save machine's media registry if this is VirtualBox 4.0 or later
10514 if (config.canHaveOwnMediaRegistry())
10515 {
10516 // determine machine folder
10517 Utf8Str strMachineFolder = i_getSettingsFileFull();
10518 strMachineFolder.stripFilename();
10519 mParent->i_saveMediaRegistry(config.mediaRegistry,
10520 i_getId(), // only media with registry ID == machine UUID
10521 strMachineFolder);
10522 // this throws HRESULT
10523 }
10524
10525 // save snapshots
10526 rc = i_saveAllSnapshots(config);
10527 if (FAILED(rc)) throw rc;
10528}
10529
10530/**
10531 * Saves all snapshots of the machine into the given machine config file. Called
10532 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10533 * @param config
10534 * @return
10535 */
10536HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10537{
10538 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10539
10540 HRESULT rc = S_OK;
10541
10542 try
10543 {
10544 config.llFirstSnapshot.clear();
10545
10546 if (mData->mFirstSnapshot)
10547 {
10548 // the settings use a list for "the first snapshot"
10549 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10550
10551 // get reference to the snapshot on the list and work on that
10552 // element straight in the list to avoid excessive copying later
10553 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10554 if (FAILED(rc)) throw rc;
10555 }
10556
10557// if (mType == IsSessionMachine)
10558// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10559
10560 }
10561 catch (HRESULT err)
10562 {
10563 /* we assume that error info is set by the thrower */
10564 rc = err;
10565 }
10566 catch (...)
10567 {
10568 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10569 }
10570
10571 return rc;
10572}
10573
10574/**
10575 * Saves the VM hardware configuration. It is assumed that the
10576 * given node is empty.
10577 *
10578 * @param data Reference to the settings object for the hardware config.
10579 * @param pDbg Pointer to the settings object for the debugging config
10580 * which happens to live in mHWData.
10581 * @param pAutostart Pointer to the settings object for the autostart config
10582 * which happens to live in mHWData.
10583 */
10584HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10585 settings::Autostart *pAutostart)
10586{
10587 HRESULT rc = S_OK;
10588
10589 try
10590 {
10591 /* The hardware version attribute (optional).
10592 Automatically upgrade from 1 to current default hardware version
10593 when there is no saved state. (ugly!) */
10594 if ( mHWData->mHWVersion == "1"
10595 && mSSData->strStateFilePath.isEmpty()
10596 )
10597 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10598
10599 data.strVersion = mHWData->mHWVersion;
10600 data.uuid = mHWData->mHardwareUUID;
10601
10602 // CPU
10603 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10604 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10605 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10606 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10607 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10608 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10609 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10610 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10611 data.fPAE = !!mHWData->mPAEEnabled;
10612 data.enmLongMode = mHWData->mLongMode;
10613 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10614 data.fAPIC = !!mHWData->mAPIC;
10615 data.fX2APIC = !!mHWData->mX2APIC;
10616 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10617 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10618 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10619 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10620 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10621 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10622 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10623 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10624 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10625 data.cCPUs = mHWData->mCPUCount;
10626 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10627 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10628 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10629 data.strCpuProfile = mHWData->mCpuProfile;
10630
10631 data.llCpus.clear();
10632 if (data.fCpuHotPlug)
10633 {
10634 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10635 {
10636 if (mHWData->mCPUAttached[idx])
10637 {
10638 settings::Cpu cpu;
10639 cpu.ulId = idx;
10640 data.llCpus.push_back(cpu);
10641 }
10642 }
10643 }
10644
10645 /* Standard and Extended CPUID leafs. */
10646 data.llCpuIdLeafs.clear();
10647 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10648
10649 // memory
10650 data.ulMemorySizeMB = mHWData->mMemorySize;
10651 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10652
10653 // firmware
10654 data.firmwareType = mHWData->mFirmwareType;
10655
10656 // HID
10657 data.pointingHIDType = mHWData->mPointingHIDType;
10658 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10659
10660 // chipset
10661 data.chipsetType = mHWData->mChipsetType;
10662
10663 // iommu
10664 data.iommuType = mHWData->mIommuType;
10665
10666 // paravirt
10667 data.paravirtProvider = mHWData->mParavirtProvider;
10668 data.strParavirtDebug = mHWData->mParavirtDebug;
10669
10670 // emulated USB card reader
10671 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10672
10673 // HPET
10674 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10675
10676 // boot order
10677 data.mapBootOrder.clear();
10678 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10679 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10680
10681 /* VRDEServer settings (optional) */
10682 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10683 if (FAILED(rc)) throw rc;
10684
10685 /* BIOS settings (required) */
10686 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10687 if (FAILED(rc)) throw rc;
10688
10689 /* Trusted Platform Module settings (required) */
10690 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10691 if (FAILED(rc)) throw rc;
10692
10693 /* NVRAM settings (required) */
10694 rc = mNvramStore->i_saveSettings(data.nvramSettings);
10695 if (FAILED(rc)) throw rc;
10696
10697 /* Recording settings (required) */
10698 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10699 if (FAILED(rc)) throw rc;
10700
10701 /* GraphicsAdapter settings (required) */
10702 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10703 if (FAILED(rc)) throw rc;
10704
10705 /* USB Controller (required) */
10706 data.usbSettings.llUSBControllers.clear();
10707 for (USBControllerList::const_iterator
10708 it = mUSBControllers->begin();
10709 it != mUSBControllers->end();
10710 ++it)
10711 {
10712 ComObjPtr<USBController> ctrl = *it;
10713 settings::USBController settingsCtrl;
10714
10715 settingsCtrl.strName = ctrl->i_getName();
10716 settingsCtrl.enmType = ctrl->i_getControllerType();
10717
10718 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10719 }
10720
10721 /* USB device filters (required) */
10722 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10723 if (FAILED(rc)) throw rc;
10724
10725 /* Network adapters (required) */
10726 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10727 data.llNetworkAdapters.clear();
10728 /* Write out only the nominal number of network adapters for this
10729 * chipset type. Since Machine::commit() hasn't been called there
10730 * may be extra NIC settings in the vector. */
10731 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10732 {
10733 settings::NetworkAdapter nic;
10734 nic.ulSlot = (uint32_t)slot;
10735 /* paranoia check... must not be NULL, but must not crash either. */
10736 if (mNetworkAdapters[slot])
10737 {
10738 if (mNetworkAdapters[slot]->i_hasDefaults())
10739 continue;
10740
10741 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10742 if (FAILED(rc)) throw rc;
10743
10744 data.llNetworkAdapters.push_back(nic);
10745 }
10746 }
10747
10748 /* Serial ports */
10749 data.llSerialPorts.clear();
10750 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10751 {
10752 if (mSerialPorts[slot]->i_hasDefaults())
10753 continue;
10754
10755 settings::SerialPort s;
10756 s.ulSlot = slot;
10757 rc = mSerialPorts[slot]->i_saveSettings(s);
10758 if (FAILED(rc)) return rc;
10759
10760 data.llSerialPorts.push_back(s);
10761 }
10762
10763 /* Parallel ports */
10764 data.llParallelPorts.clear();
10765 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10766 {
10767 if (mParallelPorts[slot]->i_hasDefaults())
10768 continue;
10769
10770 settings::ParallelPort p;
10771 p.ulSlot = slot;
10772 rc = mParallelPorts[slot]->i_saveSettings(p);
10773 if (FAILED(rc)) return rc;
10774
10775 data.llParallelPorts.push_back(p);
10776 }
10777
10778 /* Audio adapter */
10779 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10780 if (FAILED(rc)) return rc;
10781
10782 rc = i_saveStorageControllers(data.storage);
10783 if (FAILED(rc)) return rc;
10784
10785 /* Shared folders */
10786 data.llSharedFolders.clear();
10787 for (HWData::SharedFolderList::const_iterator
10788 it = mHWData->mSharedFolders.begin();
10789 it != mHWData->mSharedFolders.end();
10790 ++it)
10791 {
10792 SharedFolder *pSF = *it;
10793 AutoCaller sfCaller(pSF);
10794 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10795 settings::SharedFolder sf;
10796 sf.strName = pSF->i_getName();
10797 sf.strHostPath = pSF->i_getHostPath();
10798 sf.fWritable = !!pSF->i_isWritable();
10799 sf.fAutoMount = !!pSF->i_isAutoMounted();
10800 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10801
10802 data.llSharedFolders.push_back(sf);
10803 }
10804
10805 // clipboard
10806 data.clipboardMode = mHWData->mClipboardMode;
10807 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10808
10809 // drag'n'drop
10810 data.dndMode = mHWData->mDnDMode;
10811
10812 /* Guest */
10813 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10814
10815 // IO settings
10816 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10817 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10818
10819 /* BandwidthControl (required) */
10820 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10821 if (FAILED(rc)) throw rc;
10822
10823 /* Host PCI devices */
10824 data.pciAttachments.clear();
10825 for (HWData::PCIDeviceAssignmentList::const_iterator
10826 it = mHWData->mPCIDeviceAssignments.begin();
10827 it != mHWData->mPCIDeviceAssignments.end();
10828 ++it)
10829 {
10830 ComObjPtr<PCIDeviceAttachment> pda = *it;
10831 settings::HostPCIDeviceAttachment hpda;
10832
10833 rc = pda->i_saveSettings(hpda);
10834 if (FAILED(rc)) throw rc;
10835
10836 data.pciAttachments.push_back(hpda);
10837 }
10838
10839 // guest properties
10840 data.llGuestProperties.clear();
10841#ifdef VBOX_WITH_GUEST_PROPS
10842 for (HWData::GuestPropertyMap::const_iterator
10843 it = mHWData->mGuestProperties.begin();
10844 it != mHWData->mGuestProperties.end();
10845 ++it)
10846 {
10847 HWData::GuestProperty property = it->second;
10848
10849 /* Remove transient guest properties at shutdown unless we
10850 * are saving state. Note that restoring snapshot intentionally
10851 * keeps them, they will be removed if appropriate once the final
10852 * machine state is set (as crashes etc. need to work). */
10853 if ( ( mData->mMachineState == MachineState_PoweredOff
10854 || mData->mMachineState == MachineState_Aborted
10855 || mData->mMachineState == MachineState_Teleported)
10856 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10857 continue;
10858 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10859 prop.strName = it->first;
10860 prop.strValue = property.strValue;
10861 prop.timestamp = (uint64_t)property.mTimestamp;
10862 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10863 GuestPropWriteFlags(property.mFlags, szFlags);
10864 prop.strFlags = szFlags;
10865
10866 data.llGuestProperties.push_back(prop);
10867 }
10868
10869 /* I presume this doesn't require a backup(). */
10870 mData->mGuestPropertiesModified = FALSE;
10871#endif /* VBOX_WITH_GUEST_PROPS defined */
10872
10873 *pDbg = mHWData->mDebugging;
10874 *pAutostart = mHWData->mAutostart;
10875
10876 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10877 }
10878 catch (std::bad_alloc &)
10879 {
10880 return E_OUTOFMEMORY;
10881 }
10882
10883 AssertComRC(rc);
10884 return rc;
10885}
10886
10887/**
10888 * Saves the storage controller configuration.
10889 *
10890 * @param data storage settings.
10891 */
10892HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10893{
10894 data.llStorageControllers.clear();
10895
10896 for (StorageControllerList::const_iterator
10897 it = mStorageControllers->begin();
10898 it != mStorageControllers->end();
10899 ++it)
10900 {
10901 HRESULT rc;
10902 ComObjPtr<StorageController> pCtl = *it;
10903
10904 settings::StorageController ctl;
10905 ctl.strName = pCtl->i_getName();
10906 ctl.controllerType = pCtl->i_getControllerType();
10907 ctl.storageBus = pCtl->i_getStorageBus();
10908 ctl.ulInstance = pCtl->i_getInstance();
10909 ctl.fBootable = pCtl->i_getBootable();
10910
10911 /* Save the port count. */
10912 ULONG portCount;
10913 rc = pCtl->COMGETTER(PortCount)(&portCount);
10914 ComAssertComRCRet(rc, rc);
10915 ctl.ulPortCount = portCount;
10916
10917 /* Save fUseHostIOCache */
10918 BOOL fUseHostIOCache;
10919 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10920 ComAssertComRCRet(rc, rc);
10921 ctl.fUseHostIOCache = !!fUseHostIOCache;
10922
10923 /* save the devices now. */
10924 rc = i_saveStorageDevices(pCtl, ctl);
10925 ComAssertComRCRet(rc, rc);
10926
10927 data.llStorageControllers.push_back(ctl);
10928 }
10929
10930 return S_OK;
10931}
10932
10933/**
10934 * Saves the hard disk configuration.
10935 */
10936HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10937 settings::StorageController &data)
10938{
10939 MediumAttachmentList atts;
10940
10941 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10942 if (FAILED(rc)) return rc;
10943
10944 data.llAttachedDevices.clear();
10945 for (MediumAttachmentList::const_iterator
10946 it = atts.begin();
10947 it != atts.end();
10948 ++it)
10949 {
10950 settings::AttachedDevice dev;
10951 IMediumAttachment *iA = *it;
10952 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10953 Medium *pMedium = pAttach->i_getMedium();
10954
10955 dev.deviceType = pAttach->i_getType();
10956 dev.lPort = pAttach->i_getPort();
10957 dev.lDevice = pAttach->i_getDevice();
10958 dev.fPassThrough = pAttach->i_getPassthrough();
10959 dev.fHotPluggable = pAttach->i_getHotPluggable();
10960 if (pMedium)
10961 {
10962 if (pMedium->i_isHostDrive())
10963 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10964 else
10965 dev.uuid = pMedium->i_getId();
10966 dev.fTempEject = pAttach->i_getTempEject();
10967 dev.fNonRotational = pAttach->i_getNonRotational();
10968 dev.fDiscard = pAttach->i_getDiscard();
10969 }
10970
10971 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10972
10973 data.llAttachedDevices.push_back(dev);
10974 }
10975
10976 return S_OK;
10977}
10978
10979/**
10980 * Saves machine state settings as defined by aFlags
10981 * (SaveSTS_* values).
10982 *
10983 * @param aFlags Combination of SaveSTS_* flags.
10984 *
10985 * @note Locks objects for writing.
10986 */
10987HRESULT Machine::i_saveStateSettings(int aFlags)
10988{
10989 if (aFlags == 0)
10990 return S_OK;
10991
10992 AutoCaller autoCaller(this);
10993 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10994
10995 /* This object's write lock is also necessary to serialize file access
10996 * (prevent concurrent reads and writes) */
10997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10998
10999 HRESULT rc = S_OK;
11000
11001 Assert(mData->pMachineConfigFile);
11002
11003 try
11004 {
11005 if (aFlags & SaveSTS_CurStateModified)
11006 mData->pMachineConfigFile->fCurrentStateModified = true;
11007
11008 if (aFlags & SaveSTS_StateFilePath)
11009 {
11010 if (!mSSData->strStateFilePath.isEmpty())
11011 /* try to make the file name relative to the settings file dir */
11012 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
11013 else
11014 mData->pMachineConfigFile->strStateFile.setNull();
11015 }
11016
11017 if (aFlags & SaveSTS_StateTimeStamp)
11018 {
11019 Assert( mData->mMachineState != MachineState_Aborted
11020 || mSSData->strStateFilePath.isEmpty());
11021
11022 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
11023
11024 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
11025 || mData->mMachineState == MachineState_AbortedSaved);
11026/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
11027 }
11028
11029 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
11030 }
11031 catch (...)
11032 {
11033 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
11034 }
11035
11036 return rc;
11037}
11038
11039/**
11040 * Ensures that the given medium is added to a media registry. If this machine
11041 * was created with 4.0 or later, then the machine registry is used. Otherwise
11042 * the global VirtualBox media registry is used.
11043 *
11044 * Caller must NOT hold machine lock, media tree or any medium locks!
11045 *
11046 * @param pMedium
11047 */
11048void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
11049{
11050 /* Paranoia checks: do not hold machine or media tree locks. */
11051 AssertReturnVoid(!isWriteLockOnCurrentThread());
11052 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
11053
11054 ComObjPtr<Medium> pBase;
11055 {
11056 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11057 pBase = pMedium->i_getBase();
11058 }
11059
11060 /* Paranoia checks: do not hold medium locks. */
11061 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
11062 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
11063
11064 // decide which medium registry to use now that the medium is attached:
11065 Guid uuid;
11066 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
11067 if (fCanHaveOwnMediaRegistry)
11068 // machine XML is VirtualBox 4.0 or higher:
11069 uuid = i_getId(); // machine UUID
11070 else
11071 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
11072
11073 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
11074 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11075 if (pMedium->i_addRegistry(uuid))
11076 mParent->i_markRegistryModified(uuid);
11077
11078 /* For more complex hard disk structures it can happen that the base
11079 * medium isn't yet associated with any medium registry. Do that now. */
11080 if (pMedium != pBase)
11081 {
11082 /* Tree lock needed by Medium::addRegistryAll. */
11083 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11084 if (fCanHaveOwnMediaRegistry && pBase->i_removeRegistryAll(mParent->i_getGlobalRegistryId()))
11085 {
11086 treeLock.release();
11087 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11088 treeLock.acquire();
11089 }
11090 if (pBase->i_addRegistryAll(uuid))
11091 {
11092 treeLock.release();
11093 mParent->i_markRegistryModified(uuid);
11094 }
11095 }
11096}
11097
11098/**
11099 * Creates differencing hard disks for all normal hard disks attached to this
11100 * machine and a new set of attachments to refer to created disks.
11101 *
11102 * Used when taking a snapshot or when deleting the current state. Gets called
11103 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
11104 *
11105 * This method assumes that mMediumAttachments contains the original hard disk
11106 * attachments it needs to create diffs for. On success, these attachments will
11107 * be replaced with the created diffs.
11108 *
11109 * Attachments with non-normal hard disks are left as is.
11110 *
11111 * If @a aOnline is @c false then the original hard disks that require implicit
11112 * diffs will be locked for reading. Otherwise it is assumed that they are
11113 * already locked for writing (when the VM was started). Note that in the latter
11114 * case it is responsibility of the caller to lock the newly created diffs for
11115 * writing if this method succeeds.
11116 *
11117 * @param aProgress Progress object to run (must contain at least as
11118 * many operations left as the number of hard disks
11119 * attached).
11120 * @param aWeight Weight of this operation.
11121 * @param aOnline Whether the VM was online prior to this operation.
11122 *
11123 * @note The progress object is not marked as completed, neither on success nor
11124 * on failure. This is a responsibility of the caller.
11125 *
11126 * @note Locks this object and the media tree for writing.
11127 */
11128HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
11129 ULONG aWeight,
11130 bool aOnline)
11131{
11132 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11133
11134 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
11135 AssertReturn(!!pProgressControl, E_INVALIDARG);
11136
11137 AutoCaller autoCaller(this);
11138 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11139
11140 AutoMultiWriteLock2 alock(this->lockHandle(),
11141 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11142
11143 /* must be in a protective state because we release the lock below */
11144 AssertReturn( mData->mMachineState == MachineState_Snapshotting
11145 || mData->mMachineState == MachineState_OnlineSnapshotting
11146 || mData->mMachineState == MachineState_LiveSnapshotting
11147 || mData->mMachineState == MachineState_RestoringSnapshot
11148 || mData->mMachineState == MachineState_DeletingSnapshot
11149 , E_FAIL);
11150
11151 HRESULT rc = S_OK;
11152
11153 // use appropriate locked media map (online or offline)
11154 MediumLockListMap lockedMediaOffline;
11155 MediumLockListMap *lockedMediaMap;
11156 if (aOnline)
11157 lockedMediaMap = &mData->mSession.mLockedMedia;
11158 else
11159 lockedMediaMap = &lockedMediaOffline;
11160
11161 try
11162 {
11163 if (!aOnline)
11164 {
11165 /* lock all attached hard disks early to detect "in use"
11166 * situations before creating actual diffs */
11167 for (MediumAttachmentList::const_iterator
11168 it = mMediumAttachments->begin();
11169 it != mMediumAttachments->end();
11170 ++it)
11171 {
11172 MediumAttachment *pAtt = *it;
11173 if (pAtt->i_getType() == DeviceType_HardDisk)
11174 {
11175 Medium *pMedium = pAtt->i_getMedium();
11176 Assert(pMedium);
11177
11178 MediumLockList *pMediumLockList(new MediumLockList());
11179 alock.release();
11180 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11181 NULL /* pToLockWrite */,
11182 false /* fMediumLockWriteAll */,
11183 NULL,
11184 *pMediumLockList);
11185 alock.acquire();
11186 if (FAILED(rc))
11187 {
11188 delete pMediumLockList;
11189 throw rc;
11190 }
11191 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11192 if (FAILED(rc))
11193 {
11194 throw setError(rc,
11195 tr("Collecting locking information for all attached media failed"));
11196 }
11197 }
11198 }
11199
11200 /* Now lock all media. If this fails, nothing is locked. */
11201 alock.release();
11202 rc = lockedMediaMap->Lock();
11203 alock.acquire();
11204 if (FAILED(rc))
11205 {
11206 throw setError(rc,
11207 tr("Locking of attached media failed"));
11208 }
11209 }
11210
11211 /* remember the current list (note that we don't use backup() since
11212 * mMediumAttachments may be already backed up) */
11213 MediumAttachmentList atts = *mMediumAttachments.data();
11214
11215 /* start from scratch */
11216 mMediumAttachments->clear();
11217
11218 /* go through remembered attachments and create diffs for normal hard
11219 * disks and attach them */
11220 for (MediumAttachmentList::const_iterator
11221 it = atts.begin();
11222 it != atts.end();
11223 ++it)
11224 {
11225 MediumAttachment *pAtt = *it;
11226
11227 DeviceType_T devType = pAtt->i_getType();
11228 Medium *pMedium = pAtt->i_getMedium();
11229
11230 if ( devType != DeviceType_HardDisk
11231 || pMedium == NULL
11232 || pMedium->i_getType() != MediumType_Normal)
11233 {
11234 /* copy the attachment as is */
11235
11236 /** @todo the progress object created in SessionMachine::TakeSnaphot
11237 * only expects operations for hard disks. Later other
11238 * device types need to show up in the progress as well. */
11239 if (devType == DeviceType_HardDisk)
11240 {
11241 if (pMedium == NULL)
11242 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11243 aWeight); // weight
11244 else
11245 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11246 pMedium->i_getBase()->i_getName().c_str()).raw(),
11247 aWeight); // weight
11248 }
11249
11250 mMediumAttachments->push_back(pAtt);
11251 continue;
11252 }
11253
11254 /* need a diff */
11255 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11256 pMedium->i_getBase()->i_getName().c_str()).raw(),
11257 aWeight); // weight
11258
11259 Utf8Str strFullSnapshotFolder;
11260 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11261
11262 ComObjPtr<Medium> diff;
11263 diff.createObject();
11264 // store the diff in the same registry as the parent
11265 // (this cannot fail here because we can't create implicit diffs for
11266 // unregistered images)
11267 Guid uuidRegistryParent;
11268 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11269 Assert(fInRegistry); NOREF(fInRegistry);
11270 rc = diff->init(mParent,
11271 pMedium->i_getPreferredDiffFormat(),
11272 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11273 uuidRegistryParent,
11274 DeviceType_HardDisk);
11275 if (FAILED(rc)) throw rc;
11276
11277 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11278 * the push_back? Looks like we're going to release medium with the
11279 * wrong kind of lock (general issue with if we fail anywhere at all)
11280 * and an orphaned VDI in the snapshots folder. */
11281
11282 /* update the appropriate lock list */
11283 MediumLockList *pMediumLockList;
11284 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11285 AssertComRCThrowRC(rc);
11286 if (aOnline)
11287 {
11288 alock.release();
11289 /* The currently attached medium will be read-only, change
11290 * the lock type to read. */
11291 rc = pMediumLockList->Update(pMedium, false);
11292 alock.acquire();
11293 AssertComRCThrowRC(rc);
11294 }
11295
11296 /* release the locks before the potentially lengthy operation */
11297 alock.release();
11298 rc = pMedium->i_createDiffStorage(diff,
11299 pMedium->i_getPreferredDiffVariant(),
11300 pMediumLockList,
11301 NULL /* aProgress */,
11302 true /* aWait */,
11303 false /* aNotify */);
11304 alock.acquire();
11305 if (FAILED(rc)) throw rc;
11306
11307 /* actual lock list update is done in Machine::i_commitMedia */
11308
11309 rc = diff->i_addBackReference(mData->mUuid);
11310 AssertComRCThrowRC(rc);
11311
11312 /* add a new attachment */
11313 ComObjPtr<MediumAttachment> attachment;
11314 attachment.createObject();
11315 rc = attachment->init(this,
11316 diff,
11317 pAtt->i_getControllerName(),
11318 pAtt->i_getPort(),
11319 pAtt->i_getDevice(),
11320 DeviceType_HardDisk,
11321 true /* aImplicit */,
11322 false /* aPassthrough */,
11323 false /* aTempEject */,
11324 pAtt->i_getNonRotational(),
11325 pAtt->i_getDiscard(),
11326 pAtt->i_getHotPluggable(),
11327 pAtt->i_getBandwidthGroup());
11328 if (FAILED(rc)) throw rc;
11329
11330 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11331 AssertComRCThrowRC(rc);
11332 mMediumAttachments->push_back(attachment);
11333 }
11334 }
11335 catch (HRESULT aRC) { rc = aRC; }
11336
11337 /* unlock all hard disks we locked when there is no VM */
11338 if (!aOnline)
11339 {
11340 ErrorInfoKeeper eik;
11341
11342 HRESULT rc1 = lockedMediaMap->Clear();
11343 AssertComRC(rc1);
11344 }
11345
11346 return rc;
11347}
11348
11349/**
11350 * Deletes implicit differencing hard disks created either by
11351 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11352 * mMediumAttachments.
11353 *
11354 * Note that to delete hard disks created by #attachDevice() this method is
11355 * called from #i_rollbackMedia() when the changes are rolled back.
11356 *
11357 * @note Locks this object and the media tree for writing.
11358 */
11359HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11360{
11361 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11362
11363 AutoCaller autoCaller(this);
11364 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11365
11366 AutoMultiWriteLock2 alock(this->lockHandle(),
11367 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11368
11369 /* We absolutely must have backed up state. */
11370 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11371
11372 /* Check if there are any implicitly created diff images. */
11373 bool fImplicitDiffs = false;
11374 for (MediumAttachmentList::const_iterator
11375 it = mMediumAttachments->begin();
11376 it != mMediumAttachments->end();
11377 ++it)
11378 {
11379 const ComObjPtr<MediumAttachment> &pAtt = *it;
11380 if (pAtt->i_isImplicit())
11381 {
11382 fImplicitDiffs = true;
11383 break;
11384 }
11385 }
11386 /* If there is nothing to do, leave early. This saves lots of image locking
11387 * effort. It also avoids a MachineStateChanged event without real reason.
11388 * This is important e.g. when loading a VM config, because there should be
11389 * no events. Otherwise API clients can become thoroughly confused for
11390 * inaccessible VMs (the code for loading VM configs uses this method for
11391 * cleanup if the config makes no sense), as they take such events as an
11392 * indication that the VM is alive, and they would force the VM config to
11393 * be reread, leading to an endless loop. */
11394 if (!fImplicitDiffs)
11395 return S_OK;
11396
11397 HRESULT rc = S_OK;
11398 MachineState_T oldState = mData->mMachineState;
11399
11400 /* will release the lock before the potentially lengthy operation,
11401 * so protect with the special state (unless already protected) */
11402 if ( oldState != MachineState_Snapshotting
11403 && oldState != MachineState_OnlineSnapshotting
11404 && oldState != MachineState_LiveSnapshotting
11405 && oldState != MachineState_RestoringSnapshot
11406 && oldState != MachineState_DeletingSnapshot
11407 && oldState != MachineState_DeletingSnapshotOnline
11408 && oldState != MachineState_DeletingSnapshotPaused
11409 )
11410 i_setMachineState(MachineState_SettingUp);
11411
11412 // use appropriate locked media map (online or offline)
11413 MediumLockListMap lockedMediaOffline;
11414 MediumLockListMap *lockedMediaMap;
11415 if (aOnline)
11416 lockedMediaMap = &mData->mSession.mLockedMedia;
11417 else
11418 lockedMediaMap = &lockedMediaOffline;
11419
11420 try
11421 {
11422 if (!aOnline)
11423 {
11424 /* lock all attached hard disks early to detect "in use"
11425 * situations before deleting actual diffs */
11426 for (MediumAttachmentList::const_iterator
11427 it = mMediumAttachments->begin();
11428 it != mMediumAttachments->end();
11429 ++it)
11430 {
11431 MediumAttachment *pAtt = *it;
11432 if (pAtt->i_getType() == DeviceType_HardDisk)
11433 {
11434 Medium *pMedium = pAtt->i_getMedium();
11435 Assert(pMedium);
11436
11437 MediumLockList *pMediumLockList(new MediumLockList());
11438 alock.release();
11439 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11440 NULL /* pToLockWrite */,
11441 false /* fMediumLockWriteAll */,
11442 NULL,
11443 *pMediumLockList);
11444 alock.acquire();
11445
11446 if (FAILED(rc))
11447 {
11448 delete pMediumLockList;
11449 throw rc;
11450 }
11451
11452 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11453 if (FAILED(rc))
11454 throw rc;
11455 }
11456 }
11457
11458 if (FAILED(rc))
11459 throw rc;
11460 } // end of offline
11461
11462 /* Lock lists are now up to date and include implicitly created media */
11463
11464 /* Go through remembered attachments and delete all implicitly created
11465 * diffs and fix up the attachment information */
11466 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11467 MediumAttachmentList implicitAtts;
11468 for (MediumAttachmentList::const_iterator
11469 it = mMediumAttachments->begin();
11470 it != mMediumAttachments->end();
11471 ++it)
11472 {
11473 ComObjPtr<MediumAttachment> pAtt = *it;
11474 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11475 if (pMedium.isNull())
11476 continue;
11477
11478 // Implicit attachments go on the list for deletion and back references are removed.
11479 if (pAtt->i_isImplicit())
11480 {
11481 /* Deassociate and mark for deletion */
11482 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11483 rc = pMedium->i_removeBackReference(mData->mUuid);
11484 if (FAILED(rc))
11485 throw rc;
11486 implicitAtts.push_back(pAtt);
11487 continue;
11488 }
11489
11490 /* Was this medium attached before? */
11491 if (!i_findAttachment(oldAtts, pMedium))
11492 {
11493 /* no: de-associate */
11494 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11495 rc = pMedium->i_removeBackReference(mData->mUuid);
11496 if (FAILED(rc))
11497 throw rc;
11498 continue;
11499 }
11500 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11501 }
11502
11503 /* If there are implicit attachments to delete, throw away the lock
11504 * map contents (which will unlock all media) since the medium
11505 * attachments will be rolled back. Below we need to completely
11506 * recreate the lock map anyway since it is infinitely complex to
11507 * do this incrementally (would need reconstructing each attachment
11508 * change, which would be extremely hairy). */
11509 if (implicitAtts.size() != 0)
11510 {
11511 ErrorInfoKeeper eik;
11512
11513 HRESULT rc1 = lockedMediaMap->Clear();
11514 AssertComRC(rc1);
11515 }
11516
11517 /* rollback hard disk changes */
11518 mMediumAttachments.rollback();
11519
11520 MultiResult mrc(S_OK);
11521
11522 // Delete unused implicit diffs.
11523 if (implicitAtts.size() != 0)
11524 {
11525 alock.release();
11526
11527 for (MediumAttachmentList::const_iterator
11528 it = implicitAtts.begin();
11529 it != implicitAtts.end();
11530 ++it)
11531 {
11532 // Remove medium associated with this attachment.
11533 ComObjPtr<MediumAttachment> pAtt = *it;
11534 Assert(pAtt);
11535 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11536 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11537 Assert(pMedium);
11538
11539 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11540 // continue on delete failure, just collect error messages
11541 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11542 pMedium->i_getLocationFull().c_str() ));
11543 mrc = rc;
11544 }
11545 // Clear the list of deleted implicit attachments now, while not
11546 // holding the lock, as it will ultimately trigger Medium::uninit()
11547 // calls which assume that the media tree lock isn't held.
11548 implicitAtts.clear();
11549
11550 alock.acquire();
11551
11552 /* if there is a VM recreate media lock map as mentioned above,
11553 * otherwise it is a waste of time and we leave things unlocked */
11554 if (aOnline)
11555 {
11556 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11557 /* must never be NULL, but better safe than sorry */
11558 if (!pMachine.isNull())
11559 {
11560 alock.release();
11561 rc = mData->mSession.mMachine->i_lockMedia();
11562 alock.acquire();
11563 if (FAILED(rc))
11564 throw rc;
11565 }
11566 }
11567 }
11568 }
11569 catch (HRESULT aRC) {rc = aRC;}
11570
11571 if (mData->mMachineState == MachineState_SettingUp)
11572 i_setMachineState(oldState);
11573
11574 /* unlock all hard disks we locked when there is no VM */
11575 if (!aOnline)
11576 {
11577 ErrorInfoKeeper eik;
11578
11579 HRESULT rc1 = lockedMediaMap->Clear();
11580 AssertComRC(rc1);
11581 }
11582
11583 return rc;
11584}
11585
11586
11587/**
11588 * Looks through the given list of media attachments for one with the given parameters
11589 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11590 * can be searched as well if needed.
11591 *
11592 * @param ll
11593 * @param aControllerName
11594 * @param aControllerPort
11595 * @param aDevice
11596 * @return
11597 */
11598MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11599 const Utf8Str &aControllerName,
11600 LONG aControllerPort,
11601 LONG aDevice)
11602{
11603 for (MediumAttachmentList::const_iterator
11604 it = ll.begin();
11605 it != ll.end();
11606 ++it)
11607 {
11608 MediumAttachment *pAttach = *it;
11609 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11610 return pAttach;
11611 }
11612
11613 return NULL;
11614}
11615
11616/**
11617 * Looks through the given list of media attachments for one with the given parameters
11618 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11619 * can be searched as well if needed.
11620 *
11621 * @param ll
11622 * @param pMedium
11623 * @return
11624 */
11625MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11626 ComObjPtr<Medium> pMedium)
11627{
11628 for (MediumAttachmentList::const_iterator
11629 it = ll.begin();
11630 it != ll.end();
11631 ++it)
11632 {
11633 MediumAttachment *pAttach = *it;
11634 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11635 if (pMediumThis == pMedium)
11636 return pAttach;
11637 }
11638
11639 return NULL;
11640}
11641
11642/**
11643 * Looks through the given list of media attachments for one with the given parameters
11644 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11645 * can be searched as well if needed.
11646 *
11647 * @param ll
11648 * @param id
11649 * @return
11650 */
11651MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11652 Guid &id)
11653{
11654 for (MediumAttachmentList::const_iterator
11655 it = ll.begin();
11656 it != ll.end();
11657 ++it)
11658 {
11659 MediumAttachment *pAttach = *it;
11660 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11661 if (pMediumThis->i_getId() == id)
11662 return pAttach;
11663 }
11664
11665 return NULL;
11666}
11667
11668/**
11669 * Main implementation for Machine::DetachDevice. This also gets called
11670 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11671 *
11672 * @param pAttach Medium attachment to detach.
11673 * @param writeLock Machine write lock which the caller must have locked once.
11674 * This may be released temporarily in here.
11675 * @param pSnapshot If NULL, then the detachment is for the current machine.
11676 * Otherwise this is for a SnapshotMachine, and this must be
11677 * its snapshot.
11678 * @return
11679 */
11680HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11681 AutoWriteLock &writeLock,
11682 Snapshot *pSnapshot)
11683{
11684 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11685 DeviceType_T mediumType = pAttach->i_getType();
11686
11687 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11688
11689 if (pAttach->i_isImplicit())
11690 {
11691 /* attempt to implicitly delete the implicitly created diff */
11692
11693 /// @todo move the implicit flag from MediumAttachment to Medium
11694 /// and forbid any hard disk operation when it is implicit. Or maybe
11695 /// a special media state for it to make it even more simple.
11696
11697 Assert(mMediumAttachments.isBackedUp());
11698
11699 /* will release the lock before the potentially lengthy operation, so
11700 * protect with the special state */
11701 MachineState_T oldState = mData->mMachineState;
11702 i_setMachineState(MachineState_SettingUp);
11703
11704 writeLock.release();
11705
11706 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11707 true /*aWait*/,
11708 false /*aNotify*/);
11709
11710 writeLock.acquire();
11711
11712 i_setMachineState(oldState);
11713
11714 if (FAILED(rc)) return rc;
11715 }
11716
11717 i_setModified(IsModified_Storage);
11718 mMediumAttachments.backup();
11719 mMediumAttachments->remove(pAttach);
11720
11721 if (!oldmedium.isNull())
11722 {
11723 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11724 if (pSnapshot)
11725 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11726 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11727 else if (mediumType != DeviceType_HardDisk)
11728 oldmedium->i_removeBackReference(mData->mUuid);
11729 }
11730
11731 return S_OK;
11732}
11733
11734/**
11735 * Goes thru all media of the given list and
11736 *
11737 * 1) calls i_detachDevice() on each of them for this machine and
11738 * 2) adds all Medium objects found in the process to the given list,
11739 * depending on cleanupMode.
11740 *
11741 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11742 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11743 * media to the list.
11744 *
11745 * This gets called from Machine::Unregister, both for the actual Machine and
11746 * the SnapshotMachine objects that might be found in the snapshots.
11747 *
11748 * Requires caller and locking. The machine lock must be passed in because it
11749 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11750 *
11751 * @param writeLock Machine lock from top-level caller; this gets passed to
11752 * i_detachDevice.
11753 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11754 * object if called for a SnapshotMachine.
11755 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11756 * added to llMedia; if Full, then all media get added;
11757 * otherwise no media get added.
11758 * @param llMedia Caller's list to receive Medium objects which got detached so
11759 * caller can close() them, depending on cleanupMode.
11760 * @return
11761 */
11762HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11763 Snapshot *pSnapshot,
11764 CleanupMode_T cleanupMode,
11765 MediaList &llMedia)
11766{
11767 Assert(isWriteLockOnCurrentThread());
11768
11769 HRESULT rc;
11770
11771 // make a temporary list because i_detachDevice invalidates iterators into
11772 // mMediumAttachments
11773 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11774
11775 for (MediumAttachmentList::iterator
11776 it = llAttachments2.begin();
11777 it != llAttachments2.end();
11778 ++it)
11779 {
11780 ComObjPtr<MediumAttachment> &pAttach = *it;
11781 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11782
11783 if (!pMedium.isNull())
11784 {
11785 AutoCaller mac(pMedium);
11786 if (FAILED(mac.rc())) return mac.rc();
11787 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11788 DeviceType_T devType = pMedium->i_getDeviceType();
11789 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11790 && devType == DeviceType_HardDisk)
11791 || (cleanupMode == CleanupMode_Full)
11792 )
11793 {
11794 llMedia.push_back(pMedium);
11795 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11796 /* Not allowed to keep this lock as below we need the parent
11797 * medium lock, and the lock order is parent to child. */
11798 lock.release();
11799 /*
11800 * Search for medias which are not attached to any machine, but
11801 * in the chain to an attached disk. Mediums are only consided
11802 * if they are:
11803 * - have only one child
11804 * - no references to any machines
11805 * - are of normal medium type
11806 */
11807 while (!pParent.isNull())
11808 {
11809 AutoCaller mac1(pParent);
11810 if (FAILED(mac1.rc())) return mac1.rc();
11811 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11812 if (pParent->i_getChildren().size() == 1)
11813 {
11814 if ( pParent->i_getMachineBackRefCount() == 0
11815 && pParent->i_getType() == MediumType_Normal
11816 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11817 llMedia.push_back(pParent);
11818 }
11819 else
11820 break;
11821 pParent = pParent->i_getParent();
11822 }
11823 }
11824 }
11825
11826 // real machine: then we need to use the proper method
11827 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11828
11829 if (FAILED(rc))
11830 return rc;
11831 }
11832
11833 return S_OK;
11834}
11835
11836/**
11837 * Perform deferred hard disk detachments.
11838 *
11839 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11840 * changed (not backed up).
11841 *
11842 * If @a aOnline is @c true then this method will also unlock the old hard
11843 * disks for which the new implicit diffs were created and will lock these new
11844 * diffs for writing.
11845 *
11846 * @param aOnline Whether the VM was online prior to this operation.
11847 *
11848 * @note Locks this object for writing!
11849 */
11850void Machine::i_commitMedia(bool aOnline /*= false*/)
11851{
11852 AutoCaller autoCaller(this);
11853 AssertComRCReturnVoid(autoCaller.rc());
11854
11855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11856
11857 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11858
11859 HRESULT rc = S_OK;
11860
11861 /* no attach/detach operations -- nothing to do */
11862 if (!mMediumAttachments.isBackedUp())
11863 return;
11864
11865 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11866 bool fMediaNeedsLocking = false;
11867
11868 /* enumerate new attachments */
11869 for (MediumAttachmentList::const_iterator
11870 it = mMediumAttachments->begin();
11871 it != mMediumAttachments->end();
11872 ++it)
11873 {
11874 MediumAttachment *pAttach = *it;
11875
11876 pAttach->i_commit();
11877
11878 Medium *pMedium = pAttach->i_getMedium();
11879 bool fImplicit = pAttach->i_isImplicit();
11880
11881 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11882 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11883 fImplicit));
11884
11885 /** @todo convert all this Machine-based voodoo to MediumAttachment
11886 * based commit logic. */
11887 if (fImplicit)
11888 {
11889 /* convert implicit attachment to normal */
11890 pAttach->i_setImplicit(false);
11891
11892 if ( aOnline
11893 && pMedium
11894 && pAttach->i_getType() == DeviceType_HardDisk
11895 )
11896 {
11897 /* update the appropriate lock list */
11898 MediumLockList *pMediumLockList;
11899 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11900 AssertComRC(rc);
11901 if (pMediumLockList)
11902 {
11903 /* unlock if there's a need to change the locking */
11904 if (!fMediaNeedsLocking)
11905 {
11906 rc = mData->mSession.mLockedMedia.Unlock();
11907 AssertComRC(rc);
11908 fMediaNeedsLocking = true;
11909 }
11910 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11911 AssertComRC(rc);
11912 rc = pMediumLockList->Append(pMedium, true);
11913 AssertComRC(rc);
11914 }
11915 }
11916
11917 continue;
11918 }
11919
11920 if (pMedium)
11921 {
11922 /* was this medium attached before? */
11923 for (MediumAttachmentList::iterator
11924 oldIt = oldAtts.begin();
11925 oldIt != oldAtts.end();
11926 ++oldIt)
11927 {
11928 MediumAttachment *pOldAttach = *oldIt;
11929 if (pOldAttach->i_getMedium() == pMedium)
11930 {
11931 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11932
11933 /* yes: remove from old to avoid de-association */
11934 oldAtts.erase(oldIt);
11935 break;
11936 }
11937 }
11938 }
11939 }
11940
11941 /* enumerate remaining old attachments and de-associate from the
11942 * current machine state */
11943 for (MediumAttachmentList::const_iterator
11944 it = oldAtts.begin();
11945 it != oldAtts.end();
11946 ++it)
11947 {
11948 MediumAttachment *pAttach = *it;
11949 Medium *pMedium = pAttach->i_getMedium();
11950
11951 /* Detach only hard disks, since DVD/floppy media is detached
11952 * instantly in MountMedium. */
11953 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11954 {
11955 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11956
11957 /* now de-associate from the current machine state */
11958 rc = pMedium->i_removeBackReference(mData->mUuid);
11959 AssertComRC(rc);
11960
11961 if (aOnline)
11962 {
11963 /* unlock since medium is not used anymore */
11964 MediumLockList *pMediumLockList;
11965 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11966 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11967 {
11968 /* this happens for online snapshots, there the attachment
11969 * is changing, but only to a diff image created under
11970 * the old one, so there is no separate lock list */
11971 Assert(!pMediumLockList);
11972 }
11973 else
11974 {
11975 AssertComRC(rc);
11976 if (pMediumLockList)
11977 {
11978 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11979 AssertComRC(rc);
11980 }
11981 }
11982 }
11983 }
11984 }
11985
11986 /* take media locks again so that the locking state is consistent */
11987 if (fMediaNeedsLocking)
11988 {
11989 Assert(aOnline);
11990 rc = mData->mSession.mLockedMedia.Lock();
11991 AssertComRC(rc);
11992 }
11993
11994 /* commit the hard disk changes */
11995 mMediumAttachments.commit();
11996
11997 if (i_isSessionMachine())
11998 {
11999 /*
12000 * Update the parent machine to point to the new owner.
12001 * This is necessary because the stored parent will point to the
12002 * session machine otherwise and cause crashes or errors later
12003 * when the session machine gets invalid.
12004 */
12005 /** @todo Change the MediumAttachment class to behave like any other
12006 * class in this regard by creating peer MediumAttachment
12007 * objects for session machines and share the data with the peer
12008 * machine.
12009 */
12010 for (MediumAttachmentList::const_iterator
12011 it = mMediumAttachments->begin();
12012 it != mMediumAttachments->end();
12013 ++it)
12014 (*it)->i_updateParentMachine(mPeer);
12015
12016 /* attach new data to the primary machine and reshare it */
12017 mPeer->mMediumAttachments.attach(mMediumAttachments);
12018 }
12019
12020 return;
12021}
12022
12023/**
12024 * Perform deferred deletion of implicitly created diffs.
12025 *
12026 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
12027 * changed (not backed up).
12028 *
12029 * @note Locks this object for writing!
12030 */
12031void Machine::i_rollbackMedia()
12032{
12033 AutoCaller autoCaller(this);
12034 AssertComRCReturnVoid(autoCaller.rc());
12035
12036 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12037 LogFlowThisFunc(("Entering rollbackMedia\n"));
12038
12039 HRESULT rc = S_OK;
12040
12041 /* no attach/detach operations -- nothing to do */
12042 if (!mMediumAttachments.isBackedUp())
12043 return;
12044
12045 /* enumerate new attachments */
12046 for (MediumAttachmentList::const_iterator
12047 it = mMediumAttachments->begin();
12048 it != mMediumAttachments->end();
12049 ++it)
12050 {
12051 MediumAttachment *pAttach = *it;
12052 /* Fix up the backrefs for DVD/floppy media. */
12053 if (pAttach->i_getType() != DeviceType_HardDisk)
12054 {
12055 Medium *pMedium = pAttach->i_getMedium();
12056 if (pMedium)
12057 {
12058 rc = pMedium->i_removeBackReference(mData->mUuid);
12059 AssertComRC(rc);
12060 }
12061 }
12062
12063 (*it)->i_rollback();
12064
12065 pAttach = *it;
12066 /* Fix up the backrefs for DVD/floppy media. */
12067 if (pAttach->i_getType() != DeviceType_HardDisk)
12068 {
12069 Medium *pMedium = pAttach->i_getMedium();
12070 if (pMedium)
12071 {
12072 rc = pMedium->i_addBackReference(mData->mUuid);
12073 AssertComRC(rc);
12074 }
12075 }
12076 }
12077
12078 /** @todo convert all this Machine-based voodoo to MediumAttachment
12079 * based rollback logic. */
12080 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
12081
12082 return;
12083}
12084
12085/**
12086 * Returns true if the settings file is located in the directory named exactly
12087 * as the machine; this means, among other things, that the machine directory
12088 * should be auto-renamed.
12089 *
12090 * @param aSettingsDir if not NULL, the full machine settings file directory
12091 * name will be assigned there.
12092 *
12093 * @note Doesn't lock anything.
12094 * @note Not thread safe (must be called from this object's lock).
12095 */
12096bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
12097{
12098 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12099 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
12100 if (aSettingsDir)
12101 *aSettingsDir = strMachineDirName;
12102 strMachineDirName.stripPath(); // vmname
12103 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12104 strConfigFileOnly.stripPath() // vmname.vbox
12105 .stripSuffix(); // vmname
12106 /** @todo hack, make somehow use of ComposeMachineFilename */
12107 if (mUserData->s.fDirectoryIncludesUUID)
12108 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
12109
12110 AssertReturn(!strMachineDirName.isEmpty(), false);
12111 AssertReturn(!strConfigFileOnly.isEmpty(), false);
12112
12113 return strMachineDirName == strConfigFileOnly;
12114}
12115
12116/**
12117 * Discards all changes to machine settings.
12118 *
12119 * @param aNotify Whether to notify the direct session about changes or not.
12120 *
12121 * @note Locks objects for writing!
12122 */
12123void Machine::i_rollback(bool aNotify)
12124{
12125 AutoCaller autoCaller(this);
12126 AssertComRCReturn(autoCaller.rc(), (void)0);
12127
12128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12129
12130 if (!mStorageControllers.isNull())
12131 {
12132 if (mStorageControllers.isBackedUp())
12133 {
12134 /* unitialize all new devices (absent in the backed up list). */
12135 StorageControllerList *backedList = mStorageControllers.backedUpData();
12136 for (StorageControllerList::const_iterator
12137 it = mStorageControllers->begin();
12138 it != mStorageControllers->end();
12139 ++it)
12140 {
12141 if ( std::find(backedList->begin(), backedList->end(), *it)
12142 == backedList->end()
12143 )
12144 {
12145 (*it)->uninit();
12146 }
12147 }
12148
12149 /* restore the list */
12150 mStorageControllers.rollback();
12151 }
12152
12153 /* rollback any changes to devices after restoring the list */
12154 if (mData->flModifications & IsModified_Storage)
12155 {
12156 for (StorageControllerList::const_iterator
12157 it = mStorageControllers->begin();
12158 it != mStorageControllers->end();
12159 ++it)
12160 {
12161 (*it)->i_rollback();
12162 }
12163 }
12164 }
12165
12166 if (!mUSBControllers.isNull())
12167 {
12168 if (mUSBControllers.isBackedUp())
12169 {
12170 /* unitialize all new devices (absent in the backed up list). */
12171 USBControllerList *backedList = mUSBControllers.backedUpData();
12172 for (USBControllerList::const_iterator
12173 it = mUSBControllers->begin();
12174 it != mUSBControllers->end();
12175 ++it)
12176 {
12177 if ( std::find(backedList->begin(), backedList->end(), *it)
12178 == backedList->end()
12179 )
12180 {
12181 (*it)->uninit();
12182 }
12183 }
12184
12185 /* restore the list */
12186 mUSBControllers.rollback();
12187 }
12188
12189 /* rollback any changes to devices after restoring the list */
12190 if (mData->flModifications & IsModified_USB)
12191 {
12192 for (USBControllerList::const_iterator
12193 it = mUSBControllers->begin();
12194 it != mUSBControllers->end();
12195 ++it)
12196 {
12197 (*it)->i_rollback();
12198 }
12199 }
12200 }
12201
12202 mUserData.rollback();
12203
12204 mHWData.rollback();
12205
12206 if (mData->flModifications & IsModified_Storage)
12207 i_rollbackMedia();
12208
12209 if (mBIOSSettings)
12210 mBIOSSettings->i_rollback();
12211
12212 if (mTrustedPlatformModule)
12213 mTrustedPlatformModule->i_rollback();
12214
12215 if (mNvramStore)
12216 mNvramStore->i_rollback();
12217
12218 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
12219 mRecordingSettings->i_rollback();
12220
12221 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
12222 mGraphicsAdapter->i_rollback();
12223
12224 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12225 mVRDEServer->i_rollback();
12226
12227 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
12228 mAudioAdapter->i_rollback();
12229
12230 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12231 mUSBDeviceFilters->i_rollback();
12232
12233 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12234 mBandwidthControl->i_rollback();
12235
12236 if (!mHWData.isNull())
12237 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12238 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12239 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12240 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12241
12242 if (mData->flModifications & IsModified_NetworkAdapters)
12243 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12244 if ( mNetworkAdapters[slot]
12245 && mNetworkAdapters[slot]->i_isModified())
12246 {
12247 mNetworkAdapters[slot]->i_rollback();
12248 networkAdapters[slot] = mNetworkAdapters[slot];
12249 }
12250
12251 if (mData->flModifications & IsModified_SerialPorts)
12252 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12253 if ( mSerialPorts[slot]
12254 && mSerialPorts[slot]->i_isModified())
12255 {
12256 mSerialPorts[slot]->i_rollback();
12257 serialPorts[slot] = mSerialPorts[slot];
12258 }
12259
12260 if (mData->flModifications & IsModified_ParallelPorts)
12261 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12262 if ( mParallelPorts[slot]
12263 && mParallelPorts[slot]->i_isModified())
12264 {
12265 mParallelPorts[slot]->i_rollback();
12266 parallelPorts[slot] = mParallelPorts[slot];
12267 }
12268
12269 if (aNotify)
12270 {
12271 /* inform the direct session about changes */
12272
12273 ComObjPtr<Machine> that = this;
12274 uint32_t flModifications = mData->flModifications;
12275 alock.release();
12276
12277 if (flModifications & IsModified_SharedFolders)
12278 that->i_onSharedFolderChange();
12279
12280 if (flModifications & IsModified_VRDEServer)
12281 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12282 if (flModifications & IsModified_USB)
12283 that->i_onUSBControllerChange();
12284
12285 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12286 if (networkAdapters[slot])
12287 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12288 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12289 if (serialPorts[slot])
12290 that->i_onSerialPortChange(serialPorts[slot]);
12291 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12292 if (parallelPorts[slot])
12293 that->i_onParallelPortChange(parallelPorts[slot]);
12294
12295 if (flModifications & IsModified_Storage)
12296 {
12297 for (StorageControllerList::const_iterator
12298 it = mStorageControllers->begin();
12299 it != mStorageControllers->end();
12300 ++it)
12301 {
12302 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
12303 }
12304 }
12305
12306
12307#if 0
12308 if (flModifications & IsModified_BandwidthControl)
12309 that->onBandwidthControlChange();
12310#endif
12311 }
12312}
12313
12314/**
12315 * Commits all the changes to machine settings.
12316 *
12317 * Note that this operation is supposed to never fail.
12318 *
12319 * @note Locks this object and children for writing.
12320 */
12321void Machine::i_commit()
12322{
12323 AutoCaller autoCaller(this);
12324 AssertComRCReturnVoid(autoCaller.rc());
12325
12326 AutoCaller peerCaller(mPeer);
12327 AssertComRCReturnVoid(peerCaller.rc());
12328
12329 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12330
12331 /*
12332 * use safe commit to ensure Snapshot machines (that share mUserData)
12333 * will still refer to a valid memory location
12334 */
12335 mUserData.commitCopy();
12336
12337 mHWData.commit();
12338
12339 if (mMediumAttachments.isBackedUp())
12340 i_commitMedia(Global::IsOnline(mData->mMachineState));
12341
12342 mBIOSSettings->i_commit();
12343 mTrustedPlatformModule->i_commit();
12344 mNvramStore->i_commit();
12345 mRecordingSettings->i_commit();
12346 mGraphicsAdapter->i_commit();
12347 mVRDEServer->i_commit();
12348 mAudioAdapter->i_commit();
12349 mUSBDeviceFilters->i_commit();
12350 mBandwidthControl->i_commit();
12351
12352 /* Since mNetworkAdapters is a list which might have been changed (resized)
12353 * without using the Backupable<> template we need to handle the copying
12354 * of the list entries manually, including the creation of peers for the
12355 * new objects. */
12356 bool commitNetworkAdapters = false;
12357 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12358 if (mPeer)
12359 {
12360 /* commit everything, even the ones which will go away */
12361 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12362 mNetworkAdapters[slot]->i_commit();
12363 /* copy over the new entries, creating a peer and uninit the original */
12364 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12365 for (size_t slot = 0; slot < newSize; slot++)
12366 {
12367 /* look if this adapter has a peer device */
12368 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12369 if (!peer)
12370 {
12371 /* no peer means the adapter is a newly created one;
12372 * create a peer owning data this data share it with */
12373 peer.createObject();
12374 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12375 }
12376 mPeer->mNetworkAdapters[slot] = peer;
12377 }
12378 /* uninit any no longer needed network adapters */
12379 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12380 mNetworkAdapters[slot]->uninit();
12381 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12382 {
12383 if (mPeer->mNetworkAdapters[slot])
12384 mPeer->mNetworkAdapters[slot]->uninit();
12385 }
12386 /* Keep the original network adapter count until this point, so that
12387 * discarding a chipset type change will not lose settings. */
12388 mNetworkAdapters.resize(newSize);
12389 mPeer->mNetworkAdapters.resize(newSize);
12390 }
12391 else
12392 {
12393 /* we have no peer (our parent is the newly created machine);
12394 * just commit changes to the network adapters */
12395 commitNetworkAdapters = true;
12396 }
12397 if (commitNetworkAdapters)
12398 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12399 mNetworkAdapters[slot]->i_commit();
12400
12401 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12402 mSerialPorts[slot]->i_commit();
12403 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12404 mParallelPorts[slot]->i_commit();
12405
12406 bool commitStorageControllers = false;
12407
12408 if (mStorageControllers.isBackedUp())
12409 {
12410 mStorageControllers.commit();
12411
12412 if (mPeer)
12413 {
12414 /* Commit all changes to new controllers (this will reshare data with
12415 * peers for those who have peers) */
12416 StorageControllerList *newList = new StorageControllerList();
12417 for (StorageControllerList::const_iterator
12418 it = mStorageControllers->begin();
12419 it != mStorageControllers->end();
12420 ++it)
12421 {
12422 (*it)->i_commit();
12423
12424 /* look if this controller has a peer device */
12425 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12426 if (!peer)
12427 {
12428 /* no peer means the device is a newly created one;
12429 * create a peer owning data this device share it with */
12430 peer.createObject();
12431 peer->init(mPeer, *it, true /* aReshare */);
12432 }
12433 else
12434 {
12435 /* remove peer from the old list */
12436 mPeer->mStorageControllers->remove(peer);
12437 }
12438 /* and add it to the new list */
12439 newList->push_back(peer);
12440 }
12441
12442 /* uninit old peer's controllers that are left */
12443 for (StorageControllerList::const_iterator
12444 it = mPeer->mStorageControllers->begin();
12445 it != mPeer->mStorageControllers->end();
12446 ++it)
12447 {
12448 (*it)->uninit();
12449 }
12450
12451 /* attach new list of controllers to our peer */
12452 mPeer->mStorageControllers.attach(newList);
12453 }
12454 else
12455 {
12456 /* we have no peer (our parent is the newly created machine);
12457 * just commit changes to devices */
12458 commitStorageControllers = true;
12459 }
12460 }
12461 else
12462 {
12463 /* the list of controllers itself is not changed,
12464 * just commit changes to controllers themselves */
12465 commitStorageControllers = true;
12466 }
12467
12468 if (commitStorageControllers)
12469 {
12470 for (StorageControllerList::const_iterator
12471 it = mStorageControllers->begin();
12472 it != mStorageControllers->end();
12473 ++it)
12474 {
12475 (*it)->i_commit();
12476 }
12477 }
12478
12479 bool commitUSBControllers = false;
12480
12481 if (mUSBControllers.isBackedUp())
12482 {
12483 mUSBControllers.commit();
12484
12485 if (mPeer)
12486 {
12487 /* Commit all changes to new controllers (this will reshare data with
12488 * peers for those who have peers) */
12489 USBControllerList *newList = new USBControllerList();
12490 for (USBControllerList::const_iterator
12491 it = mUSBControllers->begin();
12492 it != mUSBControllers->end();
12493 ++it)
12494 {
12495 (*it)->i_commit();
12496
12497 /* look if this controller has a peer device */
12498 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12499 if (!peer)
12500 {
12501 /* no peer means the device is a newly created one;
12502 * create a peer owning data this device share it with */
12503 peer.createObject();
12504 peer->init(mPeer, *it, true /* aReshare */);
12505 }
12506 else
12507 {
12508 /* remove peer from the old list */
12509 mPeer->mUSBControllers->remove(peer);
12510 }
12511 /* and add it to the new list */
12512 newList->push_back(peer);
12513 }
12514
12515 /* uninit old peer's controllers that are left */
12516 for (USBControllerList::const_iterator
12517 it = mPeer->mUSBControllers->begin();
12518 it != mPeer->mUSBControllers->end();
12519 ++it)
12520 {
12521 (*it)->uninit();
12522 }
12523
12524 /* attach new list of controllers to our peer */
12525 mPeer->mUSBControllers.attach(newList);
12526 }
12527 else
12528 {
12529 /* we have no peer (our parent is the newly created machine);
12530 * just commit changes to devices */
12531 commitUSBControllers = true;
12532 }
12533 }
12534 else
12535 {
12536 /* the list of controllers itself is not changed,
12537 * just commit changes to controllers themselves */
12538 commitUSBControllers = true;
12539 }
12540
12541 if (commitUSBControllers)
12542 {
12543 for (USBControllerList::const_iterator
12544 it = mUSBControllers->begin();
12545 it != mUSBControllers->end();
12546 ++it)
12547 {
12548 (*it)->i_commit();
12549 }
12550 }
12551
12552 if (i_isSessionMachine())
12553 {
12554 /* attach new data to the primary machine and reshare it */
12555 mPeer->mUserData.attach(mUserData);
12556 mPeer->mHWData.attach(mHWData);
12557 /* mmMediumAttachments is reshared by fixupMedia */
12558 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12559 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12560 }
12561}
12562
12563/**
12564 * Copies all the hardware data from the given machine.
12565 *
12566 * Currently, only called when the VM is being restored from a snapshot. In
12567 * particular, this implies that the VM is not running during this method's
12568 * call.
12569 *
12570 * @note This method must be called from under this object's lock.
12571 *
12572 * @note This method doesn't call #i_commit(), so all data remains backed up and
12573 * unsaved.
12574 */
12575void Machine::i_copyFrom(Machine *aThat)
12576{
12577 AssertReturnVoid(!i_isSnapshotMachine());
12578 AssertReturnVoid(aThat->i_isSnapshotMachine());
12579
12580 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12581
12582 mHWData.assignCopy(aThat->mHWData);
12583
12584 // create copies of all shared folders (mHWData after attaching a copy
12585 // contains just references to original objects)
12586 for (HWData::SharedFolderList::iterator
12587 it = mHWData->mSharedFolders.begin();
12588 it != mHWData->mSharedFolders.end();
12589 ++it)
12590 {
12591 ComObjPtr<SharedFolder> folder;
12592 folder.createObject();
12593 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12594 AssertComRC(rc);
12595 *it = folder;
12596 }
12597
12598 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12599 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12600 mNvramStore->i_copyFrom(aThat->mNvramStore);
12601 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12602 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12603 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12604 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12605 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12606 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12607
12608 /* create private copies of all controllers */
12609 mStorageControllers.backup();
12610 mStorageControllers->clear();
12611 for (StorageControllerList::const_iterator
12612 it = aThat->mStorageControllers->begin();
12613 it != aThat->mStorageControllers->end();
12614 ++it)
12615 {
12616 ComObjPtr<StorageController> ctrl;
12617 ctrl.createObject();
12618 ctrl->initCopy(this, *it);
12619 mStorageControllers->push_back(ctrl);
12620 }
12621
12622 /* create private copies of all USB controllers */
12623 mUSBControllers.backup();
12624 mUSBControllers->clear();
12625 for (USBControllerList::const_iterator
12626 it = aThat->mUSBControllers->begin();
12627 it != aThat->mUSBControllers->end();
12628 ++it)
12629 {
12630 ComObjPtr<USBController> ctrl;
12631 ctrl.createObject();
12632 ctrl->initCopy(this, *it);
12633 mUSBControllers->push_back(ctrl);
12634 }
12635
12636 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12637 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12638 {
12639 if (mNetworkAdapters[slot].isNotNull())
12640 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12641 else
12642 {
12643 unconst(mNetworkAdapters[slot]).createObject();
12644 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12645 }
12646 }
12647 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12648 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12649 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12650 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12651}
12652
12653/**
12654 * Returns whether the given storage controller is hotplug capable.
12655 *
12656 * @returns true if the controller supports hotplugging
12657 * false otherwise.
12658 * @param enmCtrlType The controller type to check for.
12659 */
12660bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12661{
12662 ComPtr<ISystemProperties> systemProperties;
12663 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12664 if (FAILED(rc))
12665 return false;
12666
12667 BOOL aHotplugCapable = FALSE;
12668 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12669
12670 return RT_BOOL(aHotplugCapable);
12671}
12672
12673#ifdef VBOX_WITH_RESOURCE_USAGE_API
12674
12675void Machine::i_getDiskList(MediaList &list)
12676{
12677 for (MediumAttachmentList::const_iterator
12678 it = mMediumAttachments->begin();
12679 it != mMediumAttachments->end();
12680 ++it)
12681 {
12682 MediumAttachment *pAttach = *it;
12683 /* just in case */
12684 AssertContinue(pAttach);
12685
12686 AutoCaller localAutoCallerA(pAttach);
12687 if (FAILED(localAutoCallerA.rc())) continue;
12688
12689 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12690
12691 if (pAttach->i_getType() == DeviceType_HardDisk)
12692 list.push_back(pAttach->i_getMedium());
12693 }
12694}
12695
12696void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12697{
12698 AssertReturnVoid(isWriteLockOnCurrentThread());
12699 AssertPtrReturnVoid(aCollector);
12700
12701 pm::CollectorHAL *hal = aCollector->getHAL();
12702 /* Create sub metrics */
12703 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12704 "Percentage of processor time spent in user mode by the VM process.");
12705 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12706 "Percentage of processor time spent in kernel mode by the VM process.");
12707 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12708 "Size of resident portion of VM process in memory.");
12709 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12710 "Actual size of all VM disks combined.");
12711 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12712 "Network receive rate.");
12713 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12714 "Network transmit rate.");
12715 /* Create and register base metrics */
12716 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12717 cpuLoadUser, cpuLoadKernel);
12718 aCollector->registerBaseMetric(cpuLoad);
12719 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12720 ramUsageUsed);
12721 aCollector->registerBaseMetric(ramUsage);
12722 MediaList disks;
12723 i_getDiskList(disks);
12724 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12725 diskUsageUsed);
12726 aCollector->registerBaseMetric(diskUsage);
12727
12728 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12729 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12730 new pm::AggregateAvg()));
12731 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12732 new pm::AggregateMin()));
12733 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12734 new pm::AggregateMax()));
12735 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12736 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12737 new pm::AggregateAvg()));
12738 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12739 new pm::AggregateMin()));
12740 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12741 new pm::AggregateMax()));
12742
12743 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12744 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12745 new pm::AggregateAvg()));
12746 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12747 new pm::AggregateMin()));
12748 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12749 new pm::AggregateMax()));
12750
12751 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12752 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12753 new pm::AggregateAvg()));
12754 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12755 new pm::AggregateMin()));
12756 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12757 new pm::AggregateMax()));
12758
12759
12760 /* Guest metrics collector */
12761 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12762 aCollector->registerGuest(mCollectorGuest);
12763 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12764
12765 /* Create sub metrics */
12766 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12767 "Percentage of processor time spent in user mode as seen by the guest.");
12768 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12769 "Percentage of processor time spent in kernel mode as seen by the guest.");
12770 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12771 "Percentage of processor time spent idling as seen by the guest.");
12772
12773 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12774 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12775 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12776 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12777 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12778 pm::SubMetric *guestMemCache = new pm::SubMetric(
12779 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12780
12781 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12782 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12783
12784 /* Create and register base metrics */
12785 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12786 machineNetRx, machineNetTx);
12787 aCollector->registerBaseMetric(machineNetRate);
12788
12789 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12790 guestLoadUser, guestLoadKernel, guestLoadIdle);
12791 aCollector->registerBaseMetric(guestCpuLoad);
12792
12793 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12794 guestMemTotal, guestMemFree,
12795 guestMemBalloon, guestMemShared,
12796 guestMemCache, guestPagedTotal);
12797 aCollector->registerBaseMetric(guestCpuMem);
12798
12799 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12800 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12801 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12802 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12803
12804 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12805 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12806 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12807 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12808
12809 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12810 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12811 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12812 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12813
12814 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12815 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12816 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12817 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12818
12819 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12820 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12821 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12822 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12823
12824 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12825 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12826 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12827 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12828
12829 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12830 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12831 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12832 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12833
12834 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12835 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12836 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12837 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12838
12839 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12840 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12841 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12842 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12843
12844 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12845 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12846 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12847 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12848
12849 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12850 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12851 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12852 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12853}
12854
12855void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12856{
12857 AssertReturnVoid(isWriteLockOnCurrentThread());
12858
12859 if (aCollector)
12860 {
12861 aCollector->unregisterMetricsFor(aMachine);
12862 aCollector->unregisterBaseMetricsFor(aMachine);
12863 }
12864}
12865
12866#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12867
12868
12869////////////////////////////////////////////////////////////////////////////////
12870
12871DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12872
12873HRESULT SessionMachine::FinalConstruct()
12874{
12875 LogFlowThisFunc(("\n"));
12876
12877 mClientToken = NULL;
12878
12879 return BaseFinalConstruct();
12880}
12881
12882void SessionMachine::FinalRelease()
12883{
12884 LogFlowThisFunc(("\n"));
12885
12886 Assert(!mClientToken);
12887 /* paranoia, should not hang around any more */
12888 if (mClientToken)
12889 {
12890 delete mClientToken;
12891 mClientToken = NULL;
12892 }
12893
12894 uninit(Uninit::Unexpected);
12895
12896 BaseFinalRelease();
12897}
12898
12899/**
12900 * @note Must be called only by Machine::LockMachine() from its own write lock.
12901 */
12902HRESULT SessionMachine::init(Machine *aMachine)
12903{
12904 LogFlowThisFuncEnter();
12905 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12906
12907 AssertReturn(aMachine, E_INVALIDARG);
12908
12909 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12910
12911 /* Enclose the state transition NotReady->InInit->Ready */
12912 AutoInitSpan autoInitSpan(this);
12913 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12914
12915 HRESULT rc = S_OK;
12916
12917 RT_ZERO(mAuthLibCtx);
12918
12919 /* create the machine client token */
12920 try
12921 {
12922 mClientToken = new ClientToken(aMachine, this);
12923 if (!mClientToken->isReady())
12924 {
12925 delete mClientToken;
12926 mClientToken = NULL;
12927 rc = E_FAIL;
12928 }
12929 }
12930 catch (std::bad_alloc &)
12931 {
12932 rc = E_OUTOFMEMORY;
12933 }
12934 if (FAILED(rc))
12935 return rc;
12936
12937 /* memorize the peer Machine */
12938 unconst(mPeer) = aMachine;
12939 /* share the parent pointer */
12940 unconst(mParent) = aMachine->mParent;
12941
12942 /* take the pointers to data to share */
12943 mData.share(aMachine->mData);
12944 mSSData.share(aMachine->mSSData);
12945
12946 mUserData.share(aMachine->mUserData);
12947 mHWData.share(aMachine->mHWData);
12948 mMediumAttachments.share(aMachine->mMediumAttachments);
12949
12950 mStorageControllers.allocate();
12951 for (StorageControllerList::const_iterator
12952 it = aMachine->mStorageControllers->begin();
12953 it != aMachine->mStorageControllers->end();
12954 ++it)
12955 {
12956 ComObjPtr<StorageController> ctl;
12957 ctl.createObject();
12958 ctl->init(this, *it);
12959 mStorageControllers->push_back(ctl);
12960 }
12961
12962 mUSBControllers.allocate();
12963 for (USBControllerList::const_iterator
12964 it = aMachine->mUSBControllers->begin();
12965 it != aMachine->mUSBControllers->end();
12966 ++it)
12967 {
12968 ComObjPtr<USBController> ctl;
12969 ctl.createObject();
12970 ctl->init(this, *it);
12971 mUSBControllers->push_back(ctl);
12972 }
12973
12974 unconst(mBIOSSettings).createObject();
12975 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12976
12977 unconst(mTrustedPlatformModule).createObject();
12978 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
12979
12980 unconst(mNvramStore).createObject();
12981 mNvramStore->init(this, aMachine->mNvramStore);
12982
12983 unconst(mRecordingSettings).createObject();
12984 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12985 /* create another GraphicsAdapter object that will be mutable */
12986 unconst(mGraphicsAdapter).createObject();
12987 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12988 /* create another VRDEServer object that will be mutable */
12989 unconst(mVRDEServer).createObject();
12990 mVRDEServer->init(this, aMachine->mVRDEServer);
12991 /* create another audio adapter object that will be mutable */
12992 unconst(mAudioAdapter).createObject();
12993 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12994 /* create a list of serial ports that will be mutable */
12995 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12996 {
12997 unconst(mSerialPorts[slot]).createObject();
12998 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12999 }
13000 /* create a list of parallel ports that will be mutable */
13001 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
13002 {
13003 unconst(mParallelPorts[slot]).createObject();
13004 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
13005 }
13006
13007 /* create another USB device filters object that will be mutable */
13008 unconst(mUSBDeviceFilters).createObject();
13009 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
13010
13011 /* create a list of network adapters that will be mutable */
13012 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
13013 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13014 {
13015 unconst(mNetworkAdapters[slot]).createObject();
13016 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
13017 }
13018
13019 /* create another bandwidth control object that will be mutable */
13020 unconst(mBandwidthControl).createObject();
13021 mBandwidthControl->init(this, aMachine->mBandwidthControl);
13022
13023 /* default is to delete saved state on Saved -> PoweredOff transition */
13024 mRemoveSavedState = true;
13025
13026 /* Confirm a successful initialization when it's the case */
13027 autoInitSpan.setSucceeded();
13028
13029 miNATNetworksStarted = 0;
13030
13031 LogFlowThisFuncLeave();
13032 return rc;
13033}
13034
13035/**
13036 * Uninitializes this session object. If the reason is other than
13037 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
13038 * or the client watcher code.
13039 *
13040 * @param aReason uninitialization reason
13041 *
13042 * @note Locks mParent + this object for writing.
13043 */
13044void SessionMachine::uninit(Uninit::Reason aReason)
13045{
13046 LogFlowThisFuncEnter();
13047 LogFlowThisFunc(("reason=%d\n", aReason));
13048
13049 /*
13050 * Strongly reference ourselves to prevent this object deletion after
13051 * mData->mSession.mMachine.setNull() below (which can release the last
13052 * reference and call the destructor). Important: this must be done before
13053 * accessing any members (and before AutoUninitSpan that does it as well).
13054 * This self reference will be released as the very last step on return.
13055 */
13056 ComObjPtr<SessionMachine> selfRef;
13057 if (aReason != Uninit::Unexpected)
13058 selfRef = this;
13059
13060 /* Enclose the state transition Ready->InUninit->NotReady */
13061 AutoUninitSpan autoUninitSpan(this);
13062 if (autoUninitSpan.uninitDone())
13063 {
13064 LogFlowThisFunc(("Already uninitialized\n"));
13065 LogFlowThisFuncLeave();
13066 return;
13067 }
13068
13069 if (autoUninitSpan.initFailed())
13070 {
13071 /* We've been called by init() because it's failed. It's not really
13072 * necessary (nor it's safe) to perform the regular uninit sequence
13073 * below, the following is enough.
13074 */
13075 LogFlowThisFunc(("Initialization failed.\n"));
13076 /* destroy the machine client token */
13077 if (mClientToken)
13078 {
13079 delete mClientToken;
13080 mClientToken = NULL;
13081 }
13082 uninitDataAndChildObjects();
13083 mData.free();
13084 unconst(mParent) = NULL;
13085 unconst(mPeer) = NULL;
13086 LogFlowThisFuncLeave();
13087 return;
13088 }
13089
13090 MachineState_T lastState;
13091 {
13092 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
13093 lastState = mData->mMachineState;
13094 }
13095 NOREF(lastState);
13096
13097#ifdef VBOX_WITH_USB
13098 // release all captured USB devices, but do this before requesting the locks below
13099 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
13100 {
13101 /* Console::captureUSBDevices() is called in the VM process only after
13102 * setting the machine state to Starting or Restoring.
13103 * Console::detachAllUSBDevices() will be called upon successful
13104 * termination. So, we need to release USB devices only if there was
13105 * an abnormal termination of a running VM.
13106 *
13107 * This is identical to SessionMachine::DetachAllUSBDevices except
13108 * for the aAbnormal argument. */
13109 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13110 AssertComRC(rc);
13111 NOREF(rc);
13112
13113 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13114 if (service)
13115 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
13116 }
13117#endif /* VBOX_WITH_USB */
13118
13119 // we need to lock this object in uninit() because the lock is shared
13120 // with mPeer (as well as data we modify below). mParent lock is needed
13121 // by several calls to it.
13122 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
13123
13124#ifdef VBOX_WITH_RESOURCE_USAGE_API
13125 /*
13126 * It is safe to call Machine::i_unregisterMetrics() here because
13127 * PerformanceCollector::samplerCallback no longer accesses guest methods
13128 * holding the lock.
13129 */
13130 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
13131 /* The guest must be unregistered after its metrics (@bugref{5949}). */
13132 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
13133 if (mCollectorGuest)
13134 {
13135 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
13136 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
13137 mCollectorGuest = NULL;
13138 }
13139#endif
13140
13141 if (aReason == Uninit::Abnormal)
13142 {
13143 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
13144
13145 /*
13146 * Move the VM to the 'Aborted' machine state unless we are restoring a
13147 * VM that was in the 'Saved' machine state. In that case, if the VM
13148 * fails before reaching either the 'Restoring' machine state or the
13149 * 'Running' machine state then we set the machine state to
13150 * 'AbortedSaved' in order to preserve the saved state file so that the
13151 * VM can be restored in the future.
13152 */
13153 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
13154 i_setMachineState(MachineState_AbortedSaved);
13155 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
13156 i_setMachineState(MachineState_Aborted);
13157 }
13158
13159 // any machine settings modified?
13160 if (mData->flModifications)
13161 {
13162 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
13163 i_rollback(false /* aNotify */);
13164 }
13165
13166 mData->mSession.mPID = NIL_RTPROCESS;
13167
13168 if (aReason == Uninit::Unexpected)
13169 {
13170 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
13171 * client watcher thread to update the set of machines that have open
13172 * sessions. */
13173 mParent->i_updateClientWatcher();
13174 }
13175
13176 /* uninitialize all remote controls */
13177 if (mData->mSession.mRemoteControls.size())
13178 {
13179 LogFlowThisFunc(("Closing remote sessions (%d):\n",
13180 mData->mSession.mRemoteControls.size()));
13181
13182 /* Always restart a the beginning, since the iterator is invalidated
13183 * by using erase(). */
13184 for (Data::Session::RemoteControlList::iterator
13185 it = mData->mSession.mRemoteControls.begin();
13186 it != mData->mSession.mRemoteControls.end();
13187 it = mData->mSession.mRemoteControls.begin())
13188 {
13189 ComPtr<IInternalSessionControl> pControl = *it;
13190 mData->mSession.mRemoteControls.erase(it);
13191 multilock.release();
13192 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
13193 HRESULT rc = pControl->Uninitialize();
13194 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
13195 if (FAILED(rc))
13196 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
13197 multilock.acquire();
13198 }
13199 mData->mSession.mRemoteControls.clear();
13200 }
13201
13202 /* Remove all references to the NAT network service. The service will stop
13203 * if all references (also from other VMs) are removed. */
13204 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
13205 {
13206 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13207 {
13208 BOOL enabled;
13209 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13210 if ( FAILED(hrc)
13211 || !enabled)
13212 continue;
13213
13214 NetworkAttachmentType_T type;
13215 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13216 if ( SUCCEEDED(hrc)
13217 && type == NetworkAttachmentType_NATNetwork)
13218 {
13219 Bstr name;
13220 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13221 if (SUCCEEDED(hrc))
13222 {
13223 multilock.release();
13224 Utf8Str strName(name);
13225 LogRel(("VM '%s' stops using NAT network '%s'\n",
13226 mUserData->s.strName.c_str(), strName.c_str()));
13227 mParent->i_natNetworkRefDec(strName);
13228 multilock.acquire();
13229 }
13230 }
13231 }
13232 }
13233
13234 /*
13235 * An expected uninitialization can come only from #i_checkForDeath().
13236 * Otherwise it means that something's gone really wrong (for example,
13237 * the Session implementation has released the VirtualBox reference
13238 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
13239 * etc). However, it's also possible, that the client releases the IPC
13240 * semaphore correctly (i.e. before it releases the VirtualBox reference),
13241 * but the VirtualBox release event comes first to the server process.
13242 * This case is practically possible, so we should not assert on an
13243 * unexpected uninit, just log a warning.
13244 */
13245
13246 if (aReason == Uninit::Unexpected)
13247 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
13248
13249 if (aReason != Uninit::Normal)
13250 {
13251 mData->mSession.mDirectControl.setNull();
13252 }
13253 else
13254 {
13255 /* this must be null here (see #OnSessionEnd()) */
13256 Assert(mData->mSession.mDirectControl.isNull());
13257 Assert(mData->mSession.mState == SessionState_Unlocking);
13258 Assert(!mData->mSession.mProgress.isNull());
13259 }
13260 if (mData->mSession.mProgress)
13261 {
13262 if (aReason == Uninit::Normal)
13263 mData->mSession.mProgress->i_notifyComplete(S_OK);
13264 else
13265 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
13266 COM_IIDOF(ISession),
13267 getComponentName(),
13268 tr("The VM session was aborted"));
13269 mData->mSession.mProgress.setNull();
13270 }
13271
13272 if (mConsoleTaskData.mProgress)
13273 {
13274 Assert(aReason == Uninit::Abnormal);
13275 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
13276 COM_IIDOF(ISession),
13277 getComponentName(),
13278 tr("The VM session was aborted"));
13279 mConsoleTaskData.mProgress.setNull();
13280 }
13281
13282 /* remove the association between the peer machine and this session machine */
13283 Assert( (SessionMachine*)mData->mSession.mMachine == this
13284 || aReason == Uninit::Unexpected);
13285
13286 /* reset the rest of session data */
13287 mData->mSession.mLockType = LockType_Null;
13288 mData->mSession.mMachine.setNull();
13289 mData->mSession.mState = SessionState_Unlocked;
13290 mData->mSession.mName.setNull();
13291
13292 /* destroy the machine client token before leaving the exclusive lock */
13293 if (mClientToken)
13294 {
13295 delete mClientToken;
13296 mClientToken = NULL;
13297 }
13298
13299 /* fire an event */
13300 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
13301
13302 uninitDataAndChildObjects();
13303
13304 /* free the essential data structure last */
13305 mData.free();
13306
13307 /* release the exclusive lock before setting the below two to NULL */
13308 multilock.release();
13309
13310 unconst(mParent) = NULL;
13311 unconst(mPeer) = NULL;
13312
13313 AuthLibUnload(&mAuthLibCtx);
13314
13315 LogFlowThisFuncLeave();
13316}
13317
13318// util::Lockable interface
13319////////////////////////////////////////////////////////////////////////////////
13320
13321/**
13322 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13323 * with the primary Machine instance (mPeer).
13324 */
13325RWLockHandle *SessionMachine::lockHandle() const
13326{
13327 AssertReturn(mPeer != NULL, NULL);
13328 return mPeer->lockHandle();
13329}
13330
13331// IInternalMachineControl methods
13332////////////////////////////////////////////////////////////////////////////////
13333
13334/**
13335 * Passes collected guest statistics to performance collector object
13336 */
13337HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13338 ULONG aCpuKernel, ULONG aCpuIdle,
13339 ULONG aMemTotal, ULONG aMemFree,
13340 ULONG aMemBalloon, ULONG aMemShared,
13341 ULONG aMemCache, ULONG aPageTotal,
13342 ULONG aAllocVMM, ULONG aFreeVMM,
13343 ULONG aBalloonedVMM, ULONG aSharedVMM,
13344 ULONG aVmNetRx, ULONG aVmNetTx)
13345{
13346#ifdef VBOX_WITH_RESOURCE_USAGE_API
13347 if (mCollectorGuest)
13348 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13349 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13350 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13351 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13352
13353 return S_OK;
13354#else
13355 NOREF(aValidStats);
13356 NOREF(aCpuUser);
13357 NOREF(aCpuKernel);
13358 NOREF(aCpuIdle);
13359 NOREF(aMemTotal);
13360 NOREF(aMemFree);
13361 NOREF(aMemBalloon);
13362 NOREF(aMemShared);
13363 NOREF(aMemCache);
13364 NOREF(aPageTotal);
13365 NOREF(aAllocVMM);
13366 NOREF(aFreeVMM);
13367 NOREF(aBalloonedVMM);
13368 NOREF(aSharedVMM);
13369 NOREF(aVmNetRx);
13370 NOREF(aVmNetTx);
13371 return E_NOTIMPL;
13372#endif
13373}
13374
13375////////////////////////////////////////////////////////////////////////////////
13376//
13377// SessionMachine task records
13378//
13379////////////////////////////////////////////////////////////////////////////////
13380
13381/**
13382 * Task record for saving the machine state.
13383 */
13384class SessionMachine::SaveStateTask
13385 : public Machine::Task
13386{
13387public:
13388 SaveStateTask(SessionMachine *m,
13389 Progress *p,
13390 const Utf8Str &t,
13391 Reason_T enmReason,
13392 const Utf8Str &strStateFilePath)
13393 : Task(m, p, t),
13394 m_enmReason(enmReason),
13395 m_strStateFilePath(strStateFilePath)
13396 {}
13397
13398private:
13399 void handler()
13400 {
13401 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13402 }
13403
13404 Reason_T m_enmReason;
13405 Utf8Str m_strStateFilePath;
13406
13407 friend class SessionMachine;
13408};
13409
13410/**
13411 * Task thread implementation for SessionMachine::SaveState(), called from
13412 * SessionMachine::taskHandler().
13413 *
13414 * @note Locks this object for writing.
13415 *
13416 * @param task
13417 * @return
13418 */
13419void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13420{
13421 LogFlowThisFuncEnter();
13422
13423 AutoCaller autoCaller(this);
13424 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13425 if (FAILED(autoCaller.rc()))
13426 {
13427 /* we might have been uninitialized because the session was accidentally
13428 * closed by the client, so don't assert */
13429 HRESULT rc = setError(E_FAIL,
13430 tr("The session has been accidentally closed"));
13431 task.m_pProgress->i_notifyComplete(rc);
13432 LogFlowThisFuncLeave();
13433 return;
13434 }
13435
13436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13437
13438 HRESULT rc = S_OK;
13439
13440 try
13441 {
13442 ComPtr<IInternalSessionControl> directControl;
13443 if (mData->mSession.mLockType == LockType_VM)
13444 directControl = mData->mSession.mDirectControl;
13445 if (directControl.isNull())
13446 throw setError(VBOX_E_INVALID_VM_STATE,
13447 tr("Trying to save state without a running VM"));
13448 alock.release();
13449 BOOL fSuspendedBySave;
13450 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13451 Assert(!fSuspendedBySave);
13452 alock.acquire();
13453
13454 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13455 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13456 throw E_FAIL);
13457
13458 if (SUCCEEDED(rc))
13459 {
13460 mSSData->strStateFilePath = task.m_strStateFilePath;
13461
13462 /* save all VM settings */
13463 rc = i_saveSettings(NULL, alock);
13464 // no need to check whether VirtualBox.xml needs saving also since
13465 // we can't have a name change pending at this point
13466 }
13467 else
13468 {
13469 // On failure, set the state to the state we had at the beginning.
13470 i_setMachineState(task.m_machineStateBackup);
13471 i_updateMachineStateOnClient();
13472
13473 // Delete the saved state file (might have been already created).
13474 // No need to check whether this is shared with a snapshot here
13475 // because we certainly created a fresh saved state file here.
13476 RTFileDelete(task.m_strStateFilePath.c_str());
13477 }
13478 }
13479 catch (HRESULT aRC) { rc = aRC; }
13480
13481 task.m_pProgress->i_notifyComplete(rc);
13482
13483 LogFlowThisFuncLeave();
13484}
13485
13486/**
13487 * @note Locks this object for writing.
13488 */
13489HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13490{
13491 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13492}
13493
13494HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13495{
13496 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13497
13498 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13499 if (FAILED(rc)) return rc;
13500
13501 if ( mData->mMachineState != MachineState_Running
13502 && mData->mMachineState != MachineState_Paused
13503 )
13504 return setError(VBOX_E_INVALID_VM_STATE,
13505 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13506 Global::stringifyMachineState(mData->mMachineState));
13507
13508 ComObjPtr<Progress> pProgress;
13509 pProgress.createObject();
13510 rc = pProgress->init(i_getVirtualBox(),
13511 static_cast<IMachine *>(this) /* aInitiator */,
13512 tr("Saving the execution state of the virtual machine"),
13513 FALSE /* aCancelable */);
13514 if (FAILED(rc))
13515 return rc;
13516
13517 Utf8Str strStateFilePath;
13518 i_composeSavedStateFilename(strStateFilePath);
13519
13520 /* create and start the task on a separate thread (note that it will not
13521 * start working until we release alock) */
13522 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13523 rc = pTask->createThread();
13524 if (FAILED(rc))
13525 return rc;
13526
13527 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13528 i_setMachineState(MachineState_Saving);
13529 i_updateMachineStateOnClient();
13530
13531 pProgress.queryInterfaceTo(aProgress.asOutParam());
13532
13533 return S_OK;
13534}
13535
13536/**
13537 * @note Locks this object for writing.
13538 */
13539HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13540{
13541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13542
13543 HRESULT rc = i_checkStateDependency(MutableStateDep);
13544 if (FAILED(rc)) return rc;
13545
13546 if ( mData->mMachineState != MachineState_PoweredOff
13547 && mData->mMachineState != MachineState_Teleported
13548 && mData->mMachineState != MachineState_Aborted
13549 )
13550 return setError(VBOX_E_INVALID_VM_STATE,
13551 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13552 Global::stringifyMachineState(mData->mMachineState));
13553
13554 com::Utf8Str stateFilePathFull;
13555 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13556 if (RT_FAILURE(vrc))
13557 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13558 tr("Invalid saved state file path '%s' (%Rrc)"),
13559 aSavedStateFile.c_str(),
13560 vrc);
13561
13562 mSSData->strStateFilePath = stateFilePathFull;
13563
13564 /* The below i_setMachineState() will detect the state transition and will
13565 * update the settings file */
13566
13567 return i_setMachineState(MachineState_Saved);
13568}
13569
13570/**
13571 * @note Locks this object for writing.
13572 */
13573HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13574{
13575 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13576
13577 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13578 if (FAILED(rc)) return rc;
13579
13580 if ( mData->mMachineState != MachineState_Saved
13581 && mData->mMachineState != MachineState_AbortedSaved)
13582 return setError(VBOX_E_INVALID_VM_STATE,
13583 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13584 Global::stringifyMachineState(mData->mMachineState));
13585
13586 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13587
13588 /*
13589 * Saved -> PoweredOff transition will be detected in the SessionMachine
13590 * and properly handled.
13591 */
13592 rc = i_setMachineState(MachineState_PoweredOff);
13593 return rc;
13594}
13595
13596
13597/**
13598 * @note Locks the same as #i_setMachineState() does.
13599 */
13600HRESULT SessionMachine::updateState(MachineState_T aState)
13601{
13602 return i_setMachineState(aState);
13603}
13604
13605/**
13606 * @note Locks this object for writing.
13607 */
13608HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13609{
13610 IProgress *pProgress(aProgress);
13611
13612 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13613
13614 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13615
13616 if (mData->mSession.mState != SessionState_Locked)
13617 return VBOX_E_INVALID_OBJECT_STATE;
13618
13619 if (!mData->mSession.mProgress.isNull())
13620 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13621
13622 /* If we didn't reference the NAT network service yet, add a reference to
13623 * force a start */
13624 if (miNATNetworksStarted < 1)
13625 {
13626 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13627 {
13628 BOOL enabled;
13629 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13630 if ( FAILED(hrc)
13631 || !enabled)
13632 continue;
13633
13634 NetworkAttachmentType_T type;
13635 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13636 if ( SUCCEEDED(hrc)
13637 && type == NetworkAttachmentType_NATNetwork)
13638 {
13639 Bstr name;
13640 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13641 if (SUCCEEDED(hrc))
13642 {
13643 Utf8Str strName(name);
13644 LogRel(("VM '%s' starts using NAT network '%s'\n",
13645 mUserData->s.strName.c_str(), strName.c_str()));
13646 mPeer->lockHandle()->unlockWrite();
13647 mParent->i_natNetworkRefInc(strName);
13648#ifdef RT_LOCK_STRICT
13649 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13650#else
13651 mPeer->lockHandle()->lockWrite();
13652#endif
13653 }
13654 }
13655 }
13656 miNATNetworksStarted++;
13657 }
13658
13659 LogFlowThisFunc(("returns S_OK.\n"));
13660 return S_OK;
13661}
13662
13663/**
13664 * @note Locks this object for writing.
13665 */
13666HRESULT SessionMachine::endPowerUp(LONG aResult)
13667{
13668 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13669
13670 if (mData->mSession.mState != SessionState_Locked)
13671 return VBOX_E_INVALID_OBJECT_STATE;
13672
13673 /* Finalize the LaunchVMProcess progress object. */
13674 if (mData->mSession.mProgress)
13675 {
13676 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13677 mData->mSession.mProgress.setNull();
13678 }
13679
13680 if (SUCCEEDED((HRESULT)aResult))
13681 {
13682#ifdef VBOX_WITH_RESOURCE_USAGE_API
13683 /* The VM has been powered up successfully, so it makes sense
13684 * now to offer the performance metrics for a running machine
13685 * object. Doing it earlier wouldn't be safe. */
13686 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13687 mData->mSession.mPID);
13688#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13689 }
13690
13691 return S_OK;
13692}
13693
13694/**
13695 * @note Locks this object for writing.
13696 */
13697HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13698{
13699 LogFlowThisFuncEnter();
13700
13701 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13702
13703 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13704 E_FAIL);
13705
13706 /* create a progress object to track operation completion */
13707 ComObjPtr<Progress> pProgress;
13708 pProgress.createObject();
13709 pProgress->init(i_getVirtualBox(),
13710 static_cast<IMachine *>(this) /* aInitiator */,
13711 tr("Stopping the virtual machine"),
13712 FALSE /* aCancelable */);
13713
13714 /* fill in the console task data */
13715 mConsoleTaskData.mLastState = mData->mMachineState;
13716 mConsoleTaskData.mProgress = pProgress;
13717
13718 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13719 i_setMachineState(MachineState_Stopping);
13720
13721 pProgress.queryInterfaceTo(aProgress.asOutParam());
13722
13723 return S_OK;
13724}
13725
13726/**
13727 * @note Locks this object for writing.
13728 */
13729HRESULT SessionMachine::endPoweringDown(LONG aResult,
13730 const com::Utf8Str &aErrMsg)
13731{
13732 HRESULT const hrcResult = (HRESULT)aResult;
13733 LogFlowThisFuncEnter();
13734
13735 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13736
13737 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13738 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13739 && mConsoleTaskData.mLastState != MachineState_Null,
13740 E_FAIL);
13741
13742 /*
13743 * On failure, set the state to the state we had when BeginPoweringDown()
13744 * was called (this is expected by Console::PowerDown() and the associated
13745 * task). On success the VM process already changed the state to
13746 * MachineState_PoweredOff, so no need to do anything.
13747 */
13748 if (FAILED(hrcResult))
13749 i_setMachineState(mConsoleTaskData.mLastState);
13750
13751 /* notify the progress object about operation completion */
13752 Assert(mConsoleTaskData.mProgress);
13753 if (SUCCEEDED(hrcResult))
13754 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13755 else
13756 {
13757 if (aErrMsg.length())
13758 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13759 COM_IIDOF(ISession),
13760 getComponentName(),
13761 aErrMsg.c_str());
13762 else
13763 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13764 }
13765
13766 /* clear out the temporary saved state data */
13767 mConsoleTaskData.mLastState = MachineState_Null;
13768 mConsoleTaskData.mProgress.setNull();
13769
13770 LogFlowThisFuncLeave();
13771 return S_OK;
13772}
13773
13774
13775/**
13776 * Goes through the USB filters of the given machine to see if the given
13777 * device matches any filter or not.
13778 *
13779 * @note Locks the same as USBController::hasMatchingFilter() does.
13780 */
13781HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13782 BOOL *aMatched,
13783 ULONG *aMaskedInterfaces)
13784{
13785 LogFlowThisFunc(("\n"));
13786
13787#ifdef VBOX_WITH_USB
13788 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13789#else
13790 NOREF(aDevice);
13791 NOREF(aMaskedInterfaces);
13792 *aMatched = FALSE;
13793#endif
13794
13795 return S_OK;
13796}
13797
13798/**
13799 * @note Locks the same as Host::captureUSBDevice() does.
13800 */
13801HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13802{
13803 LogFlowThisFunc(("\n"));
13804
13805#ifdef VBOX_WITH_USB
13806 /* if captureDeviceForVM() fails, it must have set extended error info */
13807 clearError();
13808 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13809 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13810 return rc;
13811
13812 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13813 AssertReturn(service, E_FAIL);
13814 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13815#else
13816 RT_NOREF(aId, aCaptureFilename);
13817 return E_NOTIMPL;
13818#endif
13819}
13820
13821/**
13822 * @note Locks the same as Host::detachUSBDevice() does.
13823 */
13824HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13825 BOOL aDone)
13826{
13827 LogFlowThisFunc(("\n"));
13828
13829#ifdef VBOX_WITH_USB
13830 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13831 AssertReturn(service, E_FAIL);
13832 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13833#else
13834 NOREF(aId);
13835 NOREF(aDone);
13836 return E_NOTIMPL;
13837#endif
13838}
13839
13840/**
13841 * Inserts all machine filters to the USB proxy service and then calls
13842 * Host::autoCaptureUSBDevices().
13843 *
13844 * Called by Console from the VM process upon VM startup.
13845 *
13846 * @note Locks what called methods lock.
13847 */
13848HRESULT SessionMachine::autoCaptureUSBDevices()
13849{
13850 LogFlowThisFunc(("\n"));
13851
13852#ifdef VBOX_WITH_USB
13853 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13854 AssertComRC(rc);
13855 NOREF(rc);
13856
13857 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13858 AssertReturn(service, E_FAIL);
13859 return service->autoCaptureDevicesForVM(this);
13860#else
13861 return S_OK;
13862#endif
13863}
13864
13865/**
13866 * Removes all machine filters from the USB proxy service and then calls
13867 * Host::detachAllUSBDevices().
13868 *
13869 * Called by Console from the VM process upon normal VM termination or by
13870 * SessionMachine::uninit() upon abnormal VM termination (from under the
13871 * Machine/SessionMachine lock).
13872 *
13873 * @note Locks what called methods lock.
13874 */
13875HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13876{
13877 LogFlowThisFunc(("\n"));
13878
13879#ifdef VBOX_WITH_USB
13880 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13881 AssertComRC(rc);
13882 NOREF(rc);
13883
13884 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13885 AssertReturn(service, E_FAIL);
13886 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13887#else
13888 NOREF(aDone);
13889 return S_OK;
13890#endif
13891}
13892
13893/**
13894 * @note Locks this object for writing.
13895 */
13896HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13897 ComPtr<IProgress> &aProgress)
13898{
13899 LogFlowThisFuncEnter();
13900
13901 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13902 /*
13903 * We don't assert below because it might happen that a non-direct session
13904 * informs us it is closed right after we've been uninitialized -- it's ok.
13905 */
13906
13907 /* get IInternalSessionControl interface */
13908 ComPtr<IInternalSessionControl> control(aSession);
13909
13910 ComAssertRet(!control.isNull(), E_INVALIDARG);
13911
13912 /* Creating a Progress object requires the VirtualBox lock, and
13913 * thus locking it here is required by the lock order rules. */
13914 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13915
13916 if (control == mData->mSession.mDirectControl)
13917 {
13918 /* The direct session is being normally closed by the client process
13919 * ----------------------------------------------------------------- */
13920
13921 /* go to the closing state (essential for all open*Session() calls and
13922 * for #i_checkForDeath()) */
13923 Assert(mData->mSession.mState == SessionState_Locked);
13924 mData->mSession.mState = SessionState_Unlocking;
13925
13926 /* set direct control to NULL to release the remote instance */
13927 mData->mSession.mDirectControl.setNull();
13928 LogFlowThisFunc(("Direct control is set to NULL\n"));
13929
13930 if (mData->mSession.mProgress)
13931 {
13932 /* finalize the progress, someone might wait if a frontend
13933 * closes the session before powering on the VM. */
13934 mData->mSession.mProgress->notifyComplete(E_FAIL,
13935 COM_IIDOF(ISession),
13936 getComponentName(),
13937 tr("The VM session was closed before any attempt to power it on"));
13938 mData->mSession.mProgress.setNull();
13939 }
13940
13941 /* Create the progress object the client will use to wait until
13942 * #i_checkForDeath() is called to uninitialize this session object after
13943 * it releases the IPC semaphore.
13944 * Note! Because we're "reusing" mProgress here, this must be a proxy
13945 * object just like for LaunchVMProcess. */
13946 Assert(mData->mSession.mProgress.isNull());
13947 ComObjPtr<ProgressProxy> progress;
13948 progress.createObject();
13949 ComPtr<IUnknown> pPeer(mPeer);
13950 progress->init(mParent, pPeer,
13951 Bstr(tr("Closing session")).raw(),
13952 FALSE /* aCancelable */);
13953 progress.queryInterfaceTo(aProgress.asOutParam());
13954 mData->mSession.mProgress = progress;
13955 }
13956 else
13957 {
13958 /* the remote session is being normally closed */
13959 bool found = false;
13960 for (Data::Session::RemoteControlList::iterator
13961 it = mData->mSession.mRemoteControls.begin();
13962 it != mData->mSession.mRemoteControls.end();
13963 ++it)
13964 {
13965 if (control == *it)
13966 {
13967 found = true;
13968 // This MUST be erase(it), not remove(*it) as the latter
13969 // triggers a very nasty use after free due to the place where
13970 // the value "lives".
13971 mData->mSession.mRemoteControls.erase(it);
13972 break;
13973 }
13974 }
13975 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
13976 E_INVALIDARG);
13977 }
13978
13979 /* signal the client watcher thread, because the client is going away */
13980 mParent->i_updateClientWatcher();
13981
13982 LogFlowThisFuncLeave();
13983 return S_OK;
13984}
13985
13986HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13987 std::vector<com::Utf8Str> &aValues,
13988 std::vector<LONG64> &aTimestamps,
13989 std::vector<com::Utf8Str> &aFlags)
13990{
13991 LogFlowThisFunc(("\n"));
13992
13993#ifdef VBOX_WITH_GUEST_PROPS
13994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13995
13996 size_t cEntries = mHWData->mGuestProperties.size();
13997 aNames.resize(cEntries);
13998 aValues.resize(cEntries);
13999 aTimestamps.resize(cEntries);
14000 aFlags.resize(cEntries);
14001
14002 size_t i = 0;
14003 for (HWData::GuestPropertyMap::const_iterator
14004 it = mHWData->mGuestProperties.begin();
14005 it != mHWData->mGuestProperties.end();
14006 ++it, ++i)
14007 {
14008 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
14009 aNames[i] = it->first;
14010 aValues[i] = it->second.strValue;
14011 aTimestamps[i] = it->second.mTimestamp;
14012
14013 /* If it is NULL, keep it NULL. */
14014 if (it->second.mFlags)
14015 {
14016 GuestPropWriteFlags(it->second.mFlags, szFlags);
14017 aFlags[i] = szFlags;
14018 }
14019 else
14020 aFlags[i] = "";
14021
14022 AssertReturn(RT_SUCCESS(GuestPropValidateName(aNames[i].c_str(), (uint32_t)aNames[i].length() + 1 /* '\0' */)), E_INVALIDARG);
14023 AssertReturn(RT_SUCCESS(GuestPropValidateValue(aValues[i].c_str(), (uint32_t)aValues[i].length() + 1 /* '\0' */)), E_INVALIDARG);
14024 }
14025 return S_OK;
14026#else
14027 ReturnComNotImplemented();
14028#endif
14029}
14030
14031HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
14032 const com::Utf8Str &aValue,
14033 LONG64 aTimestamp,
14034 const com::Utf8Str &aFlags,
14035 BOOL fWasDeleted)
14036{
14037 LogFlowThisFunc(("\n"));
14038
14039#ifdef VBOX_WITH_GUEST_PROPS
14040 try
14041 {
14042 /*
14043 * Convert input up front.
14044 */
14045 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
14046 if (aFlags.length())
14047 {
14048 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
14049 AssertRCReturn(vrc, E_INVALIDARG);
14050 }
14051
14052 /*
14053 * Now grab the object lock, validate the state and do the update.
14054 */
14055
14056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14057
14058 if (!Global::IsOnline(mData->mMachineState))
14059 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
14060
14061 i_setModified(IsModified_MachineData);
14062 mHWData.backup();
14063
14064 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
14065 if (it != mHWData->mGuestProperties.end())
14066 {
14067 if (!fWasDeleted)
14068 {
14069 it->second.strValue = aValue;
14070 it->second.mTimestamp = aTimestamp;
14071 it->second.mFlags = fFlags;
14072 }
14073 else
14074 mHWData->mGuestProperties.erase(it);
14075
14076 mData->mGuestPropertiesModified = TRUE;
14077 }
14078 else if (!fWasDeleted)
14079 {
14080 HWData::GuestProperty prop;
14081 prop.strValue = aValue;
14082 prop.mTimestamp = aTimestamp;
14083 prop.mFlags = fFlags;
14084
14085 mHWData->mGuestProperties[aName] = prop;
14086 mData->mGuestPropertiesModified = TRUE;
14087 }
14088
14089 alock.release();
14090
14091 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fWasDeleted);
14092 }
14093 catch (...)
14094 {
14095 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
14096 }
14097 return S_OK;
14098#else
14099 ReturnComNotImplemented();
14100#endif
14101}
14102
14103
14104HRESULT SessionMachine::lockMedia()
14105{
14106 AutoMultiWriteLock2 alock(this->lockHandle(),
14107 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14108
14109 AssertReturn( mData->mMachineState == MachineState_Starting
14110 || mData->mMachineState == MachineState_Restoring
14111 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
14112
14113 clearError();
14114 alock.release();
14115 return i_lockMedia();
14116}
14117
14118HRESULT SessionMachine::unlockMedia()
14119{
14120 HRESULT hrc = i_unlockMedia();
14121 return hrc;
14122}
14123
14124HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14125 ComPtr<IMediumAttachment> &aNewAttachment)
14126{
14127 // request the host lock first, since might be calling Host methods for getting host drives;
14128 // next, protect the media tree all the while we're in here, as well as our member variables
14129 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
14130 this->lockHandle(),
14131 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14132
14133 IMediumAttachment *iAttach = aAttachment;
14134 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
14135
14136 Utf8Str ctrlName;
14137 LONG lPort;
14138 LONG lDevice;
14139 bool fTempEject;
14140 {
14141 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14142
14143 /* Need to query the details first, as the IMediumAttachment reference
14144 * might be to the original settings, which we are going to change. */
14145 ctrlName = pAttach->i_getControllerName();
14146 lPort = pAttach->i_getPort();
14147 lDevice = pAttach->i_getDevice();
14148 fTempEject = pAttach->i_getTempEject();
14149 }
14150
14151 if (!fTempEject)
14152 {
14153 /* Remember previously mounted medium. The medium before taking the
14154 * backup is not necessarily the same thing. */
14155 ComObjPtr<Medium> oldmedium;
14156 oldmedium = pAttach->i_getMedium();
14157
14158 i_setModified(IsModified_Storage);
14159 mMediumAttachments.backup();
14160
14161 // The backup operation makes the pAttach reference point to the
14162 // old settings. Re-get the correct reference.
14163 pAttach = i_findAttachment(*mMediumAttachments.data(),
14164 ctrlName,
14165 lPort,
14166 lDevice);
14167
14168 {
14169 AutoCaller autoAttachCaller(this);
14170 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
14171
14172 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14173 if (!oldmedium.isNull())
14174 oldmedium->i_removeBackReference(mData->mUuid);
14175
14176 pAttach->i_updateMedium(NULL);
14177 pAttach->i_updateEjected();
14178 }
14179
14180 i_setModified(IsModified_Storage);
14181 }
14182 else
14183 {
14184 {
14185 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14186 pAttach->i_updateEjected();
14187 }
14188 }
14189
14190 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
14191
14192 return S_OK;
14193}
14194
14195HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
14196 com::Utf8Str &aResult)
14197{
14198 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14199
14200 HRESULT hr = S_OK;
14201
14202 if (!mAuthLibCtx.hAuthLibrary)
14203 {
14204 /* Load the external authentication library. */
14205 Bstr authLibrary;
14206 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
14207
14208 Utf8Str filename = authLibrary;
14209
14210 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
14211 if (RT_FAILURE(vrc))
14212 hr = setErrorBoth(E_FAIL, vrc,
14213 tr("Could not load the external authentication library '%s' (%Rrc)"),
14214 filename.c_str(), vrc);
14215 }
14216
14217 /* The auth library might need the machine lock. */
14218 alock.release();
14219
14220 if (FAILED(hr))
14221 return hr;
14222
14223 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
14224 {
14225 enum VRDEAuthParams
14226 {
14227 parmUuid = 1,
14228 parmGuestJudgement,
14229 parmUser,
14230 parmPassword,
14231 parmDomain,
14232 parmClientId
14233 };
14234
14235 AuthResult result = AuthResultAccessDenied;
14236
14237 Guid uuid(aAuthParams[parmUuid]);
14238 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
14239 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
14240
14241 result = AuthLibAuthenticate(&mAuthLibCtx,
14242 uuid.raw(), guestJudgement,
14243 aAuthParams[parmUser].c_str(),
14244 aAuthParams[parmPassword].c_str(),
14245 aAuthParams[parmDomain].c_str(),
14246 u32ClientId);
14247
14248 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
14249 size_t cbPassword = aAuthParams[parmPassword].length();
14250 if (cbPassword)
14251 {
14252 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
14253 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
14254 }
14255
14256 if (result == AuthResultAccessGranted)
14257 aResult = "granted";
14258 else
14259 aResult = "denied";
14260
14261 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
14262 aAuthParams[parmUser].c_str(), aResult.c_str()));
14263 }
14264 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
14265 {
14266 enum VRDEAuthDisconnectParams
14267 {
14268 parmUuid = 1,
14269 parmClientId
14270 };
14271
14272 Guid uuid(aAuthParams[parmUuid]);
14273 uint32_t u32ClientId = 0;
14274 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
14275 }
14276 else
14277 {
14278 hr = E_INVALIDARG;
14279 }
14280
14281 return hr;
14282}
14283
14284// public methods only for internal purposes
14285/////////////////////////////////////////////////////////////////////////////
14286
14287#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14288/**
14289 * Called from the client watcher thread to check for expected or unexpected
14290 * death of the client process that has a direct session to this machine.
14291 *
14292 * On Win32 and on OS/2, this method is called only when we've got the
14293 * mutex (i.e. the client has either died or terminated normally) so it always
14294 * returns @c true (the client is terminated, the session machine is
14295 * uninitialized).
14296 *
14297 * On other platforms, the method returns @c true if the client process has
14298 * terminated normally or abnormally and the session machine was uninitialized,
14299 * and @c false if the client process is still alive.
14300 *
14301 * @note Locks this object for writing.
14302 */
14303bool SessionMachine::i_checkForDeath()
14304{
14305 Uninit::Reason reason;
14306 bool terminated = false;
14307
14308 /* Enclose autoCaller with a block because calling uninit() from under it
14309 * will deadlock. */
14310 {
14311 AutoCaller autoCaller(this);
14312 if (!autoCaller.isOk())
14313 {
14314 /* return true if not ready, to cause the client watcher to exclude
14315 * the corresponding session from watching */
14316 LogFlowThisFunc(("Already uninitialized!\n"));
14317 return true;
14318 }
14319
14320 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14321
14322 /* Determine the reason of death: if the session state is Closing here,
14323 * everything is fine. Otherwise it means that the client did not call
14324 * OnSessionEnd() before it released the IPC semaphore. This may happen
14325 * either because the client process has abnormally terminated, or
14326 * because it simply forgot to call ISession::Close() before exiting. We
14327 * threat the latter also as an abnormal termination (see
14328 * Session::uninit() for details). */
14329 reason = mData->mSession.mState == SessionState_Unlocking ?
14330 Uninit::Normal :
14331 Uninit::Abnormal;
14332
14333 if (mClientToken)
14334 terminated = mClientToken->release();
14335 } /* AutoCaller block */
14336
14337 if (terminated)
14338 uninit(reason);
14339
14340 return terminated;
14341}
14342
14343void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14344{
14345 LogFlowThisFunc(("\n"));
14346
14347 strTokenId.setNull();
14348
14349 AutoCaller autoCaller(this);
14350 AssertComRCReturnVoid(autoCaller.rc());
14351
14352 Assert(mClientToken);
14353 if (mClientToken)
14354 mClientToken->getId(strTokenId);
14355}
14356#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14357IToken *SessionMachine::i_getToken()
14358{
14359 LogFlowThisFunc(("\n"));
14360
14361 AutoCaller autoCaller(this);
14362 AssertComRCReturn(autoCaller.rc(), NULL);
14363
14364 Assert(mClientToken);
14365 if (mClientToken)
14366 return mClientToken->getToken();
14367 else
14368 return NULL;
14369}
14370#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14371
14372Machine::ClientToken *SessionMachine::i_getClientToken()
14373{
14374 LogFlowThisFunc(("\n"));
14375
14376 AutoCaller autoCaller(this);
14377 AssertComRCReturn(autoCaller.rc(), NULL);
14378
14379 return mClientToken;
14380}
14381
14382
14383/**
14384 * @note Locks this object for reading.
14385 */
14386HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14387{
14388 LogFlowThisFunc(("\n"));
14389
14390 AutoCaller autoCaller(this);
14391 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14392
14393 ComPtr<IInternalSessionControl> directControl;
14394 {
14395 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14396 if (mData->mSession.mLockType == LockType_VM)
14397 directControl = mData->mSession.mDirectControl;
14398 }
14399
14400 /* ignore notifications sent after #OnSessionEnd() is called */
14401 if (!directControl)
14402 return S_OK;
14403
14404 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14405}
14406
14407/**
14408 * @note Locks this object for reading.
14409 */
14410HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
14411 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
14412 const Utf8Str &aGuestIp, LONG aGuestPort)
14413{
14414 LogFlowThisFunc(("\n"));
14415
14416 AutoCaller autoCaller(this);
14417 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14418
14419 ComPtr<IInternalSessionControl> directControl;
14420 {
14421 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14422 if (mData->mSession.mLockType == LockType_VM)
14423 directControl = mData->mSession.mDirectControl;
14424 }
14425
14426 /* ignore notifications sent after #OnSessionEnd() is called */
14427 if (!directControl)
14428 return S_OK;
14429 /*
14430 * instead acting like callback we ask IVirtualBox deliver corresponding event
14431 */
14432
14433 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14434 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14435 return S_OK;
14436}
14437
14438/**
14439 * @note Locks this object for reading.
14440 */
14441HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14442{
14443 LogFlowThisFunc(("\n"));
14444
14445 AutoCaller autoCaller(this);
14446 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14447
14448 ComPtr<IInternalSessionControl> directControl;
14449 {
14450 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14451 if (mData->mSession.mLockType == LockType_VM)
14452 directControl = mData->mSession.mDirectControl;
14453 }
14454
14455 /* ignore notifications sent after #OnSessionEnd() is called */
14456 if (!directControl)
14457 return S_OK;
14458
14459 return directControl->OnAudioAdapterChange(audioAdapter);
14460}
14461
14462/**
14463 * @note Locks this object for reading.
14464 */
14465HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14466{
14467 LogFlowThisFunc(("\n"));
14468
14469 AutoCaller autoCaller(this);
14470 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14471
14472 ComPtr<IInternalSessionControl> directControl;
14473 {
14474 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14475 if (mData->mSession.mLockType == LockType_VM)
14476 directControl = mData->mSession.mDirectControl;
14477 }
14478
14479 /* ignore notifications sent after #OnSessionEnd() is called */
14480 if (!directControl)
14481 return S_OK;
14482
14483 return directControl->OnSerialPortChange(serialPort);
14484}
14485
14486/**
14487 * @note Locks this object for reading.
14488 */
14489HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14490{
14491 LogFlowThisFunc(("\n"));
14492
14493 AutoCaller autoCaller(this);
14494 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14495
14496 ComPtr<IInternalSessionControl> directControl;
14497 {
14498 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14499 if (mData->mSession.mLockType == LockType_VM)
14500 directControl = mData->mSession.mDirectControl;
14501 }
14502
14503 /* ignore notifications sent after #OnSessionEnd() is called */
14504 if (!directControl)
14505 return S_OK;
14506
14507 return directControl->OnParallelPortChange(parallelPort);
14508}
14509
14510/**
14511 * @note Locks this object for reading.
14512 */
14513HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14514{
14515 LogFlowThisFunc(("\n"));
14516
14517 AutoCaller autoCaller(this);
14518 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14519
14520 ComPtr<IInternalSessionControl> directControl;
14521 {
14522 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14523 if (mData->mSession.mLockType == LockType_VM)
14524 directControl = mData->mSession.mDirectControl;
14525 }
14526
14527 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14528
14529 /* ignore notifications sent after #OnSessionEnd() is called */
14530 if (!directControl)
14531 return S_OK;
14532
14533 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14534}
14535
14536/**
14537 * @note Locks this object for reading.
14538 */
14539HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14540{
14541 LogFlowThisFunc(("\n"));
14542
14543 AutoCaller autoCaller(this);
14544 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14545
14546 ComPtr<IInternalSessionControl> directControl;
14547 {
14548 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14549 if (mData->mSession.mLockType == LockType_VM)
14550 directControl = mData->mSession.mDirectControl;
14551 }
14552
14553 mParent->i_onMediumChanged(aAttachment);
14554
14555 /* ignore notifications sent after #OnSessionEnd() is called */
14556 if (!directControl)
14557 return S_OK;
14558
14559 return directControl->OnMediumChange(aAttachment, aForce);
14560}
14561
14562HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14563{
14564 LogFlowThisFunc(("\n"));
14565
14566 AutoCaller autoCaller(this);
14567 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14568
14569 ComPtr<IInternalSessionControl> directControl;
14570 {
14571 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14572 if (mData->mSession.mLockType == LockType_VM)
14573 directControl = mData->mSession.mDirectControl;
14574 }
14575
14576 /* ignore notifications sent after #OnSessionEnd() is called */
14577 if (!directControl)
14578 return S_OK;
14579
14580 return directControl->OnVMProcessPriorityChange(aPriority);
14581}
14582
14583/**
14584 * @note Locks this object for reading.
14585 */
14586HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14587{
14588 LogFlowThisFunc(("\n"));
14589
14590 AutoCaller autoCaller(this);
14591 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14592
14593 ComPtr<IInternalSessionControl> directControl;
14594 {
14595 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14596 if (mData->mSession.mLockType == LockType_VM)
14597 directControl = mData->mSession.mDirectControl;
14598 }
14599
14600 /* ignore notifications sent after #OnSessionEnd() is called */
14601 if (!directControl)
14602 return S_OK;
14603
14604 return directControl->OnCPUChange(aCPU, aRemove);
14605}
14606
14607HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14608{
14609 LogFlowThisFunc(("\n"));
14610
14611 AutoCaller autoCaller(this);
14612 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14613
14614 ComPtr<IInternalSessionControl> directControl;
14615 {
14616 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14617 if (mData->mSession.mLockType == LockType_VM)
14618 directControl = mData->mSession.mDirectControl;
14619 }
14620
14621 /* ignore notifications sent after #OnSessionEnd() is called */
14622 if (!directControl)
14623 return S_OK;
14624
14625 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14626}
14627
14628/**
14629 * @note Locks this object for reading.
14630 */
14631HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14632{
14633 LogFlowThisFunc(("\n"));
14634
14635 AutoCaller autoCaller(this);
14636 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14637
14638 ComPtr<IInternalSessionControl> directControl;
14639 {
14640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14641 if (mData->mSession.mLockType == LockType_VM)
14642 directControl = mData->mSession.mDirectControl;
14643 }
14644
14645 /* ignore notifications sent after #OnSessionEnd() is called */
14646 if (!directControl)
14647 return S_OK;
14648
14649 return directControl->OnVRDEServerChange(aRestart);
14650}
14651
14652/**
14653 * @note Locks this object for reading.
14654 */
14655HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14656{
14657 LogFlowThisFunc(("\n"));
14658
14659 AutoCaller autoCaller(this);
14660 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14661
14662 ComPtr<IInternalSessionControl> directControl;
14663 {
14664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14665 if (mData->mSession.mLockType == LockType_VM)
14666 directControl = mData->mSession.mDirectControl;
14667 }
14668
14669 /* ignore notifications sent after #OnSessionEnd() is called */
14670 if (!directControl)
14671 return S_OK;
14672
14673 return directControl->OnRecordingChange(aEnable);
14674}
14675
14676/**
14677 * @note Locks this object for reading.
14678 */
14679HRESULT SessionMachine::i_onUSBControllerChange()
14680{
14681 LogFlowThisFunc(("\n"));
14682
14683 AutoCaller autoCaller(this);
14684 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14685
14686 ComPtr<IInternalSessionControl> directControl;
14687 {
14688 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14689 if (mData->mSession.mLockType == LockType_VM)
14690 directControl = mData->mSession.mDirectControl;
14691 }
14692
14693 /* ignore notifications sent after #OnSessionEnd() is called */
14694 if (!directControl)
14695 return S_OK;
14696
14697 return directControl->OnUSBControllerChange();
14698}
14699
14700/**
14701 * @note Locks this object for reading.
14702 */
14703HRESULT SessionMachine::i_onSharedFolderChange()
14704{
14705 LogFlowThisFunc(("\n"));
14706
14707 AutoCaller autoCaller(this);
14708 AssertComRCReturnRC(autoCaller.rc());
14709
14710 ComPtr<IInternalSessionControl> directControl;
14711 {
14712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14713 if (mData->mSession.mLockType == LockType_VM)
14714 directControl = mData->mSession.mDirectControl;
14715 }
14716
14717 /* ignore notifications sent after #OnSessionEnd() is called */
14718 if (!directControl)
14719 return S_OK;
14720
14721 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14722}
14723
14724/**
14725 * @note Locks this object for reading.
14726 */
14727HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14728{
14729 LogFlowThisFunc(("\n"));
14730
14731 AutoCaller autoCaller(this);
14732 AssertComRCReturnRC(autoCaller.rc());
14733
14734 ComPtr<IInternalSessionControl> directControl;
14735 {
14736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14737 if (mData->mSession.mLockType == LockType_VM)
14738 directControl = mData->mSession.mDirectControl;
14739 }
14740
14741 /* ignore notifications sent after #OnSessionEnd() is called */
14742 if (!directControl)
14743 return S_OK;
14744
14745 return directControl->OnClipboardModeChange(aClipboardMode);
14746}
14747
14748/**
14749 * @note Locks this object for reading.
14750 */
14751HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14752{
14753 LogFlowThisFunc(("\n"));
14754
14755 AutoCaller autoCaller(this);
14756 AssertComRCReturnRC(autoCaller.rc());
14757
14758 ComPtr<IInternalSessionControl> directControl;
14759 {
14760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14761 if (mData->mSession.mLockType == LockType_VM)
14762 directControl = mData->mSession.mDirectControl;
14763 }
14764
14765 /* ignore notifications sent after #OnSessionEnd() is called */
14766 if (!directControl)
14767 return S_OK;
14768
14769 return directControl->OnClipboardFileTransferModeChange(aEnable);
14770}
14771
14772/**
14773 * @note Locks this object for reading.
14774 */
14775HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14776{
14777 LogFlowThisFunc(("\n"));
14778
14779 AutoCaller autoCaller(this);
14780 AssertComRCReturnRC(autoCaller.rc());
14781
14782 ComPtr<IInternalSessionControl> directControl;
14783 {
14784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14785 if (mData->mSession.mLockType == LockType_VM)
14786 directControl = mData->mSession.mDirectControl;
14787 }
14788
14789 /* ignore notifications sent after #OnSessionEnd() is called */
14790 if (!directControl)
14791 return S_OK;
14792
14793 return directControl->OnDnDModeChange(aDnDMode);
14794}
14795
14796/**
14797 * @note Locks this object for reading.
14798 */
14799HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14800{
14801 LogFlowThisFunc(("\n"));
14802
14803 AutoCaller autoCaller(this);
14804 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14805
14806 ComPtr<IInternalSessionControl> directControl;
14807 {
14808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14809 if (mData->mSession.mLockType == LockType_VM)
14810 directControl = mData->mSession.mDirectControl;
14811 }
14812
14813 /* ignore notifications sent after #OnSessionEnd() is called */
14814 if (!directControl)
14815 return S_OK;
14816
14817 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14818}
14819
14820/**
14821 * @note Locks this object for reading.
14822 */
14823HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14824{
14825 LogFlowThisFunc(("\n"));
14826
14827 AutoCaller autoCaller(this);
14828 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14829
14830 ComPtr<IInternalSessionControl> directControl;
14831 {
14832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14833 if (mData->mSession.mLockType == LockType_VM)
14834 directControl = mData->mSession.mDirectControl;
14835 }
14836
14837 /* ignore notifications sent after #OnSessionEnd() is called */
14838 if (!directControl)
14839 return S_OK;
14840
14841 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14842}
14843
14844/**
14845 * Returns @c true if this machine's USB controller reports it has a matching
14846 * filter for the given USB device and @c false otherwise.
14847 *
14848 * @note locks this object for reading.
14849 */
14850bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14851{
14852 AutoCaller autoCaller(this);
14853 /* silently return if not ready -- this method may be called after the
14854 * direct machine session has been called */
14855 if (!autoCaller.isOk())
14856 return false;
14857
14858#ifdef VBOX_WITH_USB
14859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14860
14861 switch (mData->mMachineState)
14862 {
14863 case MachineState_Starting:
14864 case MachineState_Restoring:
14865 case MachineState_TeleportingIn:
14866 case MachineState_Paused:
14867 case MachineState_Running:
14868 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14869 * elsewhere... */
14870 alock.release();
14871 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14872 default: break;
14873 }
14874#else
14875 NOREF(aDevice);
14876 NOREF(aMaskedIfs);
14877#endif
14878 return false;
14879}
14880
14881/**
14882 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14883 */
14884HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14885 IVirtualBoxErrorInfo *aError,
14886 ULONG aMaskedIfs,
14887 const com::Utf8Str &aCaptureFilename)
14888{
14889 LogFlowThisFunc(("\n"));
14890
14891 AutoCaller autoCaller(this);
14892
14893 /* This notification may happen after the machine object has been
14894 * uninitialized (the session was closed), so don't assert. */
14895 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14896
14897 ComPtr<IInternalSessionControl> directControl;
14898 {
14899 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14900 if (mData->mSession.mLockType == LockType_VM)
14901 directControl = mData->mSession.mDirectControl;
14902 }
14903
14904 /* fail on notifications sent after #OnSessionEnd() is called, it is
14905 * expected by the caller */
14906 if (!directControl)
14907 return E_FAIL;
14908
14909 /* No locks should be held at this point. */
14910 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14911 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14912
14913 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14914}
14915
14916/**
14917 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14918 */
14919HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14920 IVirtualBoxErrorInfo *aError)
14921{
14922 LogFlowThisFunc(("\n"));
14923
14924 AutoCaller autoCaller(this);
14925
14926 /* This notification may happen after the machine object has been
14927 * uninitialized (the session was closed), so don't assert. */
14928 if (FAILED(autoCaller.rc())) return 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 /* fail on notifications sent after #OnSessionEnd() is called, it is
14938 * expected by the caller */
14939 if (!directControl)
14940 return E_FAIL;
14941
14942 /* No locks should be held at this point. */
14943 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14944 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14945
14946 return directControl->OnUSBDeviceDetach(aId, aError);
14947}
14948
14949// protected methods
14950/////////////////////////////////////////////////////////////////////////////
14951
14952/**
14953 * Deletes the given file if it is no longer in use by either the current machine state
14954 * (if the machine is "saved") or any of the machine's snapshots.
14955 *
14956 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14957 * but is different for each SnapshotMachine. When calling this, the order of calling this
14958 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14959 * is therefore critical. I know, it's all rather messy.
14960 *
14961 * @param strStateFile
14962 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14963 * the test for whether the saved state file is in use.
14964 */
14965void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14966 Snapshot *pSnapshotToIgnore)
14967{
14968 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14969 if ( (strStateFile.isNotEmpty())
14970 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14971 )
14972 // ... and it must also not be shared with other snapshots
14973 if ( !mData->mFirstSnapshot
14974 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14975 // this checks the SnapshotMachine's state file paths
14976 )
14977 RTFileDelete(strStateFile.c_str());
14978}
14979
14980/**
14981 * Locks the attached media.
14982 *
14983 * All attached hard disks are locked for writing and DVD/floppy are locked for
14984 * reading. Parents of attached hard disks (if any) are locked for reading.
14985 *
14986 * This method also performs accessibility check of all media it locks: if some
14987 * media is inaccessible, the method will return a failure and a bunch of
14988 * extended error info objects per each inaccessible medium.
14989 *
14990 * Note that this method is atomic: if it returns a success, all media are
14991 * locked as described above; on failure no media is locked at all (all
14992 * succeeded individual locks will be undone).
14993 *
14994 * The caller is responsible for doing the necessary state sanity checks.
14995 *
14996 * The locks made by this method must be undone by calling #unlockMedia() when
14997 * no more needed.
14998 */
14999HRESULT SessionMachine::i_lockMedia()
15000{
15001 AutoCaller autoCaller(this);
15002 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15003
15004 AutoMultiWriteLock2 alock(this->lockHandle(),
15005 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
15006
15007 /* bail out if trying to lock things with already set up locking */
15008 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
15009
15010 MultiResult mrc(S_OK);
15011
15012 /* Collect locking information for all medium objects attached to the VM. */
15013 for (MediumAttachmentList::const_iterator
15014 it = mMediumAttachments->begin();
15015 it != mMediumAttachments->end();
15016 ++it)
15017 {
15018 MediumAttachment *pAtt = *it;
15019 DeviceType_T devType = pAtt->i_getType();
15020 Medium *pMedium = pAtt->i_getMedium();
15021
15022 MediumLockList *pMediumLockList(new MediumLockList());
15023 // There can be attachments without a medium (floppy/dvd), and thus
15024 // it's impossible to create a medium lock list. It still makes sense
15025 // to have the empty medium lock list in the map in case a medium is
15026 // attached later.
15027 if (pMedium != NULL)
15028 {
15029 MediumType_T mediumType = pMedium->i_getType();
15030 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
15031 || mediumType == MediumType_Shareable;
15032 bool fIsVitalImage = (devType == DeviceType_HardDisk);
15033
15034 alock.release();
15035 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
15036 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
15037 false /* fMediumLockWriteAll */,
15038 NULL,
15039 *pMediumLockList);
15040 alock.acquire();
15041 if (FAILED(mrc))
15042 {
15043 delete pMediumLockList;
15044 mData->mSession.mLockedMedia.Clear();
15045 break;
15046 }
15047 }
15048
15049 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
15050 if (FAILED(rc))
15051 {
15052 mData->mSession.mLockedMedia.Clear();
15053 mrc = setError(rc,
15054 tr("Collecting locking information for all attached media failed"));
15055 break;
15056 }
15057 }
15058
15059 if (SUCCEEDED(mrc))
15060 {
15061 /* Now lock all media. If this fails, nothing is locked. */
15062 alock.release();
15063 HRESULT rc = mData->mSession.mLockedMedia.Lock();
15064 alock.acquire();
15065 if (FAILED(rc))
15066 {
15067 mrc = setError(rc,
15068 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
15069 }
15070 }
15071
15072 return mrc;
15073}
15074
15075/**
15076 * Undoes the locks made by by #lockMedia().
15077 */
15078HRESULT SessionMachine::i_unlockMedia()
15079{
15080 AutoCaller autoCaller(this);
15081 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15082
15083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15084
15085 /* we may be holding important error info on the current thread;
15086 * preserve it */
15087 ErrorInfoKeeper eik;
15088
15089 HRESULT rc = mData->mSession.mLockedMedia.Clear();
15090 AssertComRC(rc);
15091 return rc;
15092}
15093
15094/**
15095 * Helper to change the machine state (reimplementation).
15096 *
15097 * @note Locks this object for writing.
15098 * @note This method must not call i_saveSettings or SaveSettings, otherwise
15099 * it can cause crashes in random places due to unexpectedly committing
15100 * the current settings. The caller is responsible for that. The call
15101 * to saveStateSettings is fine, because this method does not commit.
15102 */
15103HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
15104{
15105 LogFlowThisFuncEnter();
15106
15107 AutoCaller autoCaller(this);
15108 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15109
15110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15111
15112 MachineState_T oldMachineState = mData->mMachineState;
15113
15114 AssertMsgReturn(oldMachineState != aMachineState,
15115 ("oldMachineState=%s, aMachineState=%s\n",
15116 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
15117 E_FAIL);
15118
15119 HRESULT rc = S_OK;
15120
15121 int stsFlags = 0;
15122 bool deleteSavedState = false;
15123
15124 /* detect some state transitions */
15125
15126 if ( ( ( oldMachineState == MachineState_Saved
15127 || oldMachineState == MachineState_AbortedSaved
15128 )
15129 && aMachineState == MachineState_Restoring
15130 )
15131 || ( ( oldMachineState == MachineState_PoweredOff
15132 || oldMachineState == MachineState_Teleported
15133 || oldMachineState == MachineState_Aborted
15134 )
15135 && ( aMachineState == MachineState_TeleportingIn
15136 || aMachineState == MachineState_Starting
15137 )
15138 )
15139 )
15140 {
15141 /* The EMT thread is about to start */
15142
15143 /* Nothing to do here for now... */
15144
15145 /// @todo NEWMEDIA don't let mDVDDrive and other children
15146 /// change anything when in the Starting/Restoring state
15147 }
15148 else if ( ( oldMachineState == MachineState_Running
15149 || oldMachineState == MachineState_Paused
15150 || oldMachineState == MachineState_Teleporting
15151 || oldMachineState == MachineState_OnlineSnapshotting
15152 || oldMachineState == MachineState_LiveSnapshotting
15153 || oldMachineState == MachineState_Stuck
15154 || oldMachineState == MachineState_Starting
15155 || oldMachineState == MachineState_Stopping
15156 || oldMachineState == MachineState_Saving
15157 || oldMachineState == MachineState_Restoring
15158 || oldMachineState == MachineState_TeleportingPausedVM
15159 || oldMachineState == MachineState_TeleportingIn
15160 )
15161 && ( aMachineState == MachineState_PoweredOff
15162 || aMachineState == MachineState_Saved
15163 || aMachineState == MachineState_Teleported
15164 || aMachineState == MachineState_Aborted
15165 || aMachineState == MachineState_AbortedSaved
15166 )
15167 )
15168 {
15169 /* The EMT thread has just stopped, unlock attached media. Note that as
15170 * opposed to locking that is done from Console, we do unlocking here
15171 * because the VM process may have aborted before having a chance to
15172 * properly unlock all media it locked. */
15173
15174 unlockMedia();
15175 }
15176
15177 if (oldMachineState == MachineState_Restoring)
15178 {
15179 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
15180 {
15181 /*
15182 * delete the saved state file once the machine has finished
15183 * restoring from it (note that Console sets the state from
15184 * Restoring to AbortedSaved if the VM couldn't restore successfully,
15185 * to give the user an ability to fix an error and retry --
15186 * we keep the saved state file in this case)
15187 */
15188 deleteSavedState = true;
15189 }
15190 }
15191 else if ( oldMachineState == MachineState_Saved
15192 && ( aMachineState == MachineState_PoweredOff
15193 || aMachineState == MachineState_Teleported
15194 )
15195 )
15196 {
15197 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
15198 deleteSavedState = true;
15199 mData->mCurrentStateModified = TRUE;
15200 stsFlags |= SaveSTS_CurStateModified;
15201 }
15202 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
15203 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
15204
15205 if ( aMachineState == MachineState_Starting
15206 || aMachineState == MachineState_Restoring
15207 || aMachineState == MachineState_TeleportingIn
15208 )
15209 {
15210 /* set the current state modified flag to indicate that the current
15211 * state is no more identical to the state in the
15212 * current snapshot */
15213 if (!mData->mCurrentSnapshot.isNull())
15214 {
15215 mData->mCurrentStateModified = TRUE;
15216 stsFlags |= SaveSTS_CurStateModified;
15217 }
15218 }
15219
15220 if (deleteSavedState)
15221 {
15222 if (mRemoveSavedState)
15223 {
15224 Assert(!mSSData->strStateFilePath.isEmpty());
15225
15226 // it is safe to delete the saved state file if ...
15227 if ( !mData->mFirstSnapshot // ... we have no snapshots or
15228 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
15229 // ... none of the snapshots share the saved state file
15230 )
15231 RTFileDelete(mSSData->strStateFilePath.c_str());
15232 }
15233
15234 mSSData->strStateFilePath.setNull();
15235 stsFlags |= SaveSTS_StateFilePath;
15236 }
15237
15238 /* redirect to the underlying peer machine */
15239 mPeer->i_setMachineState(aMachineState);
15240
15241 if ( oldMachineState != MachineState_RestoringSnapshot
15242 && ( aMachineState == MachineState_PoweredOff
15243 || aMachineState == MachineState_Teleported
15244 || aMachineState == MachineState_Aborted
15245 || aMachineState == MachineState_AbortedSaved
15246 || aMachineState == MachineState_Saved))
15247 {
15248 /* the machine has stopped execution
15249 * (or the saved state file was adopted) */
15250 stsFlags |= SaveSTS_StateTimeStamp;
15251 }
15252
15253 if ( ( oldMachineState == MachineState_PoweredOff
15254 || oldMachineState == MachineState_Aborted
15255 || oldMachineState == MachineState_Teleported
15256 )
15257 && aMachineState == MachineState_Saved)
15258 {
15259 /* the saved state file was adopted */
15260 Assert(!mSSData->strStateFilePath.isEmpty());
15261 stsFlags |= SaveSTS_StateFilePath;
15262 }
15263
15264#ifdef VBOX_WITH_GUEST_PROPS
15265 if ( aMachineState == MachineState_PoweredOff
15266 || aMachineState == MachineState_Aborted
15267 || aMachineState == MachineState_Teleported)
15268 {
15269 /* Make sure any transient guest properties get removed from the
15270 * property store on shutdown. */
15271 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
15272
15273 /* remove it from the settings representation */
15274 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
15275 for (settings::GuestPropertiesList::iterator
15276 it = llGuestProperties.begin();
15277 it != llGuestProperties.end();
15278 /*nothing*/)
15279 {
15280 const settings::GuestProperty &prop = *it;
15281 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
15282 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
15283 {
15284 it = llGuestProperties.erase(it);
15285 fNeedsSaving = true;
15286 }
15287 else
15288 {
15289 ++it;
15290 }
15291 }
15292
15293 /* Additionally remove it from the HWData representation. Required to
15294 * keep everything in sync, as this is what the API keeps using. */
15295 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
15296 for (HWData::GuestPropertyMap::iterator
15297 it = llHWGuestProperties.begin();
15298 it != llHWGuestProperties.end();
15299 /*nothing*/)
15300 {
15301 uint32_t fFlags = it->second.mFlags;
15302 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
15303 {
15304 /* iterator where we need to continue after the erase call
15305 * (C++03 is a fact still, and it doesn't return the iterator
15306 * which would allow continuing) */
15307 HWData::GuestPropertyMap::iterator it2 = it;
15308 ++it2;
15309 llHWGuestProperties.erase(it);
15310 it = it2;
15311 fNeedsSaving = true;
15312 }
15313 else
15314 {
15315 ++it;
15316 }
15317 }
15318
15319 if (fNeedsSaving)
15320 {
15321 mData->mCurrentStateModified = TRUE;
15322 stsFlags |= SaveSTS_CurStateModified;
15323 }
15324 }
15325#endif /* VBOX_WITH_GUEST_PROPS */
15326
15327 rc = i_saveStateSettings(stsFlags);
15328
15329 if ( ( oldMachineState != MachineState_PoweredOff
15330 && oldMachineState != MachineState_Aborted
15331 && oldMachineState != MachineState_Teleported
15332 )
15333 && ( aMachineState == MachineState_PoweredOff
15334 || aMachineState == MachineState_Aborted
15335 || aMachineState == MachineState_Teleported
15336 )
15337 )
15338 {
15339 /* we've been shut down for any reason */
15340 /* no special action so far */
15341 }
15342
15343 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, ::stringifyMachineState(mData->mMachineState) ));
15344 LogFlowThisFuncLeave();
15345 return rc;
15346}
15347
15348/**
15349 * Sends the current machine state value to the VM process.
15350 *
15351 * @note Locks this object for reading, then calls a client process.
15352 */
15353HRESULT SessionMachine::i_updateMachineStateOnClient()
15354{
15355 AutoCaller autoCaller(this);
15356 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15357
15358 ComPtr<IInternalSessionControl> directControl;
15359 {
15360 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15361 AssertReturn(!!mData, E_FAIL);
15362 if (mData->mSession.mLockType == LockType_VM)
15363 directControl = mData->mSession.mDirectControl;
15364
15365 /* directControl may be already set to NULL here in #OnSessionEnd()
15366 * called too early by the direct session process while there is still
15367 * some operation (like deleting the snapshot) in progress. The client
15368 * process in this case is waiting inside Session::close() for the
15369 * "end session" process object to complete, while #uninit() called by
15370 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15371 * operation to complete. For now, we accept this inconsistent behavior
15372 * and simply do nothing here. */
15373
15374 if (mData->mSession.mState == SessionState_Unlocking)
15375 return S_OK;
15376 }
15377
15378 /* ignore notifications sent after #OnSessionEnd() is called */
15379 if (!directControl)
15380 return S_OK;
15381
15382 return directControl->UpdateMachineState(mData->mMachineState);
15383}
15384
15385
15386/*static*/
15387HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15388{
15389 va_list args;
15390 va_start(args, pcszMsg);
15391 HRESULT rc = setErrorInternalV(aResultCode,
15392 getStaticClassIID(),
15393 getStaticComponentName(),
15394 pcszMsg, args,
15395 false /* aWarning */,
15396 true /* aLogIt */);
15397 va_end(args);
15398 return rc;
15399}
15400
15401
15402HRESULT Machine::updateState(MachineState_T aState)
15403{
15404 NOREF(aState);
15405 ReturnComNotImplemented();
15406}
15407
15408HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15409{
15410 NOREF(aProgress);
15411 ReturnComNotImplemented();
15412}
15413
15414HRESULT Machine::endPowerUp(LONG aResult)
15415{
15416 NOREF(aResult);
15417 ReturnComNotImplemented();
15418}
15419
15420HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15421{
15422 NOREF(aProgress);
15423 ReturnComNotImplemented();
15424}
15425
15426HRESULT Machine::endPoweringDown(LONG aResult,
15427 const com::Utf8Str &aErrMsg)
15428{
15429 NOREF(aResult);
15430 NOREF(aErrMsg);
15431 ReturnComNotImplemented();
15432}
15433
15434HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15435 BOOL *aMatched,
15436 ULONG *aMaskedInterfaces)
15437{
15438 NOREF(aDevice);
15439 NOREF(aMatched);
15440 NOREF(aMaskedInterfaces);
15441 ReturnComNotImplemented();
15442
15443}
15444
15445HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15446{
15447 NOREF(aId); NOREF(aCaptureFilename);
15448 ReturnComNotImplemented();
15449}
15450
15451HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15452 BOOL aDone)
15453{
15454 NOREF(aId);
15455 NOREF(aDone);
15456 ReturnComNotImplemented();
15457}
15458
15459HRESULT Machine::autoCaptureUSBDevices()
15460{
15461 ReturnComNotImplemented();
15462}
15463
15464HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15465{
15466 NOREF(aDone);
15467 ReturnComNotImplemented();
15468}
15469
15470HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15471 ComPtr<IProgress> &aProgress)
15472{
15473 NOREF(aSession);
15474 NOREF(aProgress);
15475 ReturnComNotImplemented();
15476}
15477
15478HRESULT Machine::finishOnlineMergeMedium()
15479{
15480 ReturnComNotImplemented();
15481}
15482
15483HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15484 std::vector<com::Utf8Str> &aValues,
15485 std::vector<LONG64> &aTimestamps,
15486 std::vector<com::Utf8Str> &aFlags)
15487{
15488 NOREF(aNames);
15489 NOREF(aValues);
15490 NOREF(aTimestamps);
15491 NOREF(aFlags);
15492 ReturnComNotImplemented();
15493}
15494
15495HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15496 const com::Utf8Str &aValue,
15497 LONG64 aTimestamp,
15498 const com::Utf8Str &aFlags,
15499 BOOL fWasDeleted)
15500{
15501 NOREF(aName);
15502 NOREF(aValue);
15503 NOREF(aTimestamp);
15504 NOREF(aFlags);
15505 NOREF(fWasDeleted);
15506 ReturnComNotImplemented();
15507}
15508
15509HRESULT Machine::lockMedia()
15510{
15511 ReturnComNotImplemented();
15512}
15513
15514HRESULT Machine::unlockMedia()
15515{
15516 ReturnComNotImplemented();
15517}
15518
15519HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15520 ComPtr<IMediumAttachment> &aNewAttachment)
15521{
15522 NOREF(aAttachment);
15523 NOREF(aNewAttachment);
15524 ReturnComNotImplemented();
15525}
15526
15527HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15528 ULONG aCpuUser,
15529 ULONG aCpuKernel,
15530 ULONG aCpuIdle,
15531 ULONG aMemTotal,
15532 ULONG aMemFree,
15533 ULONG aMemBalloon,
15534 ULONG aMemShared,
15535 ULONG aMemCache,
15536 ULONG aPagedTotal,
15537 ULONG aMemAllocTotal,
15538 ULONG aMemFreeTotal,
15539 ULONG aMemBalloonTotal,
15540 ULONG aMemSharedTotal,
15541 ULONG aVmNetRx,
15542 ULONG aVmNetTx)
15543{
15544 NOREF(aValidStats);
15545 NOREF(aCpuUser);
15546 NOREF(aCpuKernel);
15547 NOREF(aCpuIdle);
15548 NOREF(aMemTotal);
15549 NOREF(aMemFree);
15550 NOREF(aMemBalloon);
15551 NOREF(aMemShared);
15552 NOREF(aMemCache);
15553 NOREF(aPagedTotal);
15554 NOREF(aMemAllocTotal);
15555 NOREF(aMemFreeTotal);
15556 NOREF(aMemBalloonTotal);
15557 NOREF(aMemSharedTotal);
15558 NOREF(aVmNetRx);
15559 NOREF(aVmNetTx);
15560 ReturnComNotImplemented();
15561}
15562
15563HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15564 com::Utf8Str &aResult)
15565{
15566 NOREF(aAuthParams);
15567 NOREF(aResult);
15568 ReturnComNotImplemented();
15569}
15570
15571com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15572{
15573 com::Utf8Str strControllerName = "Unknown";
15574 switch (aBusType)
15575 {
15576 case StorageBus_IDE:
15577 {
15578 strControllerName = "IDE";
15579 break;
15580 }
15581 case StorageBus_SATA:
15582 {
15583 strControllerName = "SATA";
15584 break;
15585 }
15586 case StorageBus_SCSI:
15587 {
15588 strControllerName = "SCSI";
15589 break;
15590 }
15591 case StorageBus_Floppy:
15592 {
15593 strControllerName = "Floppy";
15594 break;
15595 }
15596 case StorageBus_SAS:
15597 {
15598 strControllerName = "SAS";
15599 break;
15600 }
15601 case StorageBus_USB:
15602 {
15603 strControllerName = "USB";
15604 break;
15605 }
15606 default:
15607 break;
15608 }
15609 return strControllerName;
15610}
15611
15612HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15613{
15614 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15615
15616 AutoCaller autoCaller(this);
15617 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15618
15619 HRESULT rc = S_OK;
15620
15621 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15622 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15623 rc = getUSBDeviceFilters(usbDeviceFilters);
15624 if (FAILED(rc)) return rc;
15625
15626 NOREF(aFlags);
15627 com::Utf8Str osTypeId;
15628 ComObjPtr<GuestOSType> osType = NULL;
15629
15630 /* Get the guest os type as a string from the VB. */
15631 rc = getOSTypeId(osTypeId);
15632 if (FAILED(rc)) return rc;
15633
15634 /* Get the os type obj that coresponds, can be used to get
15635 * the defaults for this guest OS. */
15636 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15637 if (FAILED(rc)) return rc;
15638
15639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15640
15641 /* Let the OS type select 64-bit ness. */
15642 mHWData->mLongMode = osType->i_is64Bit()
15643 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15644
15645 /* Let the OS type enable the X2APIC */
15646 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15647
15648 /* This one covers IOAPICEnabled. */
15649 mBIOSSettings->i_applyDefaults(osType);
15650
15651 /* Initialize default record settings. */
15652 mRecordingSettings->i_applyDefaults();
15653
15654 /* Initialize default BIOS settings here */
15655 /* Hardware virtualization must be ON by default */
15656 mHWData->mAPIC = true;
15657 mHWData->mHWVirtExEnabled = true;
15658
15659 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15660 if (FAILED(rc)) return rc;
15661
15662 rc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15663 if (FAILED(rc)) return rc;
15664
15665 /* Graphics stuff. */
15666 GraphicsControllerType_T graphicsController;
15667 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15668 if (FAILED(rc)) return rc;
15669
15670 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15671 if (FAILED(rc)) return rc;
15672
15673 ULONG vramSize;
15674 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15675 if (FAILED(rc)) return rc;
15676
15677 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15678 if (FAILED(rc)) return rc;
15679
15680 BOOL fAccelerate2DVideoEnabled;
15681 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15682 if (FAILED(rc)) return rc;
15683
15684 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15685 if (FAILED(rc)) return rc;
15686
15687 BOOL fAccelerate3DEnabled;
15688 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15689 if (FAILED(rc)) return rc;
15690
15691 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15692 if (FAILED(rc)) return rc;
15693
15694 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15695 if (FAILED(rc)) return rc;
15696
15697 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15698 if (FAILED(rc)) return rc;
15699
15700 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15701 if (FAILED(rc)) return rc;
15702
15703 BOOL mRTCUseUTC;
15704 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15705 if (FAILED(rc)) return rc;
15706
15707 setRTCUseUTC(mRTCUseUTC);
15708 if (FAILED(rc)) return rc;
15709
15710 /* the setter does more than just the assignment, so use it */
15711 ChipsetType_T enmChipsetType;
15712 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15713 if (FAILED(rc)) return rc;
15714
15715 rc = COMSETTER(ChipsetType)(enmChipsetType);
15716 if (FAILED(rc)) return rc;
15717
15718 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15719 if (FAILED(rc)) return rc;
15720
15721 /* Apply IOMMU defaults. */
15722 IommuType_T enmIommuType;
15723 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15724 if (FAILED(rc)) return rc;
15725
15726 rc = COMSETTER(IommuType)(enmIommuType);
15727 if (FAILED(rc)) return rc;
15728
15729 /* Apply network adapters defaults */
15730 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15731 mNetworkAdapters[slot]->i_applyDefaults(osType);
15732
15733 /* Apply serial port defaults */
15734 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15735 mSerialPorts[slot]->i_applyDefaults(osType);
15736
15737 /* Apply parallel port defaults - not OS dependent*/
15738 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15739 mParallelPorts[slot]->i_applyDefaults();
15740
15741 /* This one covers the TPM type. */
15742 mTrustedPlatformModule->i_applyDefaults(osType);
15743
15744 /* This one covers secure boot. */
15745 rc = mNvramStore->i_applyDefaults(osType);
15746 if (FAILED(rc)) return rc;
15747
15748 /* Audio stuff. */
15749 AudioControllerType_T audioController;
15750 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15751 if (FAILED(rc)) return rc;
15752
15753 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15754 if (FAILED(rc)) return rc;
15755
15756 AudioCodecType_T audioCodec;
15757 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15758 if (FAILED(rc)) return rc;
15759
15760 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15761 if (FAILED(rc)) return rc;
15762
15763 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15764 if (FAILED(rc)) return rc;
15765
15766 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15767 if (FAILED(rc)) return rc;
15768
15769 /* Storage Controllers */
15770 StorageControllerType_T hdStorageControllerType;
15771 StorageBus_T hdStorageBusType;
15772 StorageControllerType_T dvdStorageControllerType;
15773 StorageBus_T dvdStorageBusType;
15774 BOOL recommendedFloppy;
15775 ComPtr<IStorageController> floppyController;
15776 ComPtr<IStorageController> hdController;
15777 ComPtr<IStorageController> dvdController;
15778 Utf8Str strFloppyName, strDVDName, strHDName;
15779
15780 /* GUI auto generates controller names using bus type. Do the same*/
15781 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15782
15783 /* Floppy recommended? add one. */
15784 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15785 if (FAILED(rc)) return rc;
15786 if (recommendedFloppy)
15787 {
15788 rc = addStorageController(strFloppyName,
15789 StorageBus_Floppy,
15790 floppyController);
15791 if (FAILED(rc)) return rc;
15792 }
15793
15794 /* Setup one DVD storage controller. */
15795 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15796 if (FAILED(rc)) return rc;
15797
15798 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15799 if (FAILED(rc)) return rc;
15800
15801 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15802
15803 rc = addStorageController(strDVDName,
15804 dvdStorageBusType,
15805 dvdController);
15806 if (FAILED(rc)) return rc;
15807
15808 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15809 if (FAILED(rc)) return rc;
15810
15811 /* Setup one HDD storage controller. */
15812 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15813 if (FAILED(rc)) return rc;
15814
15815 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15816 if (FAILED(rc)) return rc;
15817
15818 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15819
15820 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15821 {
15822 rc = addStorageController(strHDName,
15823 hdStorageBusType,
15824 hdController);
15825 if (FAILED(rc)) return rc;
15826
15827 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15828 if (FAILED(rc)) return rc;
15829 }
15830 else
15831 {
15832 /* The HD controller is the same as DVD: */
15833 hdController = dvdController;
15834 }
15835
15836 /* Limit the AHCI port count if it's used because windows has trouble with
15837 * too many ports and other guest (OS X in particular) may take extra long
15838 * boot: */
15839
15840 // pParent = static_cast<Medium*>(aP)
15841 IStorageController *temp = hdController;
15842 ComObjPtr<StorageController> storageController;
15843 storageController = static_cast<StorageController *>(temp);
15844
15845 // tempHDController = aHDController;
15846 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15847 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15848 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15849 storageController->COMSETTER(PortCount)(1);
15850
15851 /* USB stuff */
15852
15853 bool ohciEnabled = false;
15854
15855 ComPtr<IUSBController> usbController;
15856 BOOL recommendedUSB3;
15857 BOOL recommendedUSB;
15858 BOOL usbProxyAvailable;
15859
15860 getUSBProxyAvailable(&usbProxyAvailable);
15861 if (FAILED(rc)) return rc;
15862
15863 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15864 if (FAILED(rc)) return rc;
15865 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15866 if (FAILED(rc)) return rc;
15867
15868 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15869 {
15870#ifdef VBOX_WITH_EXTPACK
15871 /* USB 3.0 is only available if the proper ExtPack is installed. */
15872 ExtPackManager *aManager = mParent->i_getExtPackManager();
15873 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15874 {
15875 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15876 if (FAILED(rc)) return rc;
15877
15878 /* xHci includes OHCI */
15879 ohciEnabled = true;
15880 }
15881#endif
15882 }
15883 if ( !ohciEnabled
15884 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15885 {
15886 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15887 if (FAILED(rc)) return rc;
15888 ohciEnabled = true;
15889
15890#ifdef VBOX_WITH_EXTPACK
15891 /* USB 2.0 is only available if the proper ExtPack is installed.
15892 * Note. Configuring EHCI here and providing messages about
15893 * the missing extpack isn't exactly clean, but it is a
15894 * necessary evil to patch over legacy compatability issues
15895 * introduced by the new distribution model. */
15896 ExtPackManager *manager = mParent->i_getExtPackManager();
15897 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15898 {
15899 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15900 if (FAILED(rc)) return rc;
15901 }
15902#endif
15903 }
15904
15905 /* Set recommended human interface device types: */
15906 BOOL recommendedUSBHID;
15907 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15908 if (FAILED(rc)) return rc;
15909
15910 if (recommendedUSBHID)
15911 {
15912 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15913 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15914 if (!ohciEnabled && !usbDeviceFilters.isNull())
15915 {
15916 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15917 if (FAILED(rc)) return rc;
15918 }
15919 }
15920
15921 BOOL recommendedUSBTablet;
15922 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15923 if (FAILED(rc)) return rc;
15924
15925 if (recommendedUSBTablet)
15926 {
15927 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15928 if (!ohciEnabled && !usbDeviceFilters.isNull())
15929 {
15930 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15931 if (FAILED(rc)) return rc;
15932 }
15933 }
15934
15935 /* Enable the VMMDev testing feature for bootsector VMs: */
15936 if (osTypeId == "VBoxBS_64")
15937 {
15938 rc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
15939 if (FAILED(rc))
15940 return rc;
15941 }
15942
15943 return S_OK;
15944}
15945
15946#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
15947/**
15948 * Task record for change encryption settins.
15949 */
15950class Machine::ChangeEncryptionTask
15951 : public Machine::Task
15952{
15953public:
15954 ChangeEncryptionTask(Machine *m,
15955 Progress *p,
15956 const Utf8Str &t,
15957 const com::Utf8Str &aCurrentPassword,
15958 const com::Utf8Str &aCipher,
15959 const com::Utf8Str &aNewPassword,
15960 const com::Utf8Str &aNewPasswordId,
15961 const BOOL aForce,
15962 const MediaList &llMedia)
15963 : Task(m, p, t),
15964 mstrNewPassword(aNewPassword),
15965 mstrCurrentPassword(aCurrentPassword),
15966 mstrCipher(aCipher),
15967 mstrNewPasswordId(aNewPasswordId),
15968 mForce(aForce),
15969 mllMedia(llMedia)
15970 {}
15971
15972 ~ChangeEncryptionTask()
15973 {
15974 if (mstrNewPassword.length())
15975 RTMemWipeThoroughly(mstrNewPassword.mutableRaw(), mstrNewPassword.length(), 10 /* cPasses */);
15976 if (mstrCurrentPassword.length())
15977 RTMemWipeThoroughly(mstrCurrentPassword.mutableRaw(), mstrCurrentPassword.length(), 10 /* cPasses */);
15978 if (m_pCryptoIf)
15979 {
15980 m_pMachine->i_getVirtualBox()->i_releaseCryptoIf(m_pCryptoIf);
15981 m_pCryptoIf = NULL;
15982 }
15983 }
15984
15985 Utf8Str mstrNewPassword;
15986 Utf8Str mstrCurrentPassword;
15987 Utf8Str mstrCipher;
15988 Utf8Str mstrNewPasswordId;
15989 BOOL mForce;
15990 MediaList mllMedia;
15991 PCVBOXCRYPTOIF m_pCryptoIf;
15992private:
15993 void handler()
15994 {
15995 try
15996 {
15997 m_pMachine->i_changeEncryptionHandler(*this);
15998 }
15999 catch (...)
16000 {
16001 LogRel(("Some exception in the function Machine::i_changeEncryptionHandler()\n"));
16002 }
16003 }
16004
16005 friend void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task);
16006};
16007
16008/**
16009 * Scans specified directory and fills list by files found
16010 *
16011 * @returns VBox status code.
16012 * @param lstFiles
16013 * @param strDir
16014 * @param filePattern
16015 */
16016int Machine::i_findFiles(std::list<com::Utf8Str> &lstFiles, const com::Utf8Str &strDir,
16017 const com::Utf8Str &strPattern)
16018{
16019 /* To get all entries including subdirectories. */
16020 char *pszFilePattern = RTPathJoinA(strDir.c_str(), "*");
16021 if (!pszFilePattern)
16022 return VERR_NO_STR_MEMORY;
16023
16024 PRTDIRENTRYEX pDirEntry = NULL;
16025 RTDIR hDir;
16026 size_t cbDirEntry = sizeof(RTDIRENTRYEX);
16027 int rc = RTDirOpenFiltered(&hDir, pszFilePattern, RTDIRFILTER_WINNT, 0 /*fFlags*/);
16028 if (RT_SUCCESS(rc))
16029 {
16030 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
16031 if (pDirEntry)
16032 {
16033 while ( (rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK))
16034 != VERR_NO_MORE_FILES)
16035 {
16036 char *pszFilePath = NULL;
16037
16038 if (rc == VERR_BUFFER_OVERFLOW)
16039 {
16040 /* allocate new buffer. */
16041 RTMemFree(pDirEntry);
16042 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbDirEntry);
16043 if (!pDirEntry)
16044 {
16045 rc = VERR_NO_MEMORY;
16046 break;
16047 }
16048 /* Retry. */
16049 rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
16050 if (RT_FAILURE(rc))
16051 break;
16052 }
16053 else if (RT_FAILURE(rc))
16054 break;
16055
16056 /* Exclude . and .. */
16057 if ( (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '\0')
16058 || (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '.' && pDirEntry->szName[2] == '\0'))
16059 continue;
16060 if (RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode))
16061 {
16062 char *pszSubDirPath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
16063 if (!pszSubDirPath)
16064 {
16065 rc = VERR_NO_STR_MEMORY;
16066 break;
16067 }
16068 rc = i_findFiles(lstFiles, pszSubDirPath, strPattern);
16069 RTMemFree(pszSubDirPath);
16070 if (RT_FAILURE(rc))
16071 break;
16072 continue;
16073 }
16074
16075 /* We got the new entry. */
16076 if (!RTFS_IS_FILE(pDirEntry->Info.Attr.fMode))
16077 continue;
16078
16079 if (!RTStrSimplePatternMatch(strPattern.c_str(), pDirEntry->szName))
16080 continue;
16081
16082 /* Prepend the path to the libraries. */
16083 pszFilePath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
16084 if (!pszFilePath)
16085 {
16086 rc = VERR_NO_STR_MEMORY;
16087 break;
16088 }
16089
16090 lstFiles.push_back(pszFilePath);
16091 RTStrFree(pszFilePath);
16092 }
16093
16094 RTMemFree(pDirEntry);
16095 }
16096 else
16097 rc = VERR_NO_MEMORY;
16098
16099 RTDirClose(hDir);
16100 }
16101 else
16102 {
16103 /* On Windows the above immediately signals that there are no
16104 * files matching, while on other platforms enumerating the
16105 * files below fails. Either way: stop searching. */
16106 }
16107
16108 if ( rc == VERR_NO_MORE_FILES
16109 || rc == VERR_FILE_NOT_FOUND
16110 || rc == VERR_PATH_NOT_FOUND)
16111 rc = VINF_SUCCESS;
16112 RTStrFree(pszFilePattern);
16113 return rc;
16114}
16115
16116/**
16117 * Helper to set up an I/O stream to read or write a possibly encrypted file.
16118 *
16119 * @returns VBox status code.
16120 * @param pszFilename The file to open.
16121 * @param pCryptoIf Pointer to the cryptographic interface if the file should be encrypted or contains encrypted data.
16122 * @param pszKeyStore The keystore if the file should be encrypted or contains encrypted data.
16123 * @param pszPassword The password if the file should be encrypted or contains encrypted data.
16124 * @param fOpen The open flags for the file.
16125 * @param phVfsIos Where to store the handle to the I/O stream on success.
16126 */
16127int Machine::i_createIoStreamForFile(const char *pszFilename, PCVBOXCRYPTOIF pCryptoIf,
16128 const char *pszKeyStore, const char *pszPassword,
16129 uint64_t fOpen, PRTVFSIOSTREAM phVfsIos)
16130{
16131 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
16132 int vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
16133 if (RT_SUCCESS(vrc))
16134 {
16135 if (pCryptoIf)
16136 {
16137 RTVFSFILE hVfsFileCrypto = NIL_RTVFSFILE;
16138 vrc = pCryptoIf->pfnCryptoFileFromVfsFile(hVfsFile, pszKeyStore, pszPassword, &hVfsFileCrypto);
16139 if (RT_SUCCESS(vrc))
16140 {
16141 RTVfsFileRelease(hVfsFile);
16142 hVfsFile = hVfsFileCrypto;
16143 }
16144 }
16145
16146 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
16147 RTVfsFileRelease(hVfsFile);
16148 }
16149
16150 return vrc;
16151}
16152
16153/**
16154 * Helper function processing all actions for one component (saved state files,
16155 * NVRAM files, etc). Used by Machine::i_changeEncryptionHandler only.
16156 *
16157 * @param task
16158 * @param strDirectory
16159 * @param strFilePattern
16160 * @param strMagic
16161 * @param strKeyStore
16162 * @param strKeyId
16163 * @return
16164 */
16165HRESULT Machine::i_changeEncryptionForComponent(ChangeEncryptionTask &task, const com::Utf8Str strDirectory,
16166 const com::Utf8Str strFilePattern, com::Utf8Str &strKeyStore,
16167 com::Utf8Str &strKeyId, int iCipherMode)
16168{
16169 bool fDecrypt = task.mstrCurrentPassword.isNotEmpty()
16170 && task.mstrCipher.isEmpty()
16171 && task.mstrNewPassword.isEmpty()
16172 && task.mstrNewPasswordId.isEmpty();
16173 bool fEncrypt = task.mstrCurrentPassword.isEmpty()
16174 && task.mstrCipher.isNotEmpty()
16175 && task.mstrNewPassword.isNotEmpty()
16176 && task.mstrNewPasswordId.isNotEmpty();
16177
16178 /* check if the cipher is changed which causes the reencryption*/
16179
16180 const char *pszTaskCipher = NULL;
16181 if (task.mstrCipher.isNotEmpty())
16182 pszTaskCipher = getCipherString(task.mstrCipher.c_str(), iCipherMode);
16183
16184 if (!task.mForce && !fDecrypt && !fEncrypt)
16185 {
16186 char *pszCipher = NULL;
16187 int vrc = task.m_pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(strKeyStore.c_str(),
16188 NULL /*pszPassword*/,
16189 NULL /*ppbKey*/,
16190 NULL /*pcbKey*/,
16191 &pszCipher);
16192 if (RT_SUCCESS(vrc))
16193 {
16194 task.mForce = strcmp(pszTaskCipher, pszCipher) != 0;
16195 RTMemFree(pszCipher);
16196 }
16197 else
16198 return setErrorBoth(E_FAIL, vrc, tr("Obtain cipher for '%s' files failed (%Rrc)"),
16199 strFilePattern.c_str(), vrc);
16200 }
16201
16202 /* Only the password needs to be changed */
16203 if (!task.mForce && !fDecrypt && !fEncrypt)
16204 {
16205 Assert(task.m_pCryptoIf);
16206
16207 VBOXCRYPTOCTX hCryptoCtx;
16208 int vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(strKeyStore.c_str(), task.mstrCurrentPassword.c_str(), &hCryptoCtx);
16209 if (RT_FAILURE(vrc))
16210 return setErrorBoth(E_FAIL, vrc, tr("Loading old key store for '%s' files failed, (%Rrc)"),
16211 strFilePattern.c_str(), vrc);
16212 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
16213 if (RT_FAILURE(vrc))
16214 return setErrorBoth(E_FAIL, vrc, tr("Changing the password for '%s' files failed, (%Rrc)"),
16215 strFilePattern.c_str(), vrc);
16216
16217 char *pszKeyStore = NULL;
16218 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16219 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16220 if (RT_FAILURE(vrc))
16221 return setErrorBoth(E_FAIL, vrc, tr("Saving the key store for '%s' files failed, (%Rrc)"),
16222 strFilePattern.c_str(), vrc);
16223 strKeyStore = pszKeyStore;
16224 RTMemFree(pszKeyStore);
16225 strKeyId = task.mstrNewPasswordId;
16226 return S_OK;
16227 }
16228
16229 /* Reencryption required */
16230 HRESULT rc = S_OK;
16231 int vrc = VINF_SUCCESS;
16232
16233 std::list<com::Utf8Str> lstFiles;
16234 if (SUCCEEDED(rc))
16235 {
16236 vrc = i_findFiles(lstFiles, strDirectory, strFilePattern);
16237 if (RT_FAILURE(vrc))
16238 rc = setErrorBoth(E_FAIL, vrc, tr("Getting file list for '%s' files failed, (%Rrc)"),
16239 strFilePattern.c_str(), vrc);
16240 }
16241 com::Utf8Str strNewKeyStore;
16242 if (SUCCEEDED(rc))
16243 {
16244 if (!fDecrypt)
16245 {
16246 VBOXCRYPTOCTX hCryptoCtx;
16247 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(pszTaskCipher, task.mstrNewPassword.c_str(), &hCryptoCtx);
16248 if (RT_FAILURE(vrc))
16249 return setErrorBoth(E_FAIL, vrc, tr("Create new key store for '%s' files failed, (%Rrc)"),
16250 strFilePattern.c_str(), vrc);
16251
16252 char *pszKeyStore = NULL;
16253 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16254 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16255 if (RT_FAILURE(vrc))
16256 return setErrorBoth(E_FAIL, vrc, tr("Saving the new key store for '%s' files failed, (%Rrc)"),
16257 strFilePattern.c_str(), vrc);
16258 strNewKeyStore = pszKeyStore;
16259 RTMemFree(pszKeyStore);
16260 }
16261
16262 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
16263 it != lstFiles.end();
16264 ++it)
16265 {
16266 RTVFSIOSTREAM hVfsIosOld = NIL_RTVFSIOSTREAM;
16267 RTVFSIOSTREAM hVfsIosNew = NIL_RTVFSIOSTREAM;
16268
16269 uint64_t fOpenForRead = RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE;
16270 uint64_t fOpenForWrite = RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE;
16271
16272 vrc = i_createIoStreamForFile((*it).c_str(),
16273 fEncrypt ? NULL : task.m_pCryptoIf,
16274 fEncrypt ? NULL : strKeyStore.c_str(),
16275 fEncrypt ? NULL : task.mstrCurrentPassword.c_str(),
16276 fOpenForRead, &hVfsIosOld);
16277 if (RT_SUCCESS(vrc))
16278 {
16279 vrc = i_createIoStreamForFile((*it + ".tmp").c_str(),
16280 fDecrypt ? NULL : task.m_pCryptoIf,
16281 fDecrypt ? NULL : strNewKeyStore.c_str(),
16282 fDecrypt ? NULL : task.mstrNewPassword.c_str(),
16283 fOpenForWrite, &hVfsIosNew);
16284 if (RT_FAILURE(vrc))
16285 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
16286 (*it + ".tmp").c_str(), vrc);
16287 }
16288 else
16289 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
16290 (*it).c_str(), vrc);
16291
16292 if (RT_SUCCESS(vrc))
16293 {
16294 vrc = RTVfsUtilPumpIoStreams(hVfsIosOld, hVfsIosNew, BUF_DATA_SIZE);
16295 if (RT_FAILURE(vrc))
16296 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Changing encryption of the file '%s' failed with %Rrc"),
16297 (*it).c_str(), vrc);
16298 }
16299
16300 if (hVfsIosOld != NIL_RTVFSIOSTREAM)
16301 RTVfsIoStrmRelease(hVfsIosOld);
16302 if (hVfsIosNew != NIL_RTVFSIOSTREAM)
16303 RTVfsIoStrmRelease(hVfsIosNew);
16304 }
16305 }
16306
16307 if (SUCCEEDED(rc))
16308 {
16309 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
16310 it != lstFiles.end();
16311 ++it)
16312 {
16313 vrc = RTFileRename((*it + ".tmp").c_str(), (*it).c_str(), RTPATHRENAME_FLAGS_REPLACE);
16314 if (RT_FAILURE(vrc))
16315 {
16316 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming the file '%s' failed, (%Rrc)"),
16317 (*it + ".tmp").c_str(), vrc);
16318 break;
16319 }
16320 }
16321 }
16322
16323 if (SUCCEEDED(rc))
16324 {
16325 strKeyStore = strNewKeyStore;
16326 strKeyId = task.mstrNewPasswordId;
16327 }
16328
16329 return rc;
16330}
16331
16332/**
16333 * Task thread implementation for Machine::changeEncryption(), called from
16334 * Machine::taskHandler().
16335 *
16336 * @note Locks this object for writing.
16337 *
16338 * @param task
16339 * @return
16340 */
16341void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task)
16342{
16343 LogFlowThisFuncEnter();
16344
16345 AutoCaller autoCaller(this);
16346 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
16347 if (FAILED(autoCaller.rc()))
16348 {
16349 /* we might have been uninitialized because the session was accidentally
16350 * closed by the client, so don't assert */
16351 HRESULT rc = setError(E_FAIL,
16352 tr("The session has been accidentally closed"));
16353 task.m_pProgress->i_notifyComplete(rc);
16354 LogFlowThisFuncLeave();
16355 return;
16356 }
16357
16358 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16359
16360 HRESULT rc = S_OK;
16361 com::Utf8Str strOldKeyId = mData->mstrKeyId;
16362 com::Utf8Str strOldKeyStore = mData->mstrKeyStore;
16363 try
16364 {
16365 rc = this->i_getVirtualBox()->i_retainCryptoIf(&task.m_pCryptoIf);
16366 if (FAILED(rc))
16367 throw rc;
16368
16369 if (task.mstrCurrentPassword.isEmpty())
16370 {
16371 if (mData->mstrKeyStore.isNotEmpty())
16372 throw setError(VBOX_E_PASSWORD_INCORRECT,
16373 tr("The password given for the encrypted VM is incorrect"));
16374 }
16375 else
16376 {
16377 if (mData->mstrKeyStore.isEmpty())
16378 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16379 tr("The VM is not configured for encryption"));
16380 rc = checkEncryptionPassword(task.mstrCurrentPassword);
16381 if (rc == VBOX_E_PASSWORD_INCORRECT)
16382 throw setError(VBOX_E_PASSWORD_INCORRECT,
16383 tr("The password to decrypt the VM is incorrect"));
16384 }
16385
16386 if (task.mstrCipher.isNotEmpty())
16387 {
16388 if ( task.mstrNewPassword.isEmpty()
16389 && task.mstrNewPasswordId.isEmpty()
16390 && task.mstrCurrentPassword.isNotEmpty())
16391 {
16392 /* An empty password and password ID will default to the current password. */
16393 task.mstrNewPassword = task.mstrCurrentPassword;
16394 }
16395 else if (task.mstrNewPassword.isEmpty())
16396 throw setError(VBOX_E_OBJECT_NOT_FOUND,
16397 tr("A password must be given for the VM encryption"));
16398 else if (task.mstrNewPasswordId.isEmpty())
16399 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16400 tr("A valid identifier for the password must be given"));
16401 }
16402 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
16403 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16404 tr("The password and password identifier must be empty if the output should be unencrypted"));
16405
16406 /*
16407 * Save config.
16408 * Must be first operation to prevent making encrypted copies
16409 * for old version of the config file.
16410 */
16411 int fSave = Machine::SaveS_Force;
16412 if (task.mstrNewPassword.isNotEmpty())
16413 {
16414 VBOXCRYPTOCTX hCryptoCtx;
16415
16416 int vrc = VINF_SUCCESS;
16417 if (task.mForce || task.mstrCurrentPassword.isEmpty() || task.mstrCipher.isNotEmpty())
16418 {
16419 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(getCipherString(task.mstrCipher.c_str(), CipherModeGcm),
16420 task.mstrNewPassword.c_str(), &hCryptoCtx);
16421 if (RT_FAILURE(vrc))
16422 throw setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
16423 }
16424 else
16425 {
16426 vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(mData->mstrKeyStore.c_str(),
16427 task.mstrCurrentPassword.c_str(),
16428 &hCryptoCtx);
16429 if (RT_FAILURE(vrc))
16430 throw setErrorBoth(E_FAIL, vrc, tr("Loading old key store failed, (%Rrc)"), vrc);
16431 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
16432 if (RT_FAILURE(vrc))
16433 throw setErrorBoth(E_FAIL, vrc, tr("Changing the password failed, (%Rrc)"), vrc);
16434 }
16435
16436 char *pszKeyStore;
16437 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16438 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16439 if (RT_FAILURE(vrc))
16440 throw setErrorBoth(E_FAIL, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
16441 mData->mstrKeyStore = pszKeyStore;
16442 RTStrFree(pszKeyStore);
16443 mData->mstrKeyId = task.mstrNewPasswordId;
16444 size_t cbPassword = task.mstrNewPassword.length() + 1;
16445 uint8_t *pbPassword = (uint8_t *)task.mstrNewPassword.c_str();
16446 mData->mpKeyStore->deleteSecretKey(task.mstrNewPasswordId);
16447 mData->mpKeyStore->addSecretKey(task.mstrNewPasswordId, pbPassword, cbPassword);
16448 mNvramStore->i_addPassword(task.mstrNewPasswordId, task.mstrNewPassword);
16449
16450 /*
16451 * Remove backuped config after saving because it can contain
16452 * unencrypted version of the config
16453 */
16454 fSave |= Machine::SaveS_RemoveBackup;
16455 }
16456 else
16457 {
16458 mData->mstrKeyId.setNull();
16459 mData->mstrKeyStore.setNull();
16460 }
16461
16462 Bstr bstrCurrentPassword(task.mstrCurrentPassword);
16463 Bstr bstrCipher(getCipherString(task.mstrCipher.c_str(), CipherModeXts));
16464 Bstr bstrNewPassword(task.mstrNewPassword);
16465 Bstr bstrNewPasswordId(task.mstrNewPasswordId);
16466 /* encrypt mediums */
16467 alock.release();
16468 for (MediaList::iterator it = task.mllMedia.begin();
16469 it != task.mllMedia.end();
16470 ++it)
16471 {
16472 ComPtr<IProgress> pProgress1;
16473 HRESULT hrc = (*it)->ChangeEncryption(bstrCurrentPassword.raw(), bstrCipher.raw(),
16474 bstrNewPassword.raw(), bstrNewPasswordId.raw(),
16475 pProgress1.asOutParam());
16476 if (FAILED(hrc)) throw hrc;
16477 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress1, 0 /* indefinite wait */);
16478 if (FAILED(hrc)) throw hrc;
16479 }
16480 alock.acquire();
16481
16482 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the SAV files")).raw(), 1);
16483
16484 Utf8Str strFullSnapshotFolder;
16485 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
16486
16487 /* .sav files (main and snapshots) */
16488 rc = i_changeEncryptionForComponent(task, strFullSnapshotFolder, "*.sav",
16489 mSSData->strStateKeyStore, mSSData->strStateKeyId, CipherModeGcm);
16490 if (FAILED(rc))
16491 /* the helper function already sets error object */
16492 throw rc;
16493
16494 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the NVRAM files")).raw(), 1);
16495
16496 /* .nvram files */
16497 com::Utf8Str strNVRAMKeyId;
16498 com::Utf8Str strNVRAMKeyStore;
16499 rc = mNvramStore->i_getEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16500 if (FAILED(rc))
16501 throw setError(rc, tr("Getting NVRAM encryption settings failed (%Rhrc)"), rc);
16502
16503 Utf8Str strMachineFolder;
16504 i_calculateFullPath(".", strMachineFolder);
16505
16506 rc = i_changeEncryptionForComponent(task, strMachineFolder, "*.nvram",
16507 strNVRAMKeyStore, strNVRAMKeyId, CipherModeGcm);
16508 if (FAILED(rc))
16509 /* the helper function already sets error object */
16510 throw rc;
16511
16512 rc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16513 if (FAILED(rc))
16514 throw setError(rc, tr("Setting NVRAM encryption settings failed (%Rhrc)"), rc);
16515
16516 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of log files")).raw(), 1);
16517
16518 /* .log files */
16519 com::Utf8Str strLogFolder;
16520 i_getLogFolder(strLogFolder);
16521 rc = i_changeEncryptionForComponent(task, strLogFolder, "VBox.log*",
16522 mData->mstrLogKeyStore, mData->mstrLogKeyId, CipherModeCtr);
16523 if (FAILED(rc))
16524 /* the helper function already sets error object */
16525 throw rc;
16526
16527 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the config file")).raw(), 1);
16528
16529 i_saveSettings(NULL, alock, fSave);
16530 }
16531 catch (HRESULT aRC)
16532 {
16533 rc = aRC;
16534 mData->mstrKeyId = strOldKeyId;
16535 mData->mstrKeyStore = strOldKeyStore;
16536 }
16537
16538 task.m_pProgress->i_notifyComplete(rc);
16539
16540 LogFlowThisFuncLeave();
16541}
16542#endif /*!VBOX_WITH_FULL_VM_ENCRYPTION*/
16543
16544HRESULT Machine::changeEncryption(const com::Utf8Str &aCurrentPassword,
16545 const com::Utf8Str &aCipher,
16546 const com::Utf8Str &aNewPassword,
16547 const com::Utf8Str &aNewPasswordId,
16548 BOOL aForce,
16549 ComPtr<IProgress> &aProgress)
16550{
16551 LogFlowFuncEnter();
16552
16553#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16554 RT_NOREF(aCurrentPassword, aCipher, aNewPassword, aNewPasswordId, aForce, aProgress);
16555 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16556#else
16557 /* make the VM accessible */
16558 if (!mData->mAccessible)
16559 {
16560 if ( aCurrentPassword.isEmpty()
16561 || mData->mstrKeyId.isEmpty())
16562 return setError(E_ACCESSDENIED, tr("Machine is inaccessible"));
16563
16564 HRESULT rc = addEncryptionPassword(mData->mstrKeyId, aCurrentPassword);
16565 if (FAILED(rc))
16566 return rc;
16567 }
16568
16569 AutoLimitedCaller autoCaller(this);
16570 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16571
16572 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16573
16574 /* define mediums to be change encryption */
16575
16576 MediaList llMedia;
16577 for (MediumAttachmentList::iterator
16578 it = mMediumAttachments->begin();
16579 it != mMediumAttachments->end();
16580 ++it)
16581 {
16582 ComObjPtr<MediumAttachment> &pAttach = *it;
16583 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
16584
16585 if (!pMedium.isNull())
16586 {
16587 AutoCaller mac(pMedium);
16588 if (FAILED(mac.rc())) return mac.rc();
16589 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
16590 DeviceType_T devType = pMedium->i_getDeviceType();
16591 if (devType == DeviceType_HardDisk)
16592 {
16593 /*
16594 * We need to move to last child because the Medium::changeEncryption
16595 * encrypts all chain of specified medium with its parents.
16596 * Also we perform cheking of back reference and children for
16597 * all media in the chain to raise error before we start any action.
16598 * So, we first move into root parent and then we will move to last child
16599 * keeping latter in the list for encryption.
16600 */
16601
16602 /* move to root parent */
16603 ComObjPtr<Medium> pTmpMedium = pMedium;
16604 while (pTmpMedium.isNotNull())
16605 {
16606 AutoCaller mediumAC(pTmpMedium);
16607 if (FAILED(mediumAC.rc())) return mac.rc();
16608 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16609
16610 /* Cannot encrypt media which are attached to more than one virtual machine. */
16611 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16612 if (cBackRefs > 1)
16613 return setError(VBOX_E_INVALID_OBJECT_STATE,
16614 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16615 pTmpMedium->i_getName().c_str(), cBackRefs);
16616
16617 size_t cChildren = pTmpMedium->i_getChildren().size();
16618 if (cChildren > 1)
16619 return setError(VBOX_E_INVALID_OBJECT_STATE,
16620 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16621 pTmpMedium->i_getName().c_str(), cChildren);
16622
16623 pTmpMedium = pTmpMedium->i_getParent();
16624 }
16625 /* move to last child */
16626 pTmpMedium = pMedium;
16627 while (pTmpMedium.isNotNull() && pTmpMedium->i_getChildren().size() != 0)
16628 {
16629 AutoCaller mediumAC(pTmpMedium);
16630 if (FAILED(mediumAC.rc())) return mac.rc();
16631 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16632
16633 /* Cannot encrypt media which are attached to more than one virtual machine. */
16634 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16635 if (cBackRefs > 1)
16636 return setError(VBOX_E_INVALID_OBJECT_STATE,
16637 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16638 pTmpMedium->i_getName().c_str(), cBackRefs);
16639
16640 size_t cChildren = pTmpMedium->i_getChildren().size();
16641 if (cChildren > 1)
16642 return setError(VBOX_E_INVALID_OBJECT_STATE,
16643 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16644 pTmpMedium->i_getName().c_str(), cChildren);
16645
16646 pTmpMedium = pTmpMedium->i_getChildren().front();
16647 }
16648 llMedia.push_back(pTmpMedium);
16649 }
16650 }
16651 }
16652
16653 ComObjPtr<Progress> pProgress;
16654 pProgress.createObject();
16655 HRESULT rc = pProgress->init(i_getVirtualBox(),
16656 static_cast<IMachine*>(this) /* aInitiator */,
16657 tr("Change encryption"),
16658 TRUE /* fCancellable */,
16659 (ULONG)(4 + + llMedia.size()), // cOperations
16660 tr("Change encryption of the mediuma"));
16661 if (FAILED(rc))
16662 return rc;
16663
16664 /* create and start the task on a separate thread (note that it will not
16665 * start working until we release alock) */
16666 ChangeEncryptionTask *pTask = new ChangeEncryptionTask(this, pProgress, "VM encryption",
16667 aCurrentPassword, aCipher, aNewPassword,
16668 aNewPasswordId, aForce, llMedia);
16669 rc = pTask->createThread();
16670 pTask = NULL;
16671 if (FAILED(rc))
16672 return rc;
16673
16674 pProgress.queryInterfaceTo(aProgress.asOutParam());
16675
16676 LogFlowFuncLeave();
16677
16678 return S_OK;
16679#endif
16680}
16681
16682HRESULT Machine::getEncryptionSettings(com::Utf8Str &aCipher,
16683 com::Utf8Str &aPasswordId)
16684{
16685#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16686 RT_NOREF(aCipher, aPasswordId);
16687 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16688#else
16689 AutoLimitedCaller autoCaller(this);
16690 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16691
16692 PCVBOXCRYPTOIF pCryptoIf = NULL;
16693 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16694 if (FAILED(hrc)) return hrc; /* Error is set */
16695
16696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16697
16698 if (mData->mstrKeyStore.isNotEmpty())
16699 {
16700 char *pszCipher = NULL;
16701 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), NULL /*pszPassword*/,
16702 NULL /*ppbKey*/, NULL /*pcbKey*/, &pszCipher);
16703 if (RT_SUCCESS(vrc))
16704 {
16705 aCipher = getCipherStringWithoutMode(pszCipher);
16706 RTStrFree(pszCipher);
16707 aPasswordId = mData->mstrKeyId;
16708 }
16709 else
16710 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
16711 tr("Failed to query the encryption settings with %Rrc"),
16712 vrc);
16713 }
16714 else
16715 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16716
16717 mParent->i_releaseCryptoIf(pCryptoIf);
16718
16719 return hrc;
16720#endif
16721}
16722
16723HRESULT Machine::checkEncryptionPassword(const com::Utf8Str &aPassword)
16724{
16725#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16726 RT_NOREF(aPassword);
16727 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16728#else
16729 AutoLimitedCaller autoCaller(this);
16730 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16731
16732 PCVBOXCRYPTOIF pCryptoIf = NULL;
16733 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16734 if (FAILED(hrc)) return hrc; /* Error is set */
16735
16736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16737
16738 if (mData->mstrKeyStore.isNotEmpty())
16739 {
16740 char *pszCipher = NULL;
16741 uint8_t *pbDek = NULL;
16742 size_t cbDek = 0;
16743 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), aPassword.c_str(),
16744 &pbDek, &cbDek, &pszCipher);
16745 if (RT_SUCCESS(vrc))
16746 {
16747 RTStrFree(pszCipher);
16748 RTMemSaferFree(pbDek, cbDek);
16749 }
16750 else
16751 hrc = setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc,
16752 tr("The password supplied for the encrypted machine is incorrect"));
16753 }
16754 else
16755 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16756
16757 mParent->i_releaseCryptoIf(pCryptoIf);
16758
16759 return hrc;
16760#endif
16761}
16762
16763HRESULT Machine::addEncryptionPassword(const com::Utf8Str &aId,
16764 const com::Utf8Str &aPassword)
16765{
16766#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16767 RT_NOREF(aId, aPassword);
16768 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16769#else
16770 AutoLimitedCaller autoCaller(this);
16771 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16772
16773 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16774
16775 size_t cbPassword = aPassword.length() + 1;
16776 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
16777
16778 mData->mpKeyStore->addSecretKey(aId, pbPassword, cbPassword);
16779
16780 if ( mData->mAccessible
16781 && mData->mSession.mState == SessionState_Locked
16782 && mData->mSession.mLockType == LockType_VM
16783 && mData->mSession.mDirectControl != NULL)
16784 {
16785 /* get the console from the direct session */
16786 ComPtr<IConsole> console;
16787 HRESULT rc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16788 ComAssertComRC(rc);
16789 /* send passsword to console */
16790 console->AddEncryptionPassword(Bstr(aId).raw(),
16791 Bstr(aPassword).raw(),
16792 TRUE);
16793 }
16794
16795 if (mData->mstrKeyId == aId)
16796 {
16797 HRESULT hrc = checkEncryptionPassword(aPassword);
16798 if (FAILED(hrc))
16799 return hrc;
16800
16801 if (SUCCEEDED(hrc))
16802 {
16803 /*
16804 * Encryption is used and password is correct,
16805 * Reinit the machine if required.
16806 */
16807 BOOL fAccessible;
16808 alock.release();
16809 getAccessible(&fAccessible);
16810 alock.acquire();
16811 }
16812 }
16813
16814 /*
16815 * Add the password into the NvramStore only after
16816 * the machine becomes accessible and the NvramStore
16817 * contains key id and key store.
16818 */
16819 if (mNvramStore.isNotNull())
16820 mNvramStore->i_addPassword(aId, aPassword);
16821
16822 return S_OK;
16823#endif
16824}
16825
16826HRESULT Machine::addEncryptionPasswords(const std::vector<com::Utf8Str> &aIds,
16827 const std::vector<com::Utf8Str> &aPasswords)
16828{
16829#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16830 RT_NOREF(aIds, aPasswords);
16831 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16832#else
16833 if (aIds.size() != aPasswords.size())
16834 return setError(E_INVALIDARG, tr("Id and passwords arrays must have the same size"));
16835
16836 HRESULT hrc = S_OK;
16837 for (size_t i = 0; i < aIds.size() && SUCCEEDED(hrc); ++i)
16838 hrc = addEncryptionPassword(aIds[i], aPasswords[i]);
16839
16840 return hrc;
16841#endif
16842}
16843
16844HRESULT Machine::removeEncryptionPassword(AutoCaller &autoCaller, const com::Utf8Str &aId)
16845{
16846#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16847 RT_NOREF(autoCaller, aId);
16848 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16849#else
16850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16851
16852 if ( mData->mAccessible
16853 && mData->mSession.mState == SessionState_Locked
16854 && mData->mSession.mLockType == LockType_VM
16855 && mData->mSession.mDirectControl != NULL)
16856 {
16857 /* get the console from the direct session */
16858 ComPtr<IConsole> console;
16859 HRESULT rc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16860 ComAssertComRC(rc);
16861 /* send passsword to console */
16862 console->RemoveEncryptionPassword(Bstr(aId).raw());
16863 }
16864
16865 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty() && mData->mstrKeyId == aId)
16866 {
16867 if (Global::IsOnlineOrTransient(mData->mMachineState))
16868 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
16869 alock.release();
16870 autoCaller.release();
16871 /* return because all passwords are purged when machine becomes inaccessible; */
16872 return i_setInaccessible();
16873 }
16874
16875 if (mNvramStore.isNotNull())
16876 mNvramStore->i_removePassword(aId);
16877 mData->mpKeyStore->deleteSecretKey(aId);
16878 return S_OK;
16879#endif
16880}
16881
16882HRESULT Machine::clearAllEncryptionPasswords(AutoCaller &autoCaller)
16883{
16884#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16885 RT_NOREF(autoCaller);
16886 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16887#else
16888 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16889
16890 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty())
16891 {
16892 if (Global::IsOnlineOrTransient(mData->mMachineState))
16893 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
16894 alock.release();
16895 autoCaller.release();
16896 /* return because all passwords are purged when machine becomes inaccessible; */
16897 return i_setInaccessible();
16898 }
16899
16900 mNvramStore->i_removeAllPasswords();
16901 mData->mpKeyStore->deleteAllSecretKeys(false, true);
16902 return S_OK;
16903#endif
16904}
16905
16906#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
16907HRESULT Machine::i_setInaccessible()
16908{
16909 if (!mData->mAccessible)
16910 return S_OK;
16911
16912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16913 VirtualBox *pParent = mParent;
16914 com::Utf8Str strConfigFile = mData->m_strConfigFile;
16915 Guid id(i_getId());
16916
16917 alock.release();
16918
16919 uninit();
16920 HRESULT rc = initFromSettings(pParent, strConfigFile, &id, com::Utf8Str());
16921
16922 alock.acquire();
16923 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
16924 return rc;
16925}
16926#endif
16927
16928/* This isn't handled entirely by the wrapper generator yet. */
16929#ifdef VBOX_WITH_XPCOM
16930NS_DECL_CLASSINFO(SessionMachine)
16931NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
16932
16933NS_DECL_CLASSINFO(SnapshotMachine)
16934NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
16935#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