VirtualBox

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

Last change on this file since 98351 was 98351, checked in by vboxsync, 23 months ago

Main: Fix plural of "medium" in many comments.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 601.7 KB
Line 
1/* $Id: MachineImpl.cpp 98351 2023-01-30 19:26:27Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
29
30/* Make sure all the stdint.h macros are included - must come first! */
31#ifndef __STDC_LIMIT_MACROS
32# define __STDC_LIMIT_MACROS
33#endif
34#ifndef __STDC_CONSTANT_MACROS
35# define __STDC_CONSTANT_MACROS
36#endif
37
38#include "LoggingNew.h"
39#include "VirtualBoxImpl.h"
40#include "MachineImpl.h"
41#include "SnapshotImpl.h"
42#include "ClientToken.h"
43#include "ProgressImpl.h"
44#include "ProgressProxyImpl.h"
45#include "MediumAttachmentImpl.h"
46#include "MediumImpl.h"
47#include "MediumLock.h"
48#include "USBControllerImpl.h"
49#include "USBDeviceFiltersImpl.h"
50#include "HostImpl.h"
51#include "SharedFolderImpl.h"
52#include "GuestOSTypeImpl.h"
53#include "VirtualBoxErrorInfoImpl.h"
54#include "StorageControllerImpl.h"
55#include "DisplayImpl.h"
56#include "DisplayUtils.h"
57#include "MachineImplCloneVM.h"
58#include "AutostartDb.h"
59#include "SystemPropertiesImpl.h"
60#include "MachineImplMoveVM.h"
61#include "ExtPackManagerImpl.h"
62#include "MachineLaunchVMCommonWorker.h"
63#include "CryptoUtils.h"
64
65// generated header
66#include "VBoxEvents.h"
67
68#ifdef VBOX_WITH_USB
69# include "USBProxyService.h"
70#endif
71
72#include "AutoCaller.h"
73#include "HashedPw.h"
74#include "Performance.h"
75#include "StringifyEnums.h"
76
77#include <iprt/asm.h>
78#include <iprt/path.h>
79#include <iprt/dir.h>
80#include <iprt/env.h>
81#include <iprt/lockvalidator.h>
82#include <iprt/memsafer.h>
83#include <iprt/process.h>
84#include <iprt/cpp/utils.h>
85#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
86#include <iprt/sha.h>
87#include <iprt/string.h>
88#include <iprt/ctype.h>
89
90#include <VBox/com/array.h>
91#include <VBox/com/list.h>
92#include <VBox/VBoxCryptoIf.h>
93
94#include <VBox/err.h>
95#include <VBox/param.h>
96#include <VBox/settings.h>
97#include <VBox/VMMDev.h>
98#include <VBox/vmm/ssm.h>
99
100#ifdef VBOX_WITH_GUEST_PROPS
101# include <VBox/HostServices/GuestPropertySvc.h>
102# include <VBox/com/array.h>
103#endif
104
105#ifdef VBOX_WITH_SHARED_CLIPBOARD
106# include <VBox/HostServices/VBoxClipboardSvc.h>
107#endif
108
109#include "VBox/com/MultiResult.h"
110
111#include <algorithm>
112
113#ifdef VBOX_WITH_DTRACE_R3_MAIN
114# include "dtrace/VBoxAPI.h"
115#endif
116
117#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
118# define HOSTSUFF_EXE ".exe"
119#else /* !RT_OS_WINDOWS */
120# define HOSTSUFF_EXE ""
121#endif /* !RT_OS_WINDOWS */
122
123// defines / prototypes
124/////////////////////////////////////////////////////////////////////////////
125
126#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
127# define BUF_DATA_SIZE _64K
128
129enum CipherMode
130{
131 CipherModeGcm = 0,
132 CipherModeCtr,
133 CipherModeXts,
134 CipherModeMax
135};
136
137enum AesSize
138{
139 Aes128 = 0,
140 Aes256,
141 AesMax
142};
143
144const char *g_apszCipher[AesMax][CipherModeMax] =
145{
146 {"AES-GCM128", "AES-CTR128", "AES-XTS128-PLAIN64"},
147 {"AES-GCM256", "AES-CTR256", "AES-XTS256-PLAIN64"}
148};
149const char *g_apszCipherAlgo[AesMax] = {"AES-128", "AES-256"};
150
151static const char *getCipherString(const char *pszAlgo, const int iMode)
152{
153 if (iMode >= CipherModeMax)
154 return pszAlgo;
155
156 for (int i = 0; i < AesMax; i++)
157 {
158 if (strcmp(pszAlgo, g_apszCipherAlgo[i]) == 0)
159 return g_apszCipher[i][iMode];
160 }
161 return pszAlgo;
162}
163
164static const char *getCipherStringWithoutMode(const char *pszAlgo)
165{
166 for (int i = 0; i < AesMax; i++)
167 {
168 for (int j = 0; j < CipherModeMax; j++)
169 {
170 if (strcmp(pszAlgo, g_apszCipher[i][j]) == 0)
171 return g_apszCipherAlgo[i];
172 }
173 }
174 return pszAlgo;
175}
176#endif
177
178/////////////////////////////////////////////////////////////////////////////
179// Machine::Data structure
180/////////////////////////////////////////////////////////////////////////////
181
182Machine::Data::Data()
183{
184 mRegistered = FALSE;
185 pMachineConfigFile = NULL;
186 /* Contains hints on what has changed when the user is using the VM (config
187 * changes, running the VM, ...). This is used to decide if a config needs
188 * to be written to disk. */
189 flModifications = 0;
190 /* VM modification usually also trigger setting the current state to
191 * "Modified". Although this is not always the case. An e.g. is the VM
192 * initialization phase or when snapshot related data is changed. The
193 * actually behavior is controlled by the following flag. */
194 m_fAllowStateModification = false;
195 mAccessible = FALSE;
196 /* mUuid is initialized in Machine::init() */
197
198 mMachineState = MachineState_PoweredOff;
199 RTTimeNow(&mLastStateChange);
200
201 mMachineStateDeps = 0;
202 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
203 mMachineStateChangePending = 0;
204
205 mCurrentStateModified = TRUE;
206 mGuestPropertiesModified = FALSE;
207
208 mSession.mPID = NIL_RTPROCESS;
209 mSession.mLockType = LockType_Null;
210 mSession.mState = SessionState_Unlocked;
211
212#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
213 mpKeyStore = NULL;
214#endif
215}
216
217Machine::Data::~Data()
218{
219 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
220 {
221 RTSemEventMultiDestroy(mMachineStateDepsSem);
222 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
223 }
224 if (pMachineConfigFile)
225 {
226 delete pMachineConfigFile;
227 pMachineConfigFile = NULL;
228 }
229}
230
231/////////////////////////////////////////////////////////////////////////////
232// Machine::HWData structure
233/////////////////////////////////////////////////////////////////////////////
234
235Machine::HWData::HWData()
236{
237 /* default values for a newly created machine */
238 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
239 mMemorySize = 128;
240 mCPUCount = 1;
241 mCPUHotPlugEnabled = false;
242 mMemoryBalloonSize = 0;
243 mPageFusionEnabled = false;
244 mHWVirtExEnabled = true;
245 mHWVirtExNestedPagingEnabled = true;
246 mHWVirtExLargePagesEnabled = HC_ARCH_BITS == 64; /* Not supported on 32 bits hosts. */
247 mHWVirtExVPIDEnabled = true;
248 mHWVirtExUXEnabled = true;
249 mHWVirtExForceEnabled = false;
250 mHWVirtExUseNativeApi = false;
251 mHWVirtExVirtVmsaveVmload = true;
252#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
253 mPAEEnabled = true;
254#else
255 mPAEEnabled = false;
256#endif
257 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
258 mTripleFaultReset = false;
259 mAPIC = true;
260 mX2APIC = false;
261 mIBPBOnVMExit = false;
262 mIBPBOnVMEntry = false;
263 mSpecCtrl = false;
264 mSpecCtrlByHost = false;
265 mL1DFlushOnSched = true;
266 mL1DFlushOnVMEntry = false;
267 mMDSClearOnSched = true;
268 mMDSClearOnVMEntry = false;
269 mNestedHWVirt = false;
270 mHPETEnabled = false;
271 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
272 mCpuIdPortabilityLevel = 0;
273 mCpuProfile = "host";
274
275 /* default boot order: floppy - DVD - HDD */
276 mBootOrder[0] = DeviceType_Floppy;
277 mBootOrder[1] = DeviceType_DVD;
278 mBootOrder[2] = DeviceType_HardDisk;
279 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
280 mBootOrder[i] = DeviceType_Null;
281
282 mClipboardMode = ClipboardMode_Disabled;
283 mClipboardFileTransfersEnabled = FALSE;
284
285 mDnDMode = DnDMode_Disabled;
286
287 mFirmwareType = FirmwareType_BIOS;
288 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
289 mPointingHIDType = PointingHIDType_PS2Mouse;
290 mChipsetType = ChipsetType_PIIX3;
291 mIommuType = IommuType_None;
292 mParavirtProvider = ParavirtProvider_Default;
293 mEmulatedUSBCardReaderEnabled = FALSE;
294
295 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
296 mCPUAttached[i] = false;
297
298 mIOCacheEnabled = true;
299 mIOCacheSize = 5; /* 5MB */
300}
301
302Machine::HWData::~HWData()
303{
304}
305
306/////////////////////////////////////////////////////////////////////////////
307// Machine class
308/////////////////////////////////////////////////////////////////////////////
309
310// constructor / destructor
311/////////////////////////////////////////////////////////////////////////////
312
313Machine::Machine() :
314#ifdef VBOX_WITH_RESOURCE_USAGE_API
315 mCollectorGuest(NULL),
316#endif
317 mPeer(NULL),
318 mParent(NULL),
319 mSerialPorts(),
320 mParallelPorts(),
321 uRegistryNeedsSaving(0)
322{}
323
324Machine::~Machine()
325{}
326
327HRESULT Machine::FinalConstruct()
328{
329 LogFlowThisFunc(("\n"));
330 return BaseFinalConstruct();
331}
332
333void Machine::FinalRelease()
334{
335 LogFlowThisFunc(("\n"));
336 uninit();
337 BaseFinalRelease();
338}
339
340/**
341 * Initializes a new machine instance; this init() variant creates a new, empty machine.
342 * This gets called from VirtualBox::CreateMachine().
343 *
344 * @param aParent Associated parent object
345 * @param strConfigFile Local file system path to the VM settings file (can
346 * be relative to the VirtualBox config directory).
347 * @param strName name for the machine
348 * @param llGroups list of groups for the machine
349 * @param strOsType OS Type string (stored as is if aOsType is NULL).
350 * @param aOsType OS Type of this machine or NULL.
351 * @param aId UUID for the new machine.
352 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
353 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
354 * scheme (includes the UUID).
355 * @param aCipher The cipher to encrypt the VM with.
356 * @param aPasswordId The password ID, empty if the VM should not be encrypted.
357 * @param aPassword The password to encrypt the VM with.
358 *
359 * @return Success indicator. if not S_OK, the machine object is invalid
360 */
361HRESULT Machine::init(VirtualBox *aParent,
362 const Utf8Str &strConfigFile,
363 const Utf8Str &strName,
364 const StringsList &llGroups,
365 const Utf8Str &strOsType,
366 GuestOSType *aOsType,
367 const Guid &aId,
368 bool fForceOverwrite,
369 bool fDirectoryIncludesUUID,
370 const com::Utf8Str &aCipher,
371 const com::Utf8Str &aPasswordId,
372 const com::Utf8Str &aPassword)
373{
374 LogFlowThisFuncEnter();
375 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
376
377#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
378 RT_NOREF(aCipher);
379 if (aPassword.isNotEmpty() || aPasswordId.isNotEmpty())
380 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
381#endif
382
383 /* Enclose the state transition NotReady->InInit->Ready */
384 AutoInitSpan autoInitSpan(this);
385 AssertReturn(autoInitSpan.isOk(), E_FAIL);
386
387 HRESULT hrc = initImpl(aParent, strConfigFile);
388 if (FAILED(hrc)) return hrc;
389
390#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
391 com::Utf8Str strSsmKeyId;
392 com::Utf8Str strSsmKeyStore;
393 com::Utf8Str strNVRAMKeyId;
394 com::Utf8Str strNVRAMKeyStore;
395
396 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
397 {
398 /* Resolve the cryptographic interface. */
399 PCVBOXCRYPTOIF pCryptoIf = NULL;
400 hrc = aParent->i_retainCryptoIf(&pCryptoIf);
401 if (SUCCEEDED(hrc))
402 {
403 CipherMode aenmMode[] = {CipherModeGcm, CipherModeGcm, CipherModeGcm, CipherModeCtr};
404 com::Utf8Str *astrKeyId[] = {&mData->mstrKeyId, &strSsmKeyId, &strNVRAMKeyId, &mData->mstrLogKeyId};
405 com::Utf8Str *astrKeyStore[] = {&mData->mstrKeyStore, &strSsmKeyStore, &strNVRAMKeyStore, &mData->mstrLogKeyStore};
406
407 for (uint32_t i = 0; i < RT_ELEMENTS(astrKeyId); i++)
408 {
409 const char *pszCipher = getCipherString(aCipher.c_str(), aenmMode[i]);
410 if (!pszCipher)
411 {
412 hrc = setError(VBOX_E_NOT_SUPPORTED,
413 tr("The cipher '%s' is not supported"), aCipher.c_str());
414 break;
415 }
416
417 VBOXCRYPTOCTX hCryptoCtx;
418 int vrc = pCryptoIf->pfnCryptoCtxCreate(pszCipher, aPassword.c_str(), &hCryptoCtx);
419 if (RT_FAILURE(vrc))
420 {
421 hrc = setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
422 break;
423 }
424
425 char *pszKeyStore;
426 vrc = pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
427 int vrc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
428 AssertRC(vrc2);
429
430 if (RT_FAILURE(vrc))
431 {
432 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
433 break;
434 }
435
436 *(astrKeyStore[i]) = pszKeyStore;
437 RTMemFree(pszKeyStore);
438 *(astrKeyId[i]) = aPasswordId;
439 }
440
441 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
442 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
443
444 if (FAILED(hrc))
445 return hrc; /* Error is set. */
446 }
447 else
448 return hrc; /* Error is set. */
449 }
450#endif
451
452 hrc = i_tryCreateMachineConfigFile(fForceOverwrite);
453 if (FAILED(hrc)) return hrc;
454
455 if (SUCCEEDED(hrc))
456 {
457 // create an empty machine config
458 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
459
460 hrc = initDataAndChildObjects();
461 }
462
463 if (SUCCEEDED(hrc))
464 {
465#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
466 mSSData->strStateKeyId = strSsmKeyId;
467 mSSData->strStateKeyStore = strSsmKeyStore;
468#endif
469
470 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
471 mData->mAccessible = TRUE;
472
473 unconst(mData->mUuid) = aId;
474
475 mUserData->s.strName = strName;
476
477 if (llGroups.size())
478 mUserData->s.llGroups = llGroups;
479
480 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
481 // the "name sync" flag determines whether the machine directory gets renamed along
482 // with the machine file; say so if the settings file name is the same as the
483 // settings file parent directory (machine directory)
484 mUserData->s.fNameSync = i_isInOwnDir();
485
486 // initialize the default snapshots folder
487 hrc = COMSETTER(SnapshotFolder)(NULL);
488 AssertComRC(hrc);
489
490 if (aOsType)
491 {
492 /* Store OS type */
493 mUserData->s.strOsType = aOsType->i_id();
494
495 /* Let the OS type select 64-bit ness. */
496 mHWData->mLongMode = aOsType->i_is64Bit()
497 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
498
499 /* Let the OS type enable the X2APIC */
500 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
501
502 hrc = aOsType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
503 AssertComRC(hrc);
504 }
505 else if (!strOsType.isEmpty())
506 {
507 /* Store OS type */
508 mUserData->s.strOsType = strOsType;
509
510 /* No guest OS type object. Pick some plausible defaults which the
511 * host can handle. There's no way to know or validate anything. */
512 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
513 mHWData->mX2APIC = false;
514 }
515
516 /* Apply BIOS defaults. */
517 mBIOSSettings->i_applyDefaults(aOsType);
518
519 /* Apply TPM defaults. */
520 mTrustedPlatformModule->i_applyDefaults(aOsType);
521
522 /* Apply recording defaults. */
523 mRecordingSettings->i_applyDefaults();
524
525 /* Apply network adapters defaults */
526 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
527 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
528
529 /* Apply serial port defaults */
530 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
531 mSerialPorts[slot]->i_applyDefaults(aOsType);
532
533 /* Apply parallel port defaults */
534 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
535 mParallelPorts[slot]->i_applyDefaults();
536
537 /* Enable the VMMDev testing feature for bootsector VMs: */
538 if (aOsType && aOsType->i_id() == "VBoxBS_64")
539 mData->pMachineConfigFile->mapExtraDataItems["VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled"] = "1";
540
541#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
542 hrc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
543#endif
544 if (SUCCEEDED(hrc))
545 {
546 /* At this point the changing of the current state modification
547 * flag is allowed. */
548 i_allowStateModification();
549
550 /* commit all changes made during the initialization */
551 i_commit();
552 }
553 }
554
555 /* Confirm a successful initialization when it's the case */
556 if (SUCCEEDED(hrc))
557 {
558#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
559 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
560 {
561 size_t cbPassword = aPassword.length() + 1;
562 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
563 mData->mpKeyStore->addSecretKey(aPasswordId, pbPassword, cbPassword);
564 }
565#endif
566
567 if (mData->mAccessible)
568 autoInitSpan.setSucceeded();
569 else
570 autoInitSpan.setLimited();
571 }
572
573 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, hrc=%08X\n",
574 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
575 mData->mRegistered,
576 mData->mAccessible,
577 hrc));
578
579 LogFlowThisFuncLeave();
580
581 return hrc;
582}
583
584/**
585 * Initializes a new instance with data from machine XML (formerly Init_Registered).
586 * Gets called in two modes:
587 *
588 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
589 * UUID is specified and we mark the machine as "registered";
590 *
591 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
592 * and the machine remains unregistered until RegisterMachine() is called.
593 *
594 * @param aParent Associated parent object
595 * @param strConfigFile Local file system path to the VM settings file (can
596 * be relative to the VirtualBox config directory).
597 * @param aId UUID of the machine or NULL (see above).
598 * @param strPassword Password for decrypting the config
599 *
600 * @return Success indicator. if not S_OK, the machine object is invalid
601 */
602HRESULT Machine::initFromSettings(VirtualBox *aParent,
603 const Utf8Str &strConfigFile,
604 const Guid *aId,
605 const com::Utf8Str &strPassword)
606{
607 LogFlowThisFuncEnter();
608 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
609
610 PCVBOXCRYPTOIF pCryptoIf = NULL;
611#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
612 if (strPassword.isNotEmpty())
613 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
614#else
615 if (strPassword.isNotEmpty())
616 {
617 /* Get at the crpytographic interface. */
618 HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
619 if (FAILED(hrc))
620 return hrc; /* Error is set. */
621 }
622#endif
623
624 /* Enclose the state transition NotReady->InInit->Ready */
625 AutoInitSpan autoInitSpan(this);
626 AssertReturn(autoInitSpan.isOk(), E_FAIL);
627
628 HRESULT hrc = initImpl(aParent, strConfigFile);
629 if (FAILED(hrc)) return hrc;
630
631 if (aId)
632 {
633 // loading a registered VM:
634 unconst(mData->mUuid) = *aId;
635 mData->mRegistered = TRUE;
636 // now load the settings from XML:
637 hrc = i_registeredInit();
638 // this calls initDataAndChildObjects() and loadSettings()
639 }
640 else
641 {
642 // opening an unregistered VM (VirtualBox::OpenMachine()):
643 hrc = initDataAndChildObjects();
644 if (SUCCEEDED(hrc))
645 {
646 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
647 mData->mAccessible = TRUE;
648
649 try
650 {
651 // load and parse machine XML; this will throw on XML or logic errors
652 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
653 pCryptoIf,
654 strPassword.c_str());
655
656 // reject VM UUID duplicates, they can happen if someone
657 // tries to register an already known VM config again
658 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
659 true /* fPermitInaccessible */,
660 false /* aDoSetError */,
661 NULL) != VBOX_E_OBJECT_NOT_FOUND)
662 {
663 throw setError(E_FAIL,
664 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
665 mData->m_strConfigFile.c_str());
666 }
667
668 // use UUID from machine config
669 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
670
671#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
672 // No exception is thrown if config is encrypted, allowing us to get the uuid and the encryption fields.
673 // We fill in the encryptions fields, and the rest will be filled in if all data parsed.
674 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
675 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
676#endif
677
678 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
679 {
680 // We just set the inaccessible state and fill the error info allowing the caller
681 // to register the machine with encrypted config even if the password is incorrect
682 mData->mAccessible = FALSE;
683
684 /* fetch the current error info */
685 mData->mAccessError = com::ErrorInfo();
686
687 setError(VBOX_E_PASSWORD_INCORRECT,
688 tr("Decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
689 mData->pMachineConfigFile->uuid.raw());
690 }
691 else
692 {
693#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
694 if (strPassword.isNotEmpty())
695 {
696 size_t cbKey = strPassword.length() + 1; /* Include terminator */
697 const uint8_t *pbKey = (const uint8_t *)strPassword.c_str();
698 mData->mpKeyStore->addSecretKey(mData->mstrKeyId, pbKey, cbKey);
699 }
700#endif
701
702 hrc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile, NULL /* puuidRegistry */);
703 if (FAILED(hrc)) throw hrc;
704
705 /* At this point the changing of the current state modification
706 * flag is allowed. */
707 i_allowStateModification();
708
709 i_commit();
710 }
711 }
712 catch (HRESULT err)
713 {
714 /* we assume that error info is set by the thrower */
715 hrc = err;
716 }
717 catch (...)
718 {
719 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
720 }
721 }
722 }
723
724 /* Confirm a successful initialization when it's the case */
725 if (SUCCEEDED(hrc))
726 {
727 if (mData->mAccessible)
728 autoInitSpan.setSucceeded();
729 else
730 {
731 autoInitSpan.setLimited();
732
733 // uninit media from this machine's media registry, or else
734 // reloading the settings will fail
735 mParent->i_unregisterMachineMedia(i_getId());
736 }
737 }
738
739#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
740 if (pCryptoIf)
741 {
742 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
743 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
744 }
745#endif
746
747 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool hrc=%08X\n",
748 !!mUserData ? mUserData->s.strName.c_str() : "NULL", mData->mRegistered, mData->mAccessible, hrc));
749
750 LogFlowThisFuncLeave();
751
752 return hrc;
753}
754
755/**
756 * Initializes a new instance from a machine config that is already in memory
757 * (import OVF case). Since we are importing, the UUID in the machine
758 * config is ignored and we always generate a fresh one.
759 *
760 * @param aParent Associated parent object.
761 * @param strName Name for the new machine; this overrides what is specified in config.
762 * @param strSettingsFilename File name of .vbox file.
763 * @param config Machine configuration loaded and parsed from XML.
764 *
765 * @return Success indicator. if not S_OK, the machine object is invalid
766 */
767HRESULT Machine::init(VirtualBox *aParent,
768 const Utf8Str &strName,
769 const Utf8Str &strSettingsFilename,
770 const settings::MachineConfigFile &config)
771{
772 LogFlowThisFuncEnter();
773
774 /* Enclose the state transition NotReady->InInit->Ready */
775 AutoInitSpan autoInitSpan(this);
776 AssertReturn(autoInitSpan.isOk(), E_FAIL);
777
778 HRESULT hrc = initImpl(aParent, strSettingsFilename);
779 if (FAILED(hrc)) return hrc;
780
781 hrc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
782 if (FAILED(hrc)) return hrc;
783
784 hrc = initDataAndChildObjects();
785 if (SUCCEEDED(hrc))
786 {
787 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
788 mData->mAccessible = TRUE;
789
790 // create empty machine config for instance data
791 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
792
793 // generate fresh UUID, ignore machine config
794 unconst(mData->mUuid).create();
795
796 hrc = i_loadMachineDataFromSettings(config, &mData->mUuid); // puuidRegistry: initialize media with this registry ID
797
798 // override VM name as well, it may be different
799 mUserData->s.strName = strName;
800
801 if (SUCCEEDED(hrc))
802 {
803 /* At this point the changing of the current state modification
804 * flag is allowed. */
805 i_allowStateModification();
806
807 /* commit all changes made during the initialization */
808 i_commit();
809 }
810 }
811
812 /* Confirm a successful initialization when it's the case */
813 if (SUCCEEDED(hrc))
814 {
815 if (mData->mAccessible)
816 autoInitSpan.setSucceeded();
817 else
818 {
819 /* Ignore all errors from unregistering, they would destroy
820- * the more interesting error information we already have,
821- * pinpointing the issue with the VM config. */
822 ErrorInfoKeeper eik;
823
824 autoInitSpan.setLimited();
825
826 // uninit media from this machine's media registry, or else
827 // reloading the settings will fail
828 mParent->i_unregisterMachineMedia(i_getId());
829 }
830 }
831
832 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool hrc=%08X\n",
833 !!mUserData ? mUserData->s.strName.c_str() : "NULL", mData->mRegistered, mData->mAccessible, hrc));
834
835 LogFlowThisFuncLeave();
836
837 return hrc;
838}
839
840/**
841 * Shared code between the various init() implementations.
842 * @param aParent The VirtualBox object.
843 * @param strConfigFile Settings file.
844 * @return
845 */
846HRESULT Machine::initImpl(VirtualBox *aParent,
847 const Utf8Str &strConfigFile)
848{
849 LogFlowThisFuncEnter();
850
851 AssertReturn(aParent, E_INVALIDARG);
852 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
853
854 HRESULT hrc = S_OK;
855
856 /* share the parent weakly */
857 unconst(mParent) = aParent;
858
859 /* allocate the essential machine data structure (the rest will be
860 * allocated later by initDataAndChildObjects() */
861 mData.allocate();
862
863 /* memorize the config file name (as provided) */
864 mData->m_strConfigFile = strConfigFile;
865
866 /* get the full file name */
867 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
868 if (RT_FAILURE(vrc1))
869 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
870 tr("Invalid machine settings file name '%s' (%Rrc)"),
871 strConfigFile.c_str(),
872 vrc1);
873
874#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
875 /** @todo Only create when the machine is going to be encrypted. */
876 /* Non-pageable memory is not accessible for non-VM process */
877 mData->mpKeyStore = new SecretKeyStore(false /* fKeyBufNonPageable */);
878 AssertReturn(mData->mpKeyStore, E_OUTOFMEMORY);
879#endif
880
881 LogFlowThisFuncLeave();
882
883 return hrc;
884}
885
886/**
887 * Tries to create a machine settings file in the path stored in the machine
888 * instance data. Used when a new machine is created to fail gracefully if
889 * the settings file could not be written (e.g. because machine dir is read-only).
890 * @return
891 */
892HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
893{
894 HRESULT hrc = S_OK;
895
896 // when we create a new machine, we must be able to create the settings file
897 RTFILE f = NIL_RTFILE;
898 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
899 if ( RT_SUCCESS(vrc)
900 || vrc == VERR_SHARING_VIOLATION
901 )
902 {
903 if (RT_SUCCESS(vrc))
904 RTFileClose(f);
905 if (!fForceOverwrite)
906 hrc = setError(VBOX_E_FILE_ERROR, tr("Machine settings file '%s' already exists"), mData->m_strConfigFileFull.c_str());
907 else
908 {
909 /* try to delete the config file, as otherwise the creation
910 * of a new settings file will fail. */
911 i_deleteFile(mData->m_strConfigFileFull.c_str(), false /* fIgnoreFailures */, tr("existing settings file"));
912 }
913 }
914 else if ( vrc != VERR_FILE_NOT_FOUND
915 && vrc != VERR_PATH_NOT_FOUND
916 )
917 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Invalid machine settings file name '%s' (%Rrc)"),
918 mData->m_strConfigFileFull.c_str(), vrc);
919 return hrc;
920}
921
922/**
923 * Initializes the registered machine by loading the settings file.
924 * This method is separated from #init() in order to make it possible to
925 * retry the operation after VirtualBox startup instead of refusing to
926 * startup the whole VirtualBox server in case if the settings file of some
927 * registered VM is invalid or inaccessible.
928 *
929 * @note Must be always called from this object's write lock
930 * (unless called from #init() that doesn't need any locking).
931 * @note Locks the mUSBController method for writing.
932 * @note Subclasses must not call this method.
933 */
934HRESULT Machine::i_registeredInit()
935{
936 AssertReturn(!i_isSessionMachine(), E_FAIL);
937 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
938 AssertReturn(mData->mUuid.isValid(), E_FAIL);
939 AssertReturn(!mData->mAccessible, E_FAIL);
940
941 HRESULT hrc = initDataAndChildObjects();
942 if (SUCCEEDED(hrc))
943 {
944 /* Temporarily reset the registered flag in order to let setters
945 * potentially called from loadSettings() succeed (isMutable() used in
946 * all setters will return FALSE for a Machine instance if mRegistered
947 * is TRUE). */
948 mData->mRegistered = FALSE;
949
950 PCVBOXCRYPTOIF pCryptoIf = NULL;
951 SecretKey *pKey = NULL;
952 const char *pszPassword = NULL;
953#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
954 /* Resolve password and cryptographic support interface if machine is encrypted. */
955 if (mData->mstrKeyId.isNotEmpty())
956 {
957 /* Get at the crpytographic interface. */
958 hrc = mParent->i_retainCryptoIf(&pCryptoIf);
959 if (SUCCEEDED(hrc))
960 {
961 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
962 if (RT_SUCCESS(vrc))
963 pszPassword = (const char *)pKey->getKeyBuffer();
964 else
965 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to retain key for key ID '%s' with %Rrc"),
966 mData->mstrKeyId.c_str(), vrc);
967 }
968 }
969#else
970 RT_NOREF(pKey);
971#endif
972
973 if (SUCCEEDED(hrc))
974 {
975 try
976 {
977 // load and parse machine XML; this will throw on XML or logic errors
978 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
979 pCryptoIf, pszPassword);
980
981 if (mData->mUuid != mData->pMachineConfigFile->uuid)
982 throw setError(E_FAIL,
983 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
984 mData->pMachineConfigFile->uuid.raw(),
985 mData->m_strConfigFileFull.c_str(),
986 mData->mUuid.toString().c_str(),
987 mParent->i_settingsFilePath().c_str());
988
989#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
990 // If config is encrypted, no exception is thrown allowing us to get the uuid and the encryption fields.
991 // We fill in the encryptions fields, and the rest will be filled in if all data parsed
992 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
993 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
994
995 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
996 hrc = setError(VBOX_E_PASSWORD_INCORRECT,
997 tr("Config decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
998 mData->pMachineConfigFile->uuid.raw());
999 else
1000#endif
1001 hrc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile, NULL /* const Guid *puuidRegistry */);
1002 if (FAILED(hrc)) throw hrc;
1003 }
1004 catch (HRESULT err)
1005 {
1006 /* we assume that error info is set by the thrower */
1007 hrc = err;
1008 }
1009 catch (...)
1010 {
1011 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
1012 }
1013
1014 /* Restore the registered flag (even on failure) */
1015 mData->mRegistered = TRUE;
1016 }
1017
1018#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1019 if (pCryptoIf)
1020 mParent->i_releaseCryptoIf(pCryptoIf);
1021 if (pKey)
1022 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
1023#endif
1024 }
1025
1026 if (SUCCEEDED(hrc))
1027 {
1028 /* Set mAccessible to TRUE only if we successfully locked and loaded
1029 * the settings file */
1030 mData->mAccessible = TRUE;
1031
1032 /* commit all changes made during loading the settings file */
1033 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
1034 /// @todo r=klaus for some reason the settings loading logic backs up
1035 // the settings, and therefore a commit is needed. Should probably be changed.
1036 }
1037 else
1038 {
1039 /* If the machine is registered, then, instead of returning a
1040 * failure, we mark it as inaccessible and set the result to
1041 * success to give it a try later */
1042
1043 /* fetch the current error info */
1044 mData->mAccessError = com::ErrorInfo();
1045 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
1046
1047 /* rollback all changes */
1048 i_rollback(false /* aNotify */);
1049
1050 // uninit media from this machine's media registry, or else
1051 // reloading the settings will fail
1052 mParent->i_unregisterMachineMedia(i_getId());
1053
1054 /* uninitialize the common part to make sure all data is reset to
1055 * default (null) values */
1056 uninitDataAndChildObjects();
1057
1058 hrc = S_OK;
1059 }
1060
1061 return hrc;
1062}
1063
1064/**
1065 * Uninitializes the instance.
1066 * Called either from FinalRelease() or by the parent when it gets destroyed.
1067 *
1068 * @note The caller of this method must make sure that this object
1069 * a) doesn't have active callers on the current thread and b) is not locked
1070 * by the current thread; otherwise uninit() will hang either a) due to
1071 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
1072 * a dead-lock caused by this thread waiting for all callers on the other
1073 * threads are done but preventing them from doing so by holding a lock.
1074 */
1075void Machine::uninit()
1076{
1077 LogFlowThisFuncEnter();
1078
1079 Assert(!isWriteLockOnCurrentThread());
1080
1081 Assert(!uRegistryNeedsSaving);
1082 if (uRegistryNeedsSaving)
1083 {
1084 AutoCaller autoCaller(this);
1085 if (SUCCEEDED(autoCaller.hrc()))
1086 {
1087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1088 i_saveSettings(NULL, alock, Machine::SaveS_Force);
1089 }
1090 }
1091
1092 /* Enclose the state transition Ready->InUninit->NotReady */
1093 AutoUninitSpan autoUninitSpan(this);
1094 if (autoUninitSpan.uninitDone())
1095 return;
1096
1097 Assert(!i_isSnapshotMachine());
1098 Assert(!i_isSessionMachine());
1099 Assert(!!mData);
1100
1101 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
1102 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
1103
1104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1105
1106 if (!mData->mSession.mMachine.isNull())
1107 {
1108 /* Theoretically, this can only happen if the VirtualBox server has been
1109 * terminated while there were clients running that owned open direct
1110 * sessions. Since in this case we are definitely called by
1111 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
1112 * won't happen on the client watcher thread (because it has a
1113 * VirtualBox caller for the duration of the
1114 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
1115 * cannot happen until the VirtualBox caller is released). This is
1116 * important, because SessionMachine::uninit() cannot correctly operate
1117 * after we return from this method (it expects the Machine instance is
1118 * still valid). We'll call it ourselves below.
1119 */
1120 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
1121 (SessionMachine*)mData->mSession.mMachine));
1122
1123 if (Global::IsOnlineOrTransient(mData->mMachineState))
1124 {
1125 Log1WarningThisFunc(("Setting state to Aborted!\n"));
1126 /* set machine state using SessionMachine reimplementation */
1127 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
1128 }
1129
1130 /*
1131 * Uninitialize SessionMachine using public uninit() to indicate
1132 * an unexpected uninitialization.
1133 */
1134 mData->mSession.mMachine->uninit();
1135 /* SessionMachine::uninit() must set mSession.mMachine to null */
1136 Assert(mData->mSession.mMachine.isNull());
1137 }
1138
1139 // uninit media from this machine's media registry, if they're still there
1140 Guid uuidMachine(i_getId());
1141
1142 /* the lock is no more necessary (SessionMachine is uninitialized) */
1143 alock.release();
1144
1145 /* XXX This will fail with
1146 * "cannot be closed because it is still attached to 1 virtual machines"
1147 * because at this point we did not call uninitDataAndChildObjects() yet
1148 * and therefore also removeBackReference() for all these media was not called! */
1149
1150 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
1151 mParent->i_unregisterMachineMedia(uuidMachine);
1152
1153 // has machine been modified?
1154 if (mData->flModifications)
1155 {
1156 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
1157 i_rollback(false /* aNotify */);
1158 }
1159
1160 if (mData->mAccessible)
1161 uninitDataAndChildObjects();
1162
1163#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1164 if (mData->mpKeyStore != NULL)
1165 delete mData->mpKeyStore;
1166#endif
1167
1168 /* free the essential data structure last */
1169 mData.free();
1170
1171 LogFlowThisFuncLeave();
1172}
1173
1174// Wrapped IMachine properties
1175/////////////////////////////////////////////////////////////////////////////
1176HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
1177{
1178 /* mParent is constant during life time, no need to lock */
1179 ComObjPtr<VirtualBox> pVirtualBox(mParent);
1180 aParent = pVirtualBox;
1181
1182 return S_OK;
1183}
1184
1185
1186HRESULT Machine::getAccessible(BOOL *aAccessible)
1187{
1188 /* In some cases (medium registry related), it is necessary to be able to
1189 * go through the list of all machines. Happens when an inaccessible VM
1190 * has a sensible medium registry. */
1191 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
1192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1193
1194 HRESULT hrc = S_OK;
1195
1196 if (!mData->mAccessible)
1197 {
1198 /* try to initialize the VM once more if not accessible */
1199
1200 AutoReinitSpan autoReinitSpan(this);
1201 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
1202
1203#ifdef DEBUG
1204 LogFlowThisFunc(("Dumping media backreferences\n"));
1205 mParent->i_dumpAllBackRefs();
1206#endif
1207
1208 if (mData->pMachineConfigFile)
1209 {
1210 // reset the XML file to force loadSettings() (called from i_registeredInit())
1211 // to parse it again; the file might have changed
1212 delete mData->pMachineConfigFile;
1213 mData->pMachineConfigFile = NULL;
1214 }
1215
1216 hrc = i_registeredInit();
1217
1218 if (SUCCEEDED(hrc) && mData->mAccessible)
1219 {
1220 autoReinitSpan.setSucceeded();
1221
1222 /* make sure interesting parties will notice the accessibility
1223 * state change */
1224 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
1225 mParent->i_onMachineDataChanged(mData->mUuid);
1226 }
1227 }
1228
1229 if (SUCCEEDED(hrc))
1230 *aAccessible = mData->mAccessible;
1231
1232 LogFlowThisFuncLeave();
1233
1234 return hrc;
1235}
1236
1237HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
1238{
1239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1240
1241 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
1242 {
1243 /* return shortly */
1244 aAccessError = NULL;
1245 return S_OK;
1246 }
1247
1248 HRESULT hrc = S_OK;
1249
1250 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1251 hrc = errorInfo.createObject();
1252 if (SUCCEEDED(hrc))
1253 {
1254 errorInfo->init(mData->mAccessError.getResultCode(),
1255 mData->mAccessError.getInterfaceID().ref(),
1256 Utf8Str(mData->mAccessError.getComponent()).c_str(),
1257 Utf8Str(mData->mAccessError.getText()));
1258 aAccessError = errorInfo;
1259 }
1260
1261 return hrc;
1262}
1263
1264HRESULT Machine::getName(com::Utf8Str &aName)
1265{
1266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1267
1268 aName = mUserData->s.strName;
1269
1270 return S_OK;
1271}
1272
1273HRESULT Machine::setName(const com::Utf8Str &aName)
1274{
1275 // prohibit setting a UUID only as the machine name, or else it can
1276 // never be found by findMachine()
1277 Guid test(aName);
1278
1279 if (test.isValid())
1280 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1281
1282 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1283
1284 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
1285 if (FAILED(hrc)) return hrc;
1286
1287 i_setModified(IsModified_MachineData);
1288 mUserData.backup();
1289 mUserData->s.strName = aName;
1290
1291 return S_OK;
1292}
1293
1294HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1295{
1296 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1297
1298 aDescription = mUserData->s.strDescription;
1299
1300 return S_OK;
1301}
1302
1303HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1304{
1305 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1306
1307 // this can be done in principle in any state as it doesn't affect the VM
1308 // significantly, but play safe by not messing around while complex
1309 // activities are going on
1310 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1311 if (FAILED(hrc)) return hrc;
1312
1313 i_setModified(IsModified_MachineData);
1314 mUserData.backup();
1315 mUserData->s.strDescription = aDescription;
1316
1317 return S_OK;
1318}
1319
1320HRESULT Machine::getId(com::Guid &aId)
1321{
1322 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1323
1324 aId = mData->mUuid;
1325
1326 return S_OK;
1327}
1328
1329HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1330{
1331 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1332 aGroups.resize(mUserData->s.llGroups.size());
1333 size_t i = 0;
1334 for (StringsList::const_iterator
1335 it = mUserData->s.llGroups.begin();
1336 it != mUserData->s.llGroups.end();
1337 ++it, ++i)
1338 aGroups[i] = (*it);
1339
1340 return S_OK;
1341}
1342
1343HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1344{
1345 StringsList llGroups;
1346 HRESULT hrc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1347 if (FAILED(hrc))
1348 return hrc;
1349
1350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1351
1352 hrc = i_checkStateDependency(MutableOrSavedStateDep);
1353 if (FAILED(hrc)) return hrc;
1354
1355 i_setModified(IsModified_MachineData);
1356 mUserData.backup();
1357 mUserData->s.llGroups = llGroups;
1358
1359 mParent->i_onMachineGroupsChanged(mData->mUuid);
1360 return S_OK;
1361}
1362
1363HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1364{
1365 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1366
1367 aOSTypeId = mUserData->s.strOsType;
1368
1369 return S_OK;
1370}
1371
1372HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1373{
1374 /* look up the object by Id to check it is valid */
1375 ComObjPtr<GuestOSType> pGuestOSType;
1376 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1377
1378 /* when setting, always use the "etalon" value for consistency -- lookup
1379 * by ID is case-insensitive and the input value may have different case */
1380 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1381
1382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1383
1384 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1385 if (FAILED(hrc)) return hrc;
1386
1387 i_setModified(IsModified_MachineData);
1388 mUserData.backup();
1389 mUserData->s.strOsType = osTypeId;
1390
1391 return S_OK;
1392}
1393
1394HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1395{
1396 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1397
1398 *aFirmwareType = mHWData->mFirmwareType;
1399
1400 return S_OK;
1401}
1402
1403HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1404{
1405 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1406
1407 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1408 if (FAILED(hrc)) return hrc;
1409
1410 i_setModified(IsModified_MachineData);
1411 mHWData.backup();
1412 mHWData->mFirmwareType = aFirmwareType;
1413 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1414 alock.release();
1415
1416 mNvramStore->i_updateNonVolatileStorageFile(strNVRAM);
1417
1418 return S_OK;
1419}
1420
1421HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1422{
1423 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1424
1425 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1426
1427 return S_OK;
1428}
1429
1430HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1431{
1432 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1433
1434 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1435 if (FAILED(hrc)) return hrc;
1436
1437 i_setModified(IsModified_MachineData);
1438 mHWData.backup();
1439 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1440
1441 return S_OK;
1442}
1443
1444HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1445{
1446 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1447
1448 *aPointingHIDType = mHWData->mPointingHIDType;
1449
1450 return S_OK;
1451}
1452
1453HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1454{
1455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1456
1457 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1458 if (FAILED(hrc)) return hrc;
1459
1460 i_setModified(IsModified_MachineData);
1461 mHWData.backup();
1462 mHWData->mPointingHIDType = aPointingHIDType;
1463
1464 return S_OK;
1465}
1466
1467HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1468{
1469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1470
1471 *aChipsetType = mHWData->mChipsetType;
1472
1473 return S_OK;
1474}
1475
1476HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1477{
1478 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1479
1480 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1481 if (FAILED(hrc)) return hrc;
1482
1483 if (aChipsetType != mHWData->mChipsetType)
1484 {
1485 i_setModified(IsModified_MachineData);
1486 mHWData.backup();
1487 mHWData->mChipsetType = aChipsetType;
1488
1489 // Resize network adapter array, to be finalized on commit/rollback.
1490 // We must not throw away entries yet, otherwise settings are lost
1491 // without a way to roll back.
1492 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1493 size_t oldCount = mNetworkAdapters.size();
1494 if (newCount > oldCount)
1495 {
1496 mNetworkAdapters.resize(newCount);
1497 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1498 {
1499 unconst(mNetworkAdapters[slot]).createObject();
1500 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1501 }
1502 }
1503 }
1504
1505 return S_OK;
1506}
1507
1508HRESULT Machine::getIommuType(IommuType_T *aIommuType)
1509{
1510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1511
1512 *aIommuType = mHWData->mIommuType;
1513
1514 return S_OK;
1515}
1516
1517HRESULT Machine::setIommuType(IommuType_T aIommuType)
1518{
1519 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1520
1521 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1522 if (FAILED(hrc)) return hrc;
1523
1524 if (aIommuType != mHWData->mIommuType)
1525 {
1526 if (aIommuType == IommuType_Intel)
1527 {
1528#ifndef VBOX_WITH_IOMMU_INTEL
1529 LogRelFunc(("Setting Intel IOMMU when Intel IOMMU support not available!\n"));
1530 return E_UNEXPECTED;
1531#endif
1532 }
1533
1534 i_setModified(IsModified_MachineData);
1535 mHWData.backup();
1536 mHWData->mIommuType = aIommuType;
1537 }
1538
1539 return S_OK;
1540}
1541
1542HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1543{
1544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1545
1546 aParavirtDebug = mHWData->mParavirtDebug;
1547 return S_OK;
1548}
1549
1550HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1551{
1552 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1553
1554 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1555 if (FAILED(hrc)) return hrc;
1556
1557 /** @todo Parse/validate options? */
1558 if (aParavirtDebug != mHWData->mParavirtDebug)
1559 {
1560 i_setModified(IsModified_MachineData);
1561 mHWData.backup();
1562 mHWData->mParavirtDebug = aParavirtDebug;
1563 }
1564
1565 return S_OK;
1566}
1567
1568HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1569{
1570 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1571
1572 *aParavirtProvider = mHWData->mParavirtProvider;
1573
1574 return S_OK;
1575}
1576
1577HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1578{
1579 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1580
1581 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1582 if (FAILED(hrc)) return hrc;
1583
1584 if (aParavirtProvider != mHWData->mParavirtProvider)
1585 {
1586 i_setModified(IsModified_MachineData);
1587 mHWData.backup();
1588 mHWData->mParavirtProvider = aParavirtProvider;
1589 }
1590
1591 return S_OK;
1592}
1593
1594HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1595{
1596 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1597
1598 *aParavirtProvider = mHWData->mParavirtProvider;
1599 switch (mHWData->mParavirtProvider)
1600 {
1601 case ParavirtProvider_None:
1602 case ParavirtProvider_HyperV:
1603 case ParavirtProvider_KVM:
1604 case ParavirtProvider_Minimal:
1605 break;
1606
1607 /* Resolve dynamic provider types to the effective types. */
1608 default:
1609 {
1610 ComObjPtr<GuestOSType> pGuestOSType;
1611 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1612 pGuestOSType);
1613 if (FAILED(hrc2) || pGuestOSType.isNull())
1614 {
1615 *aParavirtProvider = ParavirtProvider_None;
1616 break;
1617 }
1618
1619 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1620 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1621
1622 switch (mHWData->mParavirtProvider)
1623 {
1624 case ParavirtProvider_Legacy:
1625 {
1626 if (fOsXGuest)
1627 *aParavirtProvider = ParavirtProvider_Minimal;
1628 else
1629 *aParavirtProvider = ParavirtProvider_None;
1630 break;
1631 }
1632
1633 case ParavirtProvider_Default:
1634 {
1635 if (fOsXGuest)
1636 *aParavirtProvider = ParavirtProvider_Minimal;
1637 else if ( mUserData->s.strOsType == "Windows11_64"
1638 || mUserData->s.strOsType == "Windows10"
1639 || mUserData->s.strOsType == "Windows10_64"
1640 || mUserData->s.strOsType == "Windows81"
1641 || mUserData->s.strOsType == "Windows81_64"
1642 || mUserData->s.strOsType == "Windows8"
1643 || mUserData->s.strOsType == "Windows8_64"
1644 || mUserData->s.strOsType == "Windows7"
1645 || mUserData->s.strOsType == "Windows7_64"
1646 || mUserData->s.strOsType == "WindowsVista"
1647 || mUserData->s.strOsType == "WindowsVista_64"
1648 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1649 || mUserData->s.strOsType.startsWith("Windows201"))
1650 && mUserData->s.strOsType.endsWith("_64"))
1651 || mUserData->s.strOsType == "Windows2012"
1652 || mUserData->s.strOsType == "Windows2012_64"
1653 || mUserData->s.strOsType == "Windows2008"
1654 || mUserData->s.strOsType == "Windows2008_64")
1655 {
1656 *aParavirtProvider = ParavirtProvider_HyperV;
1657 }
1658 else if (guestTypeFamilyId == "Linux" &&
1659 mUserData->s.strOsType != "Linux22" && // Linux22 and Linux24{_64} excluded as they're too old
1660 mUserData->s.strOsType != "Linux24" && // to have any KVM paravirtualization support.
1661 mUserData->s.strOsType != "Linux24_64")
1662 {
1663 *aParavirtProvider = ParavirtProvider_KVM;
1664 }
1665 else
1666 *aParavirtProvider = ParavirtProvider_None;
1667 break;
1668 }
1669
1670 default: AssertFailedBreak(); /* Shut up MSC. */
1671 }
1672 break;
1673 }
1674 }
1675
1676 Assert( *aParavirtProvider == ParavirtProvider_None
1677 || *aParavirtProvider == ParavirtProvider_Minimal
1678 || *aParavirtProvider == ParavirtProvider_HyperV
1679 || *aParavirtProvider == ParavirtProvider_KVM);
1680 return S_OK;
1681}
1682
1683HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1684{
1685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1686
1687 aHardwareVersion = mHWData->mHWVersion;
1688
1689 return S_OK;
1690}
1691
1692HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1693{
1694 /* check known version */
1695 Utf8Str hwVersion = aHardwareVersion;
1696 if ( hwVersion.compare("1") != 0
1697 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1698 return setError(E_INVALIDARG,
1699 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1700
1701 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1702
1703 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1704 if (FAILED(hrc)) return hrc;
1705
1706 i_setModified(IsModified_MachineData);
1707 mHWData.backup();
1708 mHWData->mHWVersion = aHardwareVersion;
1709
1710 return S_OK;
1711}
1712
1713HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1714{
1715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1716
1717 if (!mHWData->mHardwareUUID.isZero())
1718 aHardwareUUID = mHWData->mHardwareUUID;
1719 else
1720 aHardwareUUID = mData->mUuid;
1721
1722 return S_OK;
1723}
1724
1725HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1726{
1727 if (!aHardwareUUID.isValid())
1728 return E_INVALIDARG;
1729
1730 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1731
1732 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1733 if (FAILED(hrc)) return hrc;
1734
1735 i_setModified(IsModified_MachineData);
1736 mHWData.backup();
1737 if (aHardwareUUID == mData->mUuid)
1738 mHWData->mHardwareUUID.clear();
1739 else
1740 mHWData->mHardwareUUID = aHardwareUUID;
1741
1742 return S_OK;
1743}
1744
1745HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1746{
1747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1748
1749 *aMemorySize = mHWData->mMemorySize;
1750
1751 return S_OK;
1752}
1753
1754HRESULT Machine::setMemorySize(ULONG aMemorySize)
1755{
1756 /* check RAM limits */
1757 if ( aMemorySize < MM_RAM_MIN_IN_MB
1758 || aMemorySize > MM_RAM_MAX_IN_MB
1759 )
1760 return setError(E_INVALIDARG,
1761 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1762 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1763
1764 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1765
1766 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1767 if (FAILED(hrc)) return hrc;
1768
1769 i_setModified(IsModified_MachineData);
1770 mHWData.backup();
1771 mHWData->mMemorySize = aMemorySize;
1772
1773 return S_OK;
1774}
1775
1776HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1777{
1778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1779
1780 *aCPUCount = mHWData->mCPUCount;
1781
1782 return S_OK;
1783}
1784
1785HRESULT Machine::setCPUCount(ULONG aCPUCount)
1786{
1787 /* check CPU limits */
1788 if ( aCPUCount < SchemaDefs::MinCPUCount
1789 || aCPUCount > SchemaDefs::MaxCPUCount
1790 )
1791 return setError(E_INVALIDARG,
1792 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1793 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1794
1795 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1796
1797 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1798 if (mHWData->mCPUHotPlugEnabled)
1799 {
1800 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1801 {
1802 if (mHWData->mCPUAttached[idx])
1803 return setError(E_INVALIDARG,
1804 tr("There is still a CPU attached to socket %lu."
1805 "Detach the CPU before removing the socket"),
1806 aCPUCount, idx+1);
1807 }
1808 }
1809
1810 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1811 if (FAILED(hrc)) return hrc;
1812
1813 i_setModified(IsModified_MachineData);
1814 mHWData.backup();
1815 mHWData->mCPUCount = aCPUCount;
1816
1817 return S_OK;
1818}
1819
1820HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1821{
1822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1823
1824 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1825
1826 return S_OK;
1827}
1828
1829HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1830{
1831 /* check throttle limits */
1832 if ( aCPUExecutionCap < 1
1833 || aCPUExecutionCap > 100
1834 )
1835 return setError(E_INVALIDARG,
1836 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1837 aCPUExecutionCap, 1, 100);
1838
1839 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1840
1841 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
1842 if (FAILED(hrc)) return hrc;
1843
1844 alock.release();
1845 hrc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1846 alock.acquire();
1847 if (FAILED(hrc)) return hrc;
1848
1849 i_setModified(IsModified_MachineData);
1850 mHWData.backup();
1851 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1852
1853 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1854 if (Global::IsOnline(mData->mMachineState))
1855 i_saveSettings(NULL, alock);
1856
1857 return S_OK;
1858}
1859
1860HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1861{
1862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1863
1864 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1865
1866 return S_OK;
1867}
1868
1869HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1870{
1871 HRESULT hrc = S_OK;
1872
1873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1874
1875 hrc = i_checkStateDependency(MutableStateDep);
1876 if (FAILED(hrc)) return hrc;
1877
1878 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1879 {
1880 if (aCPUHotPlugEnabled)
1881 {
1882 i_setModified(IsModified_MachineData);
1883 mHWData.backup();
1884
1885 /* Add the amount of CPUs currently attached */
1886 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1887 mHWData->mCPUAttached[i] = true;
1888 }
1889 else
1890 {
1891 /*
1892 * We can disable hotplug only if the amount of maximum CPUs is equal
1893 * to the amount of attached CPUs
1894 */
1895 unsigned cCpusAttached = 0;
1896 unsigned iHighestId = 0;
1897
1898 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1899 {
1900 if (mHWData->mCPUAttached[i])
1901 {
1902 cCpusAttached++;
1903 iHighestId = i;
1904 }
1905 }
1906
1907 if ( (cCpusAttached != mHWData->mCPUCount)
1908 || (iHighestId >= mHWData->mCPUCount))
1909 return setError(E_INVALIDARG,
1910 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1911
1912 i_setModified(IsModified_MachineData);
1913 mHWData.backup();
1914 }
1915 }
1916
1917 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1918
1919 return hrc;
1920}
1921
1922HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1923{
1924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1925
1926 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1927
1928 return S_OK;
1929}
1930
1931HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1932{
1933 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1934
1935 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1936 if (SUCCEEDED(hrc))
1937 {
1938 i_setModified(IsModified_MachineData);
1939 mHWData.backup();
1940 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1941 }
1942 return hrc;
1943}
1944
1945HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1946{
1947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1948 aCPUProfile = mHWData->mCpuProfile;
1949 return S_OK;
1950}
1951
1952HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1953{
1954 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1955 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1956 if (SUCCEEDED(hrc))
1957 {
1958 i_setModified(IsModified_MachineData);
1959 mHWData.backup();
1960 /* Empty equals 'host'. */
1961 if (aCPUProfile.isNotEmpty())
1962 mHWData->mCpuProfile = aCPUProfile;
1963 else
1964 mHWData->mCpuProfile = "host";
1965 }
1966 return hrc;
1967}
1968
1969HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1970{
1971#ifdef VBOX_WITH_USB_CARDREADER
1972 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1973
1974 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1975
1976 return S_OK;
1977#else
1978 NOREF(aEmulatedUSBCardReaderEnabled);
1979 return E_NOTIMPL;
1980#endif
1981}
1982
1983HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1984{
1985#ifdef VBOX_WITH_USB_CARDREADER
1986 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1987
1988 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
1989 if (FAILED(hrc)) return hrc;
1990
1991 i_setModified(IsModified_MachineData);
1992 mHWData.backup();
1993 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1994
1995 return S_OK;
1996#else
1997 NOREF(aEmulatedUSBCardReaderEnabled);
1998 return E_NOTIMPL;
1999#endif
2000}
2001
2002HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
2003{
2004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2005
2006 *aHPETEnabled = mHWData->mHPETEnabled;
2007
2008 return S_OK;
2009}
2010
2011HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
2012{
2013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2014
2015 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2016 if (FAILED(hrc)) return hrc;
2017
2018 i_setModified(IsModified_MachineData);
2019 mHWData.backup();
2020
2021 mHWData->mHPETEnabled = aHPETEnabled;
2022
2023 return hrc;
2024}
2025
2026/** @todo this method should not be public */
2027HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2028{
2029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2030
2031 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2032
2033 return S_OK;
2034}
2035
2036/**
2037 * Set the memory balloon size.
2038 *
2039 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2040 * we have to make sure that we never call IGuest from here.
2041 */
2042HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2043{
2044 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2045#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2046 /* check limits */
2047 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2048 return setError(E_INVALIDARG,
2049 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2050 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2051
2052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2053
2054 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
2055 if (FAILED(hrc)) return hrc;
2056
2057 i_setModified(IsModified_MachineData);
2058 mHWData.backup();
2059 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2060
2061 return S_OK;
2062#else
2063 NOREF(aMemoryBalloonSize);
2064 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2065#endif
2066}
2067
2068HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2069{
2070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2071
2072 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2073 return S_OK;
2074}
2075
2076HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2077{
2078#ifdef VBOX_WITH_PAGE_SHARING
2079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2080
2081 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2082 if (FAILED(hrc)) return hrc;
2083
2084 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2085 i_setModified(IsModified_MachineData);
2086 mHWData.backup();
2087 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2088 return S_OK;
2089#else
2090 NOREF(aPageFusionEnabled);
2091 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2092#endif
2093}
2094
2095HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2096{
2097 /* mBIOSSettings is constant during life time, no need to lock */
2098 aBIOSSettings = mBIOSSettings;
2099
2100 return S_OK;
2101}
2102
2103HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
2104{
2105 /* mTrustedPlatformModule is constant during life time, no need to lock */
2106 aTrustedPlatformModule = mTrustedPlatformModule;
2107
2108 return S_OK;
2109}
2110
2111HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
2112{
2113 /* mNvramStore is constant during life time, no need to lock */
2114 aNvramStore = mNvramStore;
2115
2116 return S_OK;
2117}
2118
2119HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
2120{
2121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2122
2123 aRecordingSettings = mRecordingSettings;
2124
2125 return S_OK;
2126}
2127
2128HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
2129{
2130 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2131
2132 aGraphicsAdapter = mGraphicsAdapter;
2133
2134 return S_OK;
2135}
2136
2137HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2138{
2139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2140
2141 switch (aProperty)
2142 {
2143 case CPUPropertyType_PAE:
2144 *aValue = mHWData->mPAEEnabled;
2145 break;
2146
2147 case CPUPropertyType_LongMode:
2148 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2149 *aValue = TRUE;
2150 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2151 *aValue = FALSE;
2152#if HC_ARCH_BITS == 64
2153 else
2154 *aValue = TRUE;
2155#else
2156 else
2157 {
2158 *aValue = FALSE;
2159
2160 ComObjPtr<GuestOSType> pGuestOSType;
2161 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2162 pGuestOSType);
2163 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
2164 {
2165 if (pGuestOSType->i_is64Bit())
2166 {
2167 ComObjPtr<Host> pHost = mParent->i_host();
2168 alock.release();
2169
2170 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2171 if (FAILED(hrc2))
2172 *aValue = FALSE;
2173 }
2174 }
2175 }
2176#endif
2177 break;
2178
2179 case CPUPropertyType_TripleFaultReset:
2180 *aValue = mHWData->mTripleFaultReset;
2181 break;
2182
2183 case CPUPropertyType_APIC:
2184 *aValue = mHWData->mAPIC;
2185 break;
2186
2187 case CPUPropertyType_X2APIC:
2188 *aValue = mHWData->mX2APIC;
2189 break;
2190
2191 case CPUPropertyType_IBPBOnVMExit:
2192 *aValue = mHWData->mIBPBOnVMExit;
2193 break;
2194
2195 case CPUPropertyType_IBPBOnVMEntry:
2196 *aValue = mHWData->mIBPBOnVMEntry;
2197 break;
2198
2199 case CPUPropertyType_SpecCtrl:
2200 *aValue = mHWData->mSpecCtrl;
2201 break;
2202
2203 case CPUPropertyType_SpecCtrlByHost:
2204 *aValue = mHWData->mSpecCtrlByHost;
2205 break;
2206
2207 case CPUPropertyType_HWVirt:
2208 *aValue = mHWData->mNestedHWVirt;
2209 break;
2210
2211 case CPUPropertyType_L1DFlushOnEMTScheduling:
2212 *aValue = mHWData->mL1DFlushOnSched;
2213 break;
2214
2215 case CPUPropertyType_L1DFlushOnVMEntry:
2216 *aValue = mHWData->mL1DFlushOnVMEntry;
2217 break;
2218
2219 case CPUPropertyType_MDSClearOnEMTScheduling:
2220 *aValue = mHWData->mMDSClearOnSched;
2221 break;
2222
2223 case CPUPropertyType_MDSClearOnVMEntry:
2224 *aValue = mHWData->mMDSClearOnVMEntry;
2225 break;
2226
2227 default:
2228 return E_INVALIDARG;
2229 }
2230 return S_OK;
2231}
2232
2233HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2234{
2235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2236
2237 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2238 if (FAILED(hrc)) return hrc;
2239
2240 switch (aProperty)
2241 {
2242 case CPUPropertyType_PAE:
2243 i_setModified(IsModified_MachineData);
2244 mHWData.backup();
2245 mHWData->mPAEEnabled = !!aValue;
2246 break;
2247
2248 case CPUPropertyType_LongMode:
2249 i_setModified(IsModified_MachineData);
2250 mHWData.backup();
2251 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2252 break;
2253
2254 case CPUPropertyType_TripleFaultReset:
2255 i_setModified(IsModified_MachineData);
2256 mHWData.backup();
2257 mHWData->mTripleFaultReset = !!aValue;
2258 break;
2259
2260 case CPUPropertyType_APIC:
2261 if (mHWData->mX2APIC)
2262 aValue = TRUE;
2263 i_setModified(IsModified_MachineData);
2264 mHWData.backup();
2265 mHWData->mAPIC = !!aValue;
2266 break;
2267
2268 case CPUPropertyType_X2APIC:
2269 i_setModified(IsModified_MachineData);
2270 mHWData.backup();
2271 mHWData->mX2APIC = !!aValue;
2272 if (aValue)
2273 mHWData->mAPIC = !!aValue;
2274 break;
2275
2276 case CPUPropertyType_IBPBOnVMExit:
2277 i_setModified(IsModified_MachineData);
2278 mHWData.backup();
2279 mHWData->mIBPBOnVMExit = !!aValue;
2280 break;
2281
2282 case CPUPropertyType_IBPBOnVMEntry:
2283 i_setModified(IsModified_MachineData);
2284 mHWData.backup();
2285 mHWData->mIBPBOnVMEntry = !!aValue;
2286 break;
2287
2288 case CPUPropertyType_SpecCtrl:
2289 i_setModified(IsModified_MachineData);
2290 mHWData.backup();
2291 mHWData->mSpecCtrl = !!aValue;
2292 break;
2293
2294 case CPUPropertyType_SpecCtrlByHost:
2295 i_setModified(IsModified_MachineData);
2296 mHWData.backup();
2297 mHWData->mSpecCtrlByHost = !!aValue;
2298 break;
2299
2300 case CPUPropertyType_HWVirt:
2301 i_setModified(IsModified_MachineData);
2302 mHWData.backup();
2303 mHWData->mNestedHWVirt = !!aValue;
2304 break;
2305
2306 case CPUPropertyType_L1DFlushOnEMTScheduling:
2307 i_setModified(IsModified_MachineData);
2308 mHWData.backup();
2309 mHWData->mL1DFlushOnSched = !!aValue;
2310 break;
2311
2312 case CPUPropertyType_L1DFlushOnVMEntry:
2313 i_setModified(IsModified_MachineData);
2314 mHWData.backup();
2315 mHWData->mL1DFlushOnVMEntry = !!aValue;
2316 break;
2317
2318 case CPUPropertyType_MDSClearOnEMTScheduling:
2319 i_setModified(IsModified_MachineData);
2320 mHWData.backup();
2321 mHWData->mMDSClearOnSched = !!aValue;
2322 break;
2323
2324 case CPUPropertyType_MDSClearOnVMEntry:
2325 i_setModified(IsModified_MachineData);
2326 mHWData.backup();
2327 mHWData->mMDSClearOnVMEntry = !!aValue;
2328 break;
2329
2330 default:
2331 return E_INVALIDARG;
2332 }
2333 return S_OK;
2334}
2335
2336HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2337 ULONG *aValEcx, ULONG *aValEdx)
2338{
2339 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2340 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2341 {
2342 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2343 it != mHWData->mCpuIdLeafList.end();
2344 ++it)
2345 {
2346 if (aOrdinal == 0)
2347 {
2348 const settings::CpuIdLeaf &rLeaf= *it;
2349 *aIdx = rLeaf.idx;
2350 *aSubIdx = rLeaf.idxSub;
2351 *aValEax = rLeaf.uEax;
2352 *aValEbx = rLeaf.uEbx;
2353 *aValEcx = rLeaf.uEcx;
2354 *aValEdx = rLeaf.uEdx;
2355 return S_OK;
2356 }
2357 aOrdinal--;
2358 }
2359 }
2360 return E_INVALIDARG;
2361}
2362
2363HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2364{
2365 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2366
2367 /*
2368 * Search the list.
2369 */
2370 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2371 {
2372 const settings::CpuIdLeaf &rLeaf= *it;
2373 if ( rLeaf.idx == aIdx
2374 && ( aSubIdx == UINT32_MAX
2375 || rLeaf.idxSub == aSubIdx) )
2376 {
2377 *aValEax = rLeaf.uEax;
2378 *aValEbx = rLeaf.uEbx;
2379 *aValEcx = rLeaf.uEcx;
2380 *aValEdx = rLeaf.uEdx;
2381 return S_OK;
2382 }
2383 }
2384
2385 return E_INVALIDARG;
2386}
2387
2388
2389HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2390{
2391 /*
2392 * Validate input before taking locks and checking state.
2393 */
2394 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2395 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2396 if ( aIdx >= UINT32_C(0x20)
2397 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2398 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2399 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2400
2401 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2402 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2403 if (FAILED(hrc)) return hrc;
2404
2405 /*
2406 * Impose a maximum number of leaves.
2407 */
2408 if (mHWData->mCpuIdLeafList.size() > 256)
2409 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2410
2411 /*
2412 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2413 */
2414 i_setModified(IsModified_MachineData);
2415 mHWData.backup();
2416
2417 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2418 {
2419 settings::CpuIdLeaf &rLeaf= *it;
2420 if ( rLeaf.idx == aIdx
2421 && ( aSubIdx == UINT32_MAX
2422 || rLeaf.idxSub == aSubIdx) )
2423 it = mHWData->mCpuIdLeafList.erase(it);
2424 else
2425 ++it;
2426 }
2427
2428 settings::CpuIdLeaf NewLeaf;
2429 NewLeaf.idx = aIdx;
2430 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2431 NewLeaf.uEax = aValEax;
2432 NewLeaf.uEbx = aValEbx;
2433 NewLeaf.uEcx = aValEcx;
2434 NewLeaf.uEdx = aValEdx;
2435 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2436 return S_OK;
2437}
2438
2439HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2440{
2441 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2442
2443 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2444 if (FAILED(hrc)) return hrc;
2445
2446 /*
2447 * Do the removal.
2448 */
2449 bool fModified = mHWData.isBackedUp();
2450 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2451 {
2452 settings::CpuIdLeaf &rLeaf= *it;
2453 if ( rLeaf.idx == aIdx
2454 && ( aSubIdx == UINT32_MAX
2455 || rLeaf.idxSub == aSubIdx) )
2456 {
2457 if (!fModified)
2458 {
2459 fModified = true;
2460 i_setModified(IsModified_MachineData);
2461 mHWData.backup();
2462 // Start from the beginning, since mHWData.backup() creates
2463 // a new list, causing iterator mixup. This makes sure that
2464 // the settings are not unnecessarily marked as modified,
2465 // at the price of extra list walking.
2466 it = mHWData->mCpuIdLeafList.begin();
2467 }
2468 else
2469 it = mHWData->mCpuIdLeafList.erase(it);
2470 }
2471 else
2472 ++it;
2473 }
2474
2475 return S_OK;
2476}
2477
2478HRESULT Machine::removeAllCPUIDLeaves()
2479{
2480 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2481
2482 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2483 if (FAILED(hrc)) return hrc;
2484
2485 if (mHWData->mCpuIdLeafList.size() > 0)
2486 {
2487 i_setModified(IsModified_MachineData);
2488 mHWData.backup();
2489
2490 mHWData->mCpuIdLeafList.clear();
2491 }
2492
2493 return S_OK;
2494}
2495HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2496{
2497 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2498
2499 switch(aProperty)
2500 {
2501 case HWVirtExPropertyType_Enabled:
2502 *aValue = mHWData->mHWVirtExEnabled;
2503 break;
2504
2505 case HWVirtExPropertyType_VPID:
2506 *aValue = mHWData->mHWVirtExVPIDEnabled;
2507 break;
2508
2509 case HWVirtExPropertyType_NestedPaging:
2510 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2511 break;
2512
2513 case HWVirtExPropertyType_UnrestrictedExecution:
2514 *aValue = mHWData->mHWVirtExUXEnabled;
2515 break;
2516
2517 case HWVirtExPropertyType_LargePages:
2518 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2519 break;
2520
2521 case HWVirtExPropertyType_Force:
2522 *aValue = mHWData->mHWVirtExForceEnabled;
2523 break;
2524
2525 case HWVirtExPropertyType_UseNativeApi:
2526 *aValue = mHWData->mHWVirtExUseNativeApi;
2527 break;
2528
2529 case HWVirtExPropertyType_VirtVmsaveVmload:
2530 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2531 break;
2532
2533 default:
2534 return E_INVALIDARG;
2535 }
2536 return S_OK;
2537}
2538
2539HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2540{
2541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2542
2543 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2544 if (FAILED(hrc)) return hrc;
2545
2546 switch (aProperty)
2547 {
2548 case HWVirtExPropertyType_Enabled:
2549 i_setModified(IsModified_MachineData);
2550 mHWData.backup();
2551 mHWData->mHWVirtExEnabled = !!aValue;
2552 break;
2553
2554 case HWVirtExPropertyType_VPID:
2555 i_setModified(IsModified_MachineData);
2556 mHWData.backup();
2557 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2558 break;
2559
2560 case HWVirtExPropertyType_NestedPaging:
2561 i_setModified(IsModified_MachineData);
2562 mHWData.backup();
2563 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2564 break;
2565
2566 case HWVirtExPropertyType_UnrestrictedExecution:
2567 i_setModified(IsModified_MachineData);
2568 mHWData.backup();
2569 mHWData->mHWVirtExUXEnabled = !!aValue;
2570 break;
2571
2572 case HWVirtExPropertyType_LargePages:
2573 i_setModified(IsModified_MachineData);
2574 mHWData.backup();
2575 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2576 break;
2577
2578 case HWVirtExPropertyType_Force:
2579 i_setModified(IsModified_MachineData);
2580 mHWData.backup();
2581 mHWData->mHWVirtExForceEnabled = !!aValue;
2582 break;
2583
2584 case HWVirtExPropertyType_UseNativeApi:
2585 i_setModified(IsModified_MachineData);
2586 mHWData.backup();
2587 mHWData->mHWVirtExUseNativeApi = !!aValue;
2588 break;
2589
2590 case HWVirtExPropertyType_VirtVmsaveVmload:
2591 i_setModified(IsModified_MachineData);
2592 mHWData.backup();
2593 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2594 break;
2595
2596 default:
2597 return E_INVALIDARG;
2598 }
2599
2600 return S_OK;
2601}
2602
2603HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2604{
2605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2606
2607 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2608
2609 return S_OK;
2610}
2611
2612HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2613{
2614 /** @todo (r=dmik):
2615 * 1. Allow to change the name of the snapshot folder containing snapshots
2616 * 2. Rename the folder on disk instead of just changing the property
2617 * value (to be smart and not to leave garbage). Note that it cannot be
2618 * done here because the change may be rolled back. Thus, the right
2619 * place is #saveSettings().
2620 */
2621
2622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2623
2624 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2625 if (FAILED(hrc)) return hrc;
2626
2627 if (!mData->mCurrentSnapshot.isNull())
2628 return setError(E_FAIL,
2629 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2630
2631 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2632
2633 if (strSnapshotFolder.isEmpty())
2634 strSnapshotFolder = "Snapshots";
2635 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2636 if (RT_FAILURE(vrc))
2637 return setErrorBoth(E_FAIL, vrc,
2638 tr("Invalid snapshot folder '%s' (%Rrc)"),
2639 strSnapshotFolder.c_str(), vrc);
2640
2641 i_setModified(IsModified_MachineData);
2642 mUserData.backup();
2643
2644 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2645
2646 return S_OK;
2647}
2648
2649HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2650{
2651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2652
2653 aMediumAttachments.resize(mMediumAttachments->size());
2654 size_t i = 0;
2655 for (MediumAttachmentList::const_iterator
2656 it = mMediumAttachments->begin();
2657 it != mMediumAttachments->end();
2658 ++it, ++i)
2659 aMediumAttachments[i] = *it;
2660
2661 return S_OK;
2662}
2663
2664HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2665{
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 Assert(!!mVRDEServer);
2669
2670 aVRDEServer = mVRDEServer;
2671
2672 return S_OK;
2673}
2674
2675HRESULT Machine::getAudioSettings(ComPtr<IAudioSettings> &aAudioSettings)
2676{
2677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2678
2679 aAudioSettings = mAudioSettings;
2680
2681 return S_OK;
2682}
2683
2684HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2685{
2686#ifdef VBOX_WITH_VUSB
2687 clearError();
2688 MultiResult hrcMult(S_OK);
2689
2690# ifdef VBOX_WITH_USB
2691 hrcMult = mParent->i_host()->i_checkUSBProxyService();
2692 if (FAILED(hrcMult)) return hrcMult;
2693# endif
2694
2695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2696
2697 aUSBControllers.resize(mUSBControllers->size());
2698 size_t i = 0;
2699 for (USBControllerList::const_iterator
2700 it = mUSBControllers->begin();
2701 it != mUSBControllers->end();
2702 ++it, ++i)
2703 aUSBControllers[i] = *it;
2704
2705 return S_OK;
2706#else
2707 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2708 * extended error info to indicate that USB is simply not available
2709 * (w/o treating it as a failure), for example, as in OSE */
2710 NOREF(aUSBControllers);
2711 ReturnComNotImplemented();
2712#endif /* VBOX_WITH_VUSB */
2713}
2714
2715HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2716{
2717#ifdef VBOX_WITH_VUSB
2718 clearError();
2719 MultiResult hrcMult(S_OK);
2720
2721# ifdef VBOX_WITH_USB
2722 hrcMult = mParent->i_host()->i_checkUSBProxyService();
2723 if (FAILED(hrcMult)) return hrcMult;
2724# endif
2725
2726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2727
2728 aUSBDeviceFilters = mUSBDeviceFilters;
2729 return hrcMult;
2730#else
2731 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2732 * extended error info to indicate that USB is simply not available
2733 * (w/o treating it as a failure), for example, as in OSE */
2734 NOREF(aUSBDeviceFilters);
2735 ReturnComNotImplemented();
2736#endif /* VBOX_WITH_VUSB */
2737}
2738
2739HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2740{
2741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2742
2743 aSettingsFilePath = mData->m_strConfigFileFull;
2744
2745 return S_OK;
2746}
2747
2748HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2749{
2750 RT_NOREF(aSettingsFilePath);
2751 ReturnComNotImplemented();
2752}
2753
2754HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2755{
2756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2757
2758 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2759 if (FAILED(hrc)) return hrc;
2760
2761 if (!mData->pMachineConfigFile->fileExists())
2762 // this is a new machine, and no config file exists yet:
2763 *aSettingsModified = TRUE;
2764 else
2765 *aSettingsModified = (mData->flModifications != 0);
2766
2767 return S_OK;
2768}
2769
2770HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2771{
2772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2773
2774 *aSessionState = mData->mSession.mState;
2775
2776 return S_OK;
2777}
2778
2779HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2780{
2781 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2782
2783 aSessionName = mData->mSession.mName;
2784
2785 return S_OK;
2786}
2787
2788HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2789{
2790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2791
2792 *aSessionPID = mData->mSession.mPID;
2793
2794 return S_OK;
2795}
2796
2797HRESULT Machine::getState(MachineState_T *aState)
2798{
2799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2800
2801 *aState = mData->mMachineState;
2802 Assert(mData->mMachineState != MachineState_Null);
2803
2804 return S_OK;
2805}
2806
2807HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2808{
2809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2810
2811 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2812
2813 return S_OK;
2814}
2815
2816HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2817{
2818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2819
2820 aStateFilePath = mSSData->strStateFilePath;
2821
2822 return S_OK;
2823}
2824
2825HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2826{
2827 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2828
2829 i_getLogFolder(aLogFolder);
2830
2831 return S_OK;
2832}
2833
2834HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2835{
2836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2837
2838 aCurrentSnapshot = mData->mCurrentSnapshot;
2839
2840 return S_OK;
2841}
2842
2843HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2844{
2845 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2846
2847 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2848 ? 0
2849 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2850
2851 return S_OK;
2852}
2853
2854HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2855{
2856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2857
2858 /* Note: for machines with no snapshots, we always return FALSE
2859 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2860 * reasons :) */
2861
2862 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2863 ? FALSE
2864 : mData->mCurrentStateModified;
2865
2866 return S_OK;
2867}
2868
2869HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2870{
2871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2872
2873 aSharedFolders.resize(mHWData->mSharedFolders.size());
2874 size_t i = 0;
2875 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2876 it = mHWData->mSharedFolders.begin();
2877 it != mHWData->mSharedFolders.end();
2878 ++it, ++i)
2879 aSharedFolders[i] = *it;
2880
2881 return S_OK;
2882}
2883
2884HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2885{
2886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2887
2888 *aClipboardMode = mHWData->mClipboardMode;
2889
2890 return S_OK;
2891}
2892
2893HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2894{
2895 HRESULT hrc = S_OK;
2896
2897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2898
2899 hrc = i_checkStateDependency(MutableOrRunningStateDep);
2900 if (FAILED(hrc)) return hrc;
2901
2902 alock.release();
2903 hrc = i_onClipboardModeChange(aClipboardMode);
2904 alock.acquire();
2905 if (FAILED(hrc)) return hrc;
2906
2907 i_setModified(IsModified_MachineData);
2908 mHWData.backup();
2909 mHWData->mClipboardMode = aClipboardMode;
2910
2911 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2912 if (Global::IsOnline(mData->mMachineState))
2913 i_saveSettings(NULL, alock);
2914
2915 return S_OK;
2916}
2917
2918HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2919{
2920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2921
2922 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2923
2924 return S_OK;
2925}
2926
2927HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2928{
2929 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2930
2931 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
2932 if (FAILED(hrc)) return hrc;
2933
2934 alock.release();
2935 hrc = i_onClipboardFileTransferModeChange(aEnabled);
2936 alock.acquire();
2937 if (FAILED(hrc)) return hrc;
2938
2939 i_setModified(IsModified_MachineData);
2940 mHWData.backup();
2941 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2942
2943 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2944 if (Global::IsOnline(mData->mMachineState))
2945 i_saveSettings(NULL, alock);
2946
2947 return S_OK;
2948}
2949
2950HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2951{
2952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2953
2954 *aDnDMode = mHWData->mDnDMode;
2955
2956 return S_OK;
2957}
2958
2959HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2960{
2961 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2962
2963 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
2964 if (FAILED(hrc)) return hrc;
2965
2966 alock.release();
2967 hrc = i_onDnDModeChange(aDnDMode);
2968
2969 alock.acquire();
2970 if (FAILED(hrc)) return hrc;
2971
2972 i_setModified(IsModified_MachineData);
2973 mHWData.backup();
2974 mHWData->mDnDMode = aDnDMode;
2975
2976 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2977 if (Global::IsOnline(mData->mMachineState))
2978 i_saveSettings(NULL, alock);
2979
2980 return S_OK;
2981}
2982
2983HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2984{
2985 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2986
2987 aStorageControllers.resize(mStorageControllers->size());
2988 size_t i = 0;
2989 for (StorageControllerList::const_iterator
2990 it = mStorageControllers->begin();
2991 it != mStorageControllers->end();
2992 ++it, ++i)
2993 aStorageControllers[i] = *it;
2994
2995 return S_OK;
2996}
2997
2998HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2999{
3000 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3001
3002 *aEnabled = mUserData->s.fTeleporterEnabled;
3003
3004 return S_OK;
3005}
3006
3007HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
3008{
3009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3010
3011 /* Only allow it to be set to true when PoweredOff or Aborted.
3012 (Clearing it is always permitted.) */
3013 if ( aTeleporterEnabled
3014 && mData->mRegistered
3015 && ( !i_isSessionMachine()
3016 || ( mData->mMachineState != MachineState_PoweredOff
3017 && mData->mMachineState != MachineState_Teleported
3018 && mData->mMachineState != MachineState_Aborted
3019 )
3020 )
3021 )
3022 return setError(VBOX_E_INVALID_VM_STATE,
3023 tr("The machine is not powered off (state is %s)"),
3024 Global::stringifyMachineState(mData->mMachineState));
3025
3026 i_setModified(IsModified_MachineData);
3027 mUserData.backup();
3028 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
3029
3030 return S_OK;
3031}
3032
3033HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
3034{
3035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3036
3037 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
3038
3039 return S_OK;
3040}
3041
3042HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
3043{
3044 if (aTeleporterPort >= _64K)
3045 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
3046
3047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3048
3049 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3050 if (FAILED(hrc)) return hrc;
3051
3052 i_setModified(IsModified_MachineData);
3053 mUserData.backup();
3054 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3055
3056 return S_OK;
3057}
3058
3059HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3060{
3061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3062
3063 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3064
3065 return S_OK;
3066}
3067
3068HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3069{
3070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3071
3072 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3073 if (FAILED(hrc)) return hrc;
3074
3075 i_setModified(IsModified_MachineData);
3076 mUserData.backup();
3077 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3078
3079 return S_OK;
3080}
3081
3082HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3083{
3084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3085 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3086
3087 return S_OK;
3088}
3089
3090HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3091{
3092 /*
3093 * Hash the password first.
3094 */
3095 com::Utf8Str aT = aTeleporterPassword;
3096
3097 if (!aT.isEmpty())
3098 {
3099 if (VBoxIsPasswordHashed(&aT))
3100 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3101 VBoxHashPassword(&aT);
3102 }
3103
3104 /*
3105 * Do the update.
3106 */
3107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3108 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3109 if (SUCCEEDED(hrc))
3110 {
3111 i_setModified(IsModified_MachineData);
3112 mUserData.backup();
3113 mUserData->s.strTeleporterPassword = aT;
3114 }
3115
3116 return hrc;
3117}
3118
3119HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3120{
3121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3122
3123 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3124
3125 return S_OK;
3126}
3127
3128HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3129{
3130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3131
3132 /* Only allow it to be set to true when PoweredOff or Aborted.
3133 (Clearing it is always permitted.) */
3134 if ( aRTCUseUTC
3135 && mData->mRegistered
3136 && ( !i_isSessionMachine()
3137 || ( mData->mMachineState != MachineState_PoweredOff
3138 && mData->mMachineState != MachineState_Teleported
3139 && mData->mMachineState != MachineState_Aborted
3140 )
3141 )
3142 )
3143 return setError(VBOX_E_INVALID_VM_STATE,
3144 tr("The machine is not powered off (state is %s)"),
3145 Global::stringifyMachineState(mData->mMachineState));
3146
3147 i_setModified(IsModified_MachineData);
3148 mUserData.backup();
3149 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3150
3151 return S_OK;
3152}
3153
3154HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3155{
3156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3157
3158 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3159
3160 return S_OK;
3161}
3162
3163HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3164{
3165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3166
3167 HRESULT hrc = i_checkStateDependency(MutableStateDep);
3168 if (FAILED(hrc)) return hrc;
3169
3170 i_setModified(IsModified_MachineData);
3171 mHWData.backup();
3172 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3173
3174 return S_OK;
3175}
3176
3177HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3178{
3179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3180
3181 *aIOCacheSize = mHWData->mIOCacheSize;
3182
3183 return S_OK;
3184}
3185
3186HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3187{
3188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3189
3190 HRESULT hrc = i_checkStateDependency(MutableStateDep);
3191 if (FAILED(hrc)) return hrc;
3192
3193 i_setModified(IsModified_MachineData);
3194 mHWData.backup();
3195 mHWData->mIOCacheSize = aIOCacheSize;
3196
3197 return S_OK;
3198}
3199
3200HRESULT Machine::getStateKeyId(com::Utf8Str &aKeyId)
3201{
3202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3203
3204#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3205 aKeyId = mSSData->strStateKeyId;
3206#else
3207 aKeyId = com::Utf8Str::Empty;
3208#endif
3209
3210 return S_OK;
3211}
3212
3213HRESULT Machine::getStateKeyStore(com::Utf8Str &aKeyStore)
3214{
3215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3216
3217#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3218 aKeyStore = mSSData->strStateKeyStore;
3219#else
3220 aKeyStore = com::Utf8Str::Empty;
3221#endif
3222
3223 return S_OK;
3224}
3225
3226HRESULT Machine::getLogKeyId(com::Utf8Str &aKeyId)
3227{
3228 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3229
3230#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3231 aKeyId = mData->mstrLogKeyId;
3232#else
3233 aKeyId = com::Utf8Str::Empty;
3234#endif
3235
3236 return S_OK;
3237}
3238
3239HRESULT Machine::getLogKeyStore(com::Utf8Str &aKeyStore)
3240{
3241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3242
3243#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3244 aKeyStore = mData->mstrLogKeyStore;
3245#else
3246 aKeyStore = com::Utf8Str::Empty;
3247#endif
3248
3249 return S_OK;
3250}
3251
3252HRESULT Machine::getGuestDebugControl(ComPtr<IGuestDebugControl> &aGuestDebugControl)
3253{
3254 mGuestDebugControl.queryInterfaceTo(aGuestDebugControl.asOutParam());
3255
3256 return S_OK;
3257}
3258
3259
3260/**
3261 * @note Locks objects!
3262 */
3263HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3264 LockType_T aLockType)
3265{
3266 /* check the session state */
3267 SessionState_T state;
3268 HRESULT hrc = aSession->COMGETTER(State)(&state);
3269 if (FAILED(hrc)) return hrc;
3270
3271 if (state != SessionState_Unlocked)
3272 return setError(VBOX_E_INVALID_OBJECT_STATE,
3273 tr("The given session is busy"));
3274
3275 // get the client's IInternalSessionControl interface
3276 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3277 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
3278 E_INVALIDARG);
3279
3280 // session name (only used in some code paths)
3281 Utf8Str strSessionName;
3282
3283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3284
3285 if (!mData->mRegistered)
3286 return setError(E_UNEXPECTED,
3287 tr("The machine '%s' is not registered"),
3288 mUserData->s.strName.c_str());
3289
3290 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3291
3292 SessionState_T oldState = mData->mSession.mState;
3293 /* Hack: in case the session is closing and there is a progress object
3294 * which allows waiting for the session to be closed, take the opportunity
3295 * and do a limited wait (max. 1 second). This helps a lot when the system
3296 * is busy and thus session closing can take a little while. */
3297 if ( mData->mSession.mState == SessionState_Unlocking
3298 && mData->mSession.mProgress)
3299 {
3300 alock.release();
3301 mData->mSession.mProgress->WaitForCompletion(1000);
3302 alock.acquire();
3303 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3304 }
3305
3306 // try again now
3307 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3308 // (i.e. session machine exists)
3309 && (aLockType == LockType_Shared) // caller wants a shared link to the
3310 // existing session that holds the write lock:
3311 )
3312 {
3313 // OK, share the session... we are now dealing with three processes:
3314 // 1) VBoxSVC (where this code runs);
3315 // 2) process C: the caller's client process (who wants a shared session);
3316 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3317
3318 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3319 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3320 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3321 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3322 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3323
3324 /*
3325 * Release the lock before calling the client process. It's safe here
3326 * since the only thing to do after we get the lock again is to add
3327 * the remote control to the list (which doesn't directly influence
3328 * anything).
3329 */
3330 alock.release();
3331
3332 // get the console of the session holding the write lock (this is a remote call)
3333 ComPtr<IConsole> pConsoleW;
3334 if (mData->mSession.mLockType == LockType_VM)
3335 {
3336 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3337 hrc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3338 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", hrc));
3339 if (FAILED(hrc))
3340 // the failure may occur w/o any error info (from RPC), so provide one
3341 return setError(VBOX_E_VM_ERROR, tr("Failed to get a console object from the direct session (%Rhrc)"), hrc);
3342 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3343 }
3344
3345 // share the session machine and W's console with the caller's session
3346 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3347 hrc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3348 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", hrc));
3349
3350 if (FAILED(hrc))
3351 // the failure may occur w/o any error info (from RPC), so provide one
3352 return setError(VBOX_E_VM_ERROR, tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
3353 alock.acquire();
3354
3355 // need to revalidate the state after acquiring the lock again
3356 if (mData->mSession.mState != SessionState_Locked)
3357 {
3358 pSessionControl->Uninitialize();
3359 return setError(VBOX_E_INVALID_SESSION_STATE,
3360 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3361 mUserData->s.strName.c_str());
3362 }
3363
3364 // add the caller's session to the list
3365 mData->mSession.mRemoteControls.push_back(pSessionControl);
3366 }
3367 else if ( mData->mSession.mState == SessionState_Locked
3368 || mData->mSession.mState == SessionState_Unlocking
3369 )
3370 {
3371 // sharing not permitted, or machine still unlocking:
3372 return setError(VBOX_E_INVALID_OBJECT_STATE,
3373 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3374 mUserData->s.strName.c_str());
3375 }
3376 else
3377 {
3378 // machine is not locked: then write-lock the machine (create the session machine)
3379
3380 // must not be busy
3381 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3382
3383 // get the caller's session PID
3384 RTPROCESS pid = NIL_RTPROCESS;
3385 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3386 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3387 Assert(pid != NIL_RTPROCESS);
3388
3389 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3390
3391 if (fLaunchingVMProcess)
3392 {
3393 if (mData->mSession.mPID == NIL_RTPROCESS)
3394 {
3395 // two or more clients racing for a lock, the one which set the
3396 // session state to Spawning will win, the others will get an
3397 // error as we can't decide here if waiting a little would help
3398 // (only for shared locks this would avoid an error)
3399 return setError(VBOX_E_INVALID_OBJECT_STATE,
3400 tr("The machine '%s' already has a lock request pending"),
3401 mUserData->s.strName.c_str());
3402 }
3403
3404 // this machine is awaiting for a spawning session to be opened:
3405 // then the calling process must be the one that got started by
3406 // LaunchVMProcess()
3407
3408 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3409 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3410
3411#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3412 /* Hardened windows builds spawns three processes when a VM is
3413 launched, the 3rd one is the one that will end up here. */
3414 RTPROCESS pidParent;
3415 int vrc = RTProcQueryParent(pid, &pidParent);
3416 if (RT_SUCCESS(vrc))
3417 vrc = RTProcQueryParent(pidParent, &pidParent);
3418 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3419 || vrc == VERR_ACCESS_DENIED)
3420 {
3421 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3422 mData->mSession.mPID = pid;
3423 }
3424#endif
3425
3426 if (mData->mSession.mPID != pid)
3427 return setError(E_ACCESSDENIED,
3428 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3429 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3430 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3431 }
3432
3433 // create the mutable SessionMachine from the current machine
3434 ComObjPtr<SessionMachine> sessionMachine;
3435 sessionMachine.createObject();
3436 hrc = sessionMachine->init(this);
3437 AssertComRC(hrc);
3438
3439 /* NOTE: doing return from this function after this point but
3440 * before the end is forbidden since it may call SessionMachine::uninit()
3441 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3442 * lock while still holding the Machine lock in alock so that a deadlock
3443 * is possible due to the wrong lock order. */
3444
3445 if (SUCCEEDED(hrc))
3446 {
3447 /*
3448 * Set the session state to Spawning to protect against subsequent
3449 * attempts to open a session and to unregister the machine after
3450 * we release the lock.
3451 */
3452 SessionState_T origState = mData->mSession.mState;
3453 mData->mSession.mState = SessionState_Spawning;
3454
3455#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3456 /* Get the client token ID to be passed to the client process */
3457 Utf8Str strTokenId;
3458 sessionMachine->i_getTokenId(strTokenId);
3459 Assert(!strTokenId.isEmpty());
3460#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3461 /* Get the client token to be passed to the client process */
3462 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3463 /* The token is now "owned" by pToken, fix refcount */
3464 if (!pToken.isNull())
3465 pToken->Release();
3466#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3467
3468 /*
3469 * Release the lock before calling the client process -- it will call
3470 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3471 * because the state is Spawning, so that LaunchVMProcess() and
3472 * LockMachine() calls will fail. This method, called before we
3473 * acquire the lock again, will fail because of the wrong PID.
3474 *
3475 * Note that mData->mSession.mRemoteControls accessed outside
3476 * the lock may not be modified when state is Spawning, so it's safe.
3477 */
3478 alock.release();
3479
3480 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3481#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3482 hrc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3483#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3484 hrc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3485 /* Now the token is owned by the client process. */
3486 pToken.setNull();
3487#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3488 LogFlowThisFunc(("AssignMachine() returned %08X\n", hrc));
3489
3490 /* The failure may occur w/o any error info (from RPC), so provide one */
3491 if (FAILED(hrc))
3492 setError(VBOX_E_VM_ERROR, tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
3493
3494 // get session name, either to remember or to compare against
3495 // the already known session name.
3496 {
3497 Bstr bstrSessionName;
3498 HRESULT hrc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3499 if (SUCCEEDED(hrc2))
3500 strSessionName = bstrSessionName;
3501 }
3502
3503 if ( SUCCEEDED(hrc)
3504 && fLaunchingVMProcess
3505 )
3506 {
3507 /* complete the remote session initialization */
3508
3509 /* get the console from the direct session */
3510 ComPtr<IConsole> console;
3511 hrc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3512 ComAssertComRC(hrc);
3513
3514 if (SUCCEEDED(hrc) && !console)
3515 {
3516 ComAssert(!!console);
3517 hrc = E_FAIL;
3518 }
3519
3520 /* assign machine & console to the remote session */
3521 if (SUCCEEDED(hrc))
3522 {
3523 /*
3524 * after LaunchVMProcess(), the first and the only
3525 * entry in remoteControls is that remote session
3526 */
3527 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3528 hrc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3529 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", hrc));
3530
3531 /* The failure may occur w/o any error info (from RPC), so provide one */
3532 if (FAILED(hrc))
3533 setError(VBOX_E_VM_ERROR,
3534 tr("Failed to assign the machine to the remote session (%Rhrc)"), hrc);
3535 }
3536
3537 if (FAILED(hrc))
3538 pSessionControl->Uninitialize();
3539 }
3540
3541 /* acquire the lock again */
3542 alock.acquire();
3543
3544 /* Restore the session state */
3545 mData->mSession.mState = origState;
3546 }
3547
3548 // finalize spawning anyway (this is why we don't return on errors above)
3549 if (fLaunchingVMProcess)
3550 {
3551 Assert(mData->mSession.mName == strSessionName || FAILED(hrc));
3552 /* Note that the progress object is finalized later */
3553 /** @todo Consider checking mData->mSession.mProgress for cancellation
3554 * around here. */
3555
3556 /* We don't reset mSession.mPID here because it is necessary for
3557 * SessionMachine::uninit() to reap the child process later. */
3558
3559 if (FAILED(hrc))
3560 {
3561 /* Close the remote session, remove the remote control from the list
3562 * and reset session state to Closed (@note keep the code in sync
3563 * with the relevant part in checkForSpawnFailure()). */
3564
3565 Assert(mData->mSession.mRemoteControls.size() == 1);
3566 if (mData->mSession.mRemoteControls.size() == 1)
3567 {
3568 ErrorInfoKeeper eik;
3569 mData->mSession.mRemoteControls.front()->Uninitialize();
3570 }
3571
3572 mData->mSession.mRemoteControls.clear();
3573 mData->mSession.mState = SessionState_Unlocked;
3574 }
3575 }
3576 else
3577 {
3578 /* memorize PID of the directly opened session */
3579 if (SUCCEEDED(hrc))
3580 mData->mSession.mPID = pid;
3581 }
3582
3583 if (SUCCEEDED(hrc))
3584 {
3585 mData->mSession.mLockType = aLockType;
3586 /* memorize the direct session control and cache IUnknown for it */
3587 mData->mSession.mDirectControl = pSessionControl;
3588 mData->mSession.mState = SessionState_Locked;
3589 if (!fLaunchingVMProcess)
3590 mData->mSession.mName = strSessionName;
3591 /* associate the SessionMachine with this Machine */
3592 mData->mSession.mMachine = sessionMachine;
3593
3594 /* request an IUnknown pointer early from the remote party for later
3595 * identity checks (it will be internally cached within mDirectControl
3596 * at least on XPCOM) */
3597 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3598 NOREF(unk);
3599
3600#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3601 if (aLockType == LockType_VM)
3602 {
3603 /* get the console from the direct session */
3604 ComPtr<IConsole> console;
3605 hrc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3606 ComAssertComRC(hrc);
3607 /* send passswords to console */
3608 for (SecretKeyStore::SecretKeyMap::iterator it = mData->mpKeyStore->begin();
3609 it != mData->mpKeyStore->end();
3610 ++it)
3611 {
3612 SecretKey *pKey = it->second;
3613 pKey->retain();
3614 console->AddEncryptionPassword(Bstr(it->first).raw(),
3615 Bstr((const char*)pKey->getKeyBuffer()).raw(),
3616 TRUE);
3617 pKey->release();
3618 }
3619
3620 }
3621#endif
3622 }
3623
3624 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3625 * would break the lock order */
3626 alock.release();
3627
3628 /* uninitialize the created session machine on failure */
3629 if (FAILED(hrc))
3630 sessionMachine->uninit();
3631 }
3632
3633 if (SUCCEEDED(hrc))
3634 {
3635 /*
3636 * tell the client watcher thread to update the set of
3637 * machines that have open sessions
3638 */
3639 mParent->i_updateClientWatcher();
3640
3641 if (oldState != SessionState_Locked)
3642 /* fire an event */
3643 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3644 }
3645
3646 return hrc;
3647}
3648
3649/**
3650 * @note Locks objects!
3651 */
3652HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3653 const com::Utf8Str &aName,
3654 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3655 ComPtr<IProgress> &aProgress)
3656{
3657 Utf8Str strFrontend(aName);
3658 /* "emergencystop" doesn't need the session, so skip the checks/interface
3659 * retrieval. This code doesn't quite fit in here, but introducing a
3660 * special API method would be even more effort, and would require explicit
3661 * support by every API client. It's better to hide the feature a bit. */
3662 if (strFrontend != "emergencystop")
3663 CheckComArgNotNull(aSession);
3664
3665 HRESULT hrc = S_OK;
3666 if (strFrontend.isEmpty())
3667 {
3668 Bstr bstrFrontend;
3669 hrc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3670 if (FAILED(hrc))
3671 return hrc;
3672 strFrontend = bstrFrontend;
3673 if (strFrontend.isEmpty())
3674 {
3675 ComPtr<ISystemProperties> systemProperties;
3676 hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3677 if (FAILED(hrc))
3678 return hrc;
3679 hrc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3680 if (FAILED(hrc))
3681 return hrc;
3682 strFrontend = bstrFrontend;
3683 }
3684 /* paranoia - emergencystop is not a valid default */
3685 if (strFrontend == "emergencystop")
3686 strFrontend = Utf8Str::Empty;
3687 }
3688 /* default frontend: Qt GUI */
3689 if (strFrontend.isEmpty())
3690 strFrontend = "GUI/Qt";
3691
3692 if (strFrontend != "emergencystop")
3693 {
3694 /* check the session state */
3695 SessionState_T state;
3696 hrc = aSession->COMGETTER(State)(&state);
3697 if (FAILED(hrc))
3698 return hrc;
3699
3700 if (state != SessionState_Unlocked)
3701 return setError(VBOX_E_INVALID_OBJECT_STATE,
3702 tr("The given session is busy"));
3703
3704 /* get the IInternalSessionControl interface */
3705 ComPtr<IInternalSessionControl> control(aSession);
3706 ComAssertMsgRet(!control.isNull(),
3707 ("No IInternalSessionControl interface"),
3708 E_INVALIDARG);
3709
3710 /* get the teleporter enable state for the progress object init. */
3711 BOOL fTeleporterEnabled;
3712 hrc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3713 if (FAILED(hrc))
3714 return hrc;
3715
3716 /* create a progress object */
3717 ComObjPtr<ProgressProxy> progress;
3718 progress.createObject();
3719 hrc = progress->init(mParent,
3720 static_cast<IMachine*>(this),
3721 Bstr(tr("Starting VM")).raw(),
3722 TRUE /* aCancelable */,
3723 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3724 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3725 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3726 2 /* uFirstOperationWeight */,
3727 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3728 if (SUCCEEDED(hrc))
3729 {
3730 hrc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3731 if (SUCCEEDED(hrc))
3732 {
3733 aProgress = progress;
3734
3735 /* signal the client watcher thread */
3736 mParent->i_updateClientWatcher();
3737
3738 /* fire an event */
3739 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3740 }
3741 }
3742 }
3743 else
3744 {
3745 /* no progress object - either instant success or failure */
3746 aProgress = NULL;
3747
3748 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3749
3750 if (mData->mSession.mState != SessionState_Locked)
3751 return setError(VBOX_E_INVALID_OBJECT_STATE,
3752 tr("The machine '%s' is not locked by a session"),
3753 mUserData->s.strName.c_str());
3754
3755 /* must have a VM process associated - do not kill normal API clients
3756 * with an open session */
3757 if (!Global::IsOnline(mData->mMachineState))
3758 return setError(VBOX_E_INVALID_OBJECT_STATE,
3759 tr("The machine '%s' does not have a VM process"),
3760 mUserData->s.strName.c_str());
3761
3762 /* forcibly terminate the VM process */
3763 if (mData->mSession.mPID != NIL_RTPROCESS)
3764 RTProcTerminate(mData->mSession.mPID);
3765
3766 /* signal the client watcher thread, as most likely the client has
3767 * been terminated */
3768 mParent->i_updateClientWatcher();
3769 }
3770
3771 return hrc;
3772}
3773
3774HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3775{
3776 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3777 return setError(E_INVALIDARG,
3778 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3779 aPosition, SchemaDefs::MaxBootPosition);
3780
3781 if (aDevice == DeviceType_USB)
3782 return setError(E_NOTIMPL,
3783 tr("Booting from USB device is currently not supported"));
3784
3785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3786
3787 HRESULT hrc = i_checkStateDependency(MutableStateDep);
3788 if (FAILED(hrc)) return hrc;
3789
3790 i_setModified(IsModified_MachineData);
3791 mHWData.backup();
3792 mHWData->mBootOrder[aPosition - 1] = aDevice;
3793
3794 return S_OK;
3795}
3796
3797HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3798{
3799 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3800 return setError(E_INVALIDARG,
3801 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3802 aPosition, SchemaDefs::MaxBootPosition);
3803
3804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3805
3806 *aDevice = mHWData->mBootOrder[aPosition - 1];
3807
3808 return S_OK;
3809}
3810
3811HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3812 LONG aControllerPort,
3813 LONG aDevice,
3814 DeviceType_T aType,
3815 const ComPtr<IMedium> &aMedium)
3816{
3817 IMedium *aM = aMedium;
3818 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3819 aName.c_str(), aControllerPort, aDevice, aType, aM));
3820
3821 // request the host lock first, since might be calling Host methods for getting host drives;
3822 // next, protect the media tree all the while we're in here, as well as our member variables
3823 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3824 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3825
3826 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3827 if (FAILED(hrc)) return hrc;
3828
3829 /// @todo NEWMEDIA implicit machine registration
3830 if (!mData->mRegistered)
3831 return setError(VBOX_E_INVALID_OBJECT_STATE,
3832 tr("Cannot attach storage devices to an unregistered machine"));
3833
3834 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3835
3836 /* Check for an existing controller. */
3837 ComObjPtr<StorageController> ctl;
3838 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3839 if (FAILED(hrc)) return hrc;
3840
3841 StorageControllerType_T ctrlType;
3842 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3843 if (FAILED(hrc))
3844 return setError(E_FAIL, tr("Could not get type of controller '%s'"), aName.c_str());
3845
3846 bool fSilent = false;
3847
3848 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3849 Utf8Str const strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3850 if ( mData->mMachineState == MachineState_Paused
3851 && strReconfig == "1")
3852 fSilent = true;
3853
3854 /* Check that the controller can do hot-plugging if we attach the device while the VM is running. */
3855 bool fHotplug = false;
3856 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3857 fHotplug = true;
3858
3859 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3860 return setError(VBOX_E_INVALID_VM_STATE,
3861 tr("Controller '%s' does not support hot-plugging"),
3862 aName.c_str());
3863
3864 // check that the port and device are not out of range
3865 hrc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3866 if (FAILED(hrc)) return hrc;
3867
3868 /* check if the device slot is already busy */
3869 MediumAttachment *pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3870 aName,
3871 aControllerPort,
3872 aDevice);
3873 if (pAttachTemp)
3874 {
3875 Medium *pMedium = pAttachTemp->i_getMedium();
3876 if (pMedium)
3877 {
3878 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3879 return setError(VBOX_E_OBJECT_IN_USE,
3880 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3881 pMedium->i_getLocationFull().c_str(),
3882 aControllerPort,
3883 aDevice,
3884 aName.c_str());
3885 }
3886 else
3887 return setError(VBOX_E_OBJECT_IN_USE,
3888 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3889 aControllerPort, aDevice, aName.c_str());
3890 }
3891
3892 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3893 if (aMedium && medium.isNull())
3894 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3895
3896 AutoCaller mediumCaller(medium);
3897 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3898
3899 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3900
3901 pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium);
3902 if ( pAttachTemp
3903 && !medium.isNull()
3904 && ( medium->i_getType() != MediumType_Readonly
3905 || medium->i_getDeviceType() != DeviceType_DVD)
3906 )
3907 return setError(VBOX_E_OBJECT_IN_USE,
3908 tr("Medium '%s' is already attached to this virtual machine"),
3909 medium->i_getLocationFull().c_str());
3910
3911 if (!medium.isNull())
3912 {
3913 MediumType_T mtype = medium->i_getType();
3914 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3915 // For DVDs it's not written to the config file, so needs no global config
3916 // version bump. For floppies it's a new attribute "type", which is ignored
3917 // by older VirtualBox version, so needs no global config version bump either.
3918 // For hard disks this type is not accepted.
3919 if (mtype == MediumType_MultiAttach)
3920 {
3921 // This type is new with VirtualBox 4.0 and therefore requires settings
3922 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3923 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3924 // two reasons: The medium type is a property of the media registry tree, which
3925 // can reside in the global config file (for pre-4.0 media); we would therefore
3926 // possibly need to bump the global config version. We don't want to do that though
3927 // because that might make downgrading to pre-4.0 impossible.
3928 // As a result, we can only use these two new types if the medium is NOT in the
3929 // global registry:
3930 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3931 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3932 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3933 )
3934 return setError(VBOX_E_INVALID_OBJECT_STATE,
3935 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3936 "to machines that were created with VirtualBox 4.0 or later"),
3937 medium->i_getLocationFull().c_str());
3938 }
3939 }
3940
3941 bool fIndirect = false;
3942 if (!medium.isNull())
3943 fIndirect = medium->i_isReadOnly();
3944 bool associate = true;
3945
3946 do
3947 {
3948 if ( aType == DeviceType_HardDisk
3949 && mMediumAttachments.isBackedUp())
3950 {
3951 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3952
3953 /* check if the medium was attached to the VM before we started
3954 * changing attachments in which case the attachment just needs to
3955 * be restored */
3956 pAttachTemp = i_findAttachment(oldAtts, medium);
3957 if (pAttachTemp)
3958 {
3959 AssertReturn(!fIndirect, E_FAIL);
3960
3961 /* see if it's the same bus/channel/device */
3962 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3963 {
3964 /* the simplest case: restore the whole attachment
3965 * and return, nothing else to do */
3966 mMediumAttachments->push_back(pAttachTemp);
3967
3968 /* Reattach the medium to the VM. */
3969 if (fHotplug || fSilent)
3970 {
3971 mediumLock.release();
3972 treeLock.release();
3973 alock.release();
3974
3975 MediumLockList *pMediumLockList(new MediumLockList());
3976
3977 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3978 medium /* pToLockWrite */,
3979 false /* fMediumLockWriteAll */,
3980 NULL,
3981 *pMediumLockList);
3982 alock.acquire();
3983 if (FAILED(hrc))
3984 delete pMediumLockList;
3985 else
3986 {
3987 Assert(mData->mSession.mLockedMedia.IsLocked());
3988 mData->mSession.mLockedMedia.Unlock();
3989 alock.release();
3990 hrc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3991 mData->mSession.mLockedMedia.Lock();
3992 alock.acquire();
3993 }
3994 alock.release();
3995
3996 if (SUCCEEDED(hrc))
3997 {
3998 hrc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3999 /* Remove lock list in case of error. */
4000 if (FAILED(hrc))
4001 {
4002 mData->mSession.mLockedMedia.Unlock();
4003 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4004 mData->mSession.mLockedMedia.Lock();
4005 }
4006 }
4007 }
4008
4009 return S_OK;
4010 }
4011
4012 /* bus/channel/device differ; we need a new attachment object,
4013 * but don't try to associate it again */
4014 associate = false;
4015 break;
4016 }
4017 }
4018
4019 /* go further only if the attachment is to be indirect */
4020 if (!fIndirect)
4021 break;
4022
4023 /* perform the so called smart attachment logic for indirect
4024 * attachments. Note that smart attachment is only applicable to base
4025 * hard disks. */
4026
4027 if (medium->i_getParent().isNull())
4028 {
4029 /* first, investigate the backup copy of the current hard disk
4030 * attachments to make it possible to re-attach existing diffs to
4031 * another device slot w/o losing their contents */
4032 if (mMediumAttachments.isBackedUp())
4033 {
4034 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4035
4036 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4037 uint32_t foundLevel = 0;
4038
4039 for (MediumAttachmentList::const_iterator
4040 it = oldAtts.begin();
4041 it != oldAtts.end();
4042 ++it)
4043 {
4044 uint32_t level = 0;
4045 MediumAttachment *pAttach = *it;
4046 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4047 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4048 if (pMedium.isNull())
4049 continue;
4050
4051 if (pMedium->i_getBase(&level) == medium)
4052 {
4053 /* skip the hard disk if its currently attached (we
4054 * cannot attach the same hard disk twice) */
4055 if (i_findAttachment(*mMediumAttachments.data(),
4056 pMedium))
4057 continue;
4058
4059 /* matched device, channel and bus (i.e. attached to the
4060 * same place) will win and immediately stop the search;
4061 * otherwise the attachment that has the youngest
4062 * descendant of medium will be used
4063 */
4064 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4065 {
4066 /* the simplest case: restore the whole attachment
4067 * and return, nothing else to do */
4068 mMediumAttachments->push_back(*it);
4069
4070 /* Reattach the medium to the VM. */
4071 if (fHotplug || fSilent)
4072 {
4073 mediumLock.release();
4074 treeLock.release();
4075 alock.release();
4076
4077 MediumLockList *pMediumLockList(new MediumLockList());
4078
4079 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4080 medium /* pToLockWrite */,
4081 false /* fMediumLockWriteAll */,
4082 NULL,
4083 *pMediumLockList);
4084 alock.acquire();
4085 if (FAILED(hrc))
4086 delete pMediumLockList;
4087 else
4088 {
4089 Assert(mData->mSession.mLockedMedia.IsLocked());
4090 mData->mSession.mLockedMedia.Unlock();
4091 alock.release();
4092 hrc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4093 mData->mSession.mLockedMedia.Lock();
4094 alock.acquire();
4095 }
4096 alock.release();
4097
4098 if (SUCCEEDED(hrc))
4099 {
4100 hrc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4101 /* Remove lock list in case of error. */
4102 if (FAILED(hrc))
4103 {
4104 mData->mSession.mLockedMedia.Unlock();
4105 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4106 mData->mSession.mLockedMedia.Lock();
4107 }
4108 }
4109 }
4110
4111 return S_OK;
4112 }
4113 else if ( foundIt == oldAtts.end()
4114 || level > foundLevel /* prefer younger */
4115 )
4116 {
4117 foundIt = it;
4118 foundLevel = level;
4119 }
4120 }
4121 }
4122
4123 if (foundIt != oldAtts.end())
4124 {
4125 /* use the previously attached hard disk */
4126 medium = (*foundIt)->i_getMedium();
4127 mediumCaller.attach(medium);
4128 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
4129 mediumLock.attach(medium);
4130 /* not implicit, doesn't require association with this VM */
4131 fIndirect = false;
4132 associate = false;
4133 /* go right to the MediumAttachment creation */
4134 break;
4135 }
4136 }
4137
4138 /* must give up the medium lock and medium tree lock as below we
4139 * go over snapshots, which needs a lock with higher lock order. */
4140 mediumLock.release();
4141 treeLock.release();
4142
4143 /* then, search through snapshots for the best diff in the given
4144 * hard disk's chain to base the new diff on */
4145
4146 ComObjPtr<Medium> base;
4147 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4148 while (snap)
4149 {
4150 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4151
4152 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4153
4154 MediumAttachment *pAttachFound = NULL;
4155 uint32_t foundLevel = 0;
4156
4157 for (MediumAttachmentList::const_iterator
4158 it = snapAtts.begin();
4159 it != snapAtts.end();
4160 ++it)
4161 {
4162 MediumAttachment *pAttach = *it;
4163 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4164 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4165 if (pMedium.isNull())
4166 continue;
4167
4168 uint32_t level = 0;
4169 if (pMedium->i_getBase(&level) == medium)
4170 {
4171 /* matched device, channel and bus (i.e. attached to the
4172 * same place) will win and immediately stop the search;
4173 * otherwise the attachment that has the youngest
4174 * descendant of medium will be used
4175 */
4176 if ( pAttach->i_getDevice() == aDevice
4177 && pAttach->i_getPort() == aControllerPort
4178 && pAttach->i_getControllerName() == aName
4179 )
4180 {
4181 pAttachFound = pAttach;
4182 break;
4183 }
4184 else if ( !pAttachFound
4185 || level > foundLevel /* prefer younger */
4186 )
4187 {
4188 pAttachFound = pAttach;
4189 foundLevel = level;
4190 }
4191 }
4192 }
4193
4194 if (pAttachFound)
4195 {
4196 base = pAttachFound->i_getMedium();
4197 break;
4198 }
4199
4200 snap = snap->i_getParent();
4201 }
4202
4203 /* re-lock medium tree and the medium, as we need it below */
4204 treeLock.acquire();
4205 mediumLock.acquire();
4206
4207 /* found a suitable diff, use it as a base */
4208 if (!base.isNull())
4209 {
4210 medium = base;
4211 mediumCaller.attach(medium);
4212 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
4213 mediumLock.attach(medium);
4214 }
4215 }
4216
4217 Utf8Str strFullSnapshotFolder;
4218 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4219
4220 ComObjPtr<Medium> diff;
4221 diff.createObject();
4222 // store this diff in the same registry as the parent
4223 Guid uuidRegistryParent;
4224 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4225 {
4226 // parent image has no registry: this can happen if we're attaching a new immutable
4227 // image that has not yet been attached (medium then points to the base and we're
4228 // creating the diff image for the immutable, and the parent is not yet registered);
4229 // put the parent in the machine registry then
4230 mediumLock.release();
4231 treeLock.release();
4232 alock.release();
4233 i_addMediumToRegistry(medium);
4234 alock.acquire();
4235 treeLock.acquire();
4236 mediumLock.acquire();
4237 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4238 }
4239 hrc = diff->init(mParent,
4240 medium->i_getPreferredDiffFormat(),
4241 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4242 uuidRegistryParent,
4243 DeviceType_HardDisk);
4244 if (FAILED(hrc)) return hrc;
4245
4246 /* Apply the normal locking logic to the entire chain. */
4247 MediumLockList *pMediumLockList(new MediumLockList());
4248 mediumLock.release();
4249 treeLock.release();
4250 hrc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4251 diff /* pToLockWrite */,
4252 false /* fMediumLockWriteAll */,
4253 medium,
4254 *pMediumLockList);
4255 treeLock.acquire();
4256 mediumLock.acquire();
4257 if (SUCCEEDED(hrc))
4258 {
4259 mediumLock.release();
4260 treeLock.release();
4261 hrc = pMediumLockList->Lock();
4262 treeLock.acquire();
4263 mediumLock.acquire();
4264 if (FAILED(hrc))
4265 setError(hrc,
4266 tr("Could not lock medium when creating diff '%s'"),
4267 diff->i_getLocationFull().c_str());
4268 else
4269 {
4270 /* will release the lock before the potentially lengthy
4271 * operation, so protect with the special state */
4272 MachineState_T oldState = mData->mMachineState;
4273 i_setMachineState(MachineState_SettingUp);
4274
4275 mediumLock.release();
4276 treeLock.release();
4277 alock.release();
4278
4279 hrc = medium->i_createDiffStorage(diff,
4280 medium->i_getPreferredDiffVariant(),
4281 pMediumLockList,
4282 NULL /* aProgress */,
4283 true /* aWait */,
4284 false /* aNotify */);
4285
4286 alock.acquire();
4287 treeLock.acquire();
4288 mediumLock.acquire();
4289
4290 i_setMachineState(oldState);
4291 }
4292 }
4293
4294 /* Unlock the media and free the associated memory. */
4295 delete pMediumLockList;
4296
4297 if (FAILED(hrc)) return hrc;
4298
4299 /* use the created diff for the actual attachment */
4300 medium = diff;
4301 mediumCaller.attach(medium);
4302 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
4303 mediumLock.attach(medium);
4304 }
4305 while (0);
4306
4307 ComObjPtr<MediumAttachment> attachment;
4308 attachment.createObject();
4309 hrc = attachment->init(this,
4310 medium,
4311 aName,
4312 aControllerPort,
4313 aDevice,
4314 aType,
4315 fIndirect,
4316 false /* fPassthrough */,
4317 false /* fTempEject */,
4318 false /* fNonRotational */,
4319 false /* fDiscard */,
4320 fHotplug || ctrlType == StorageControllerType_USB /* fHotPluggable */,
4321 Utf8Str::Empty);
4322 if (FAILED(hrc)) return hrc;
4323
4324 if (associate && !medium.isNull())
4325 {
4326 // as the last step, associate the medium to the VM
4327 hrc = medium->i_addBackReference(mData->mUuid);
4328 // here we can fail because of Deleting, or being in process of creating a Diff
4329 if (FAILED(hrc)) return hrc;
4330
4331 mediumLock.release();
4332 treeLock.release();
4333 alock.release();
4334 i_addMediumToRegistry(medium);
4335 alock.acquire();
4336 treeLock.acquire();
4337 mediumLock.acquire();
4338 }
4339
4340 /* success: finally remember the attachment */
4341 i_setModified(IsModified_Storage);
4342 mMediumAttachments.backup();
4343 mMediumAttachments->push_back(attachment);
4344
4345 mediumLock.release();
4346 treeLock.release();
4347 alock.release();
4348
4349 if (fHotplug || fSilent)
4350 {
4351 if (!medium.isNull())
4352 {
4353 MediumLockList *pMediumLockList(new MediumLockList());
4354
4355 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4356 medium /* pToLockWrite */,
4357 false /* fMediumLockWriteAll */,
4358 NULL,
4359 *pMediumLockList);
4360 alock.acquire();
4361 if (FAILED(hrc))
4362 delete pMediumLockList;
4363 else
4364 {
4365 Assert(mData->mSession.mLockedMedia.IsLocked());
4366 mData->mSession.mLockedMedia.Unlock();
4367 alock.release();
4368 hrc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4369 mData->mSession.mLockedMedia.Lock();
4370 alock.acquire();
4371 }
4372 alock.release();
4373 }
4374
4375 if (SUCCEEDED(hrc))
4376 {
4377 hrc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4378 /* Remove lock list in case of error. */
4379 if (FAILED(hrc))
4380 {
4381 mData->mSession.mLockedMedia.Unlock();
4382 mData->mSession.mLockedMedia.Remove(attachment);
4383 mData->mSession.mLockedMedia.Lock();
4384 }
4385 }
4386 }
4387
4388 /* Save modified registries, but skip this machine as it's the caller's
4389 * job to save its settings like all other settings changes. */
4390 mParent->i_unmarkRegistryModified(i_getId());
4391 mParent->i_saveModifiedRegistries();
4392
4393 if (SUCCEEDED(hrc))
4394 {
4395 if (fIndirect && medium != aM)
4396 mParent->i_onMediumConfigChanged(medium);
4397 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4398 }
4399
4400 return hrc;
4401}
4402
4403HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4404 LONG aDevice)
4405{
4406 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n", aName.c_str(), aControllerPort, aDevice));
4407
4408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4409
4410 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4411 if (FAILED(hrc)) return hrc;
4412
4413 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4414
4415 /* Check for an existing controller. */
4416 ComObjPtr<StorageController> ctl;
4417 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4418 if (FAILED(hrc)) return hrc;
4419
4420 StorageControllerType_T ctrlType;
4421 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
4422 if (FAILED(hrc))
4423 return setError(E_FAIL, tr("Could not get type of controller '%s'"), aName.c_str());
4424
4425 bool fSilent = false;
4426 Utf8Str strReconfig;
4427
4428 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4429 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4430 if ( mData->mMachineState == MachineState_Paused
4431 && strReconfig == "1")
4432 fSilent = true;
4433
4434 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4435 bool fHotplug = false;
4436 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4437 fHotplug = true;
4438
4439 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4440 return setError(VBOX_E_INVALID_VM_STATE,
4441 tr("Controller '%s' does not support hot-plugging"),
4442 aName.c_str());
4443
4444 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4445 aName,
4446 aControllerPort,
4447 aDevice);
4448 if (!pAttach)
4449 return setError(VBOX_E_OBJECT_NOT_FOUND,
4450 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4451 aDevice, aControllerPort, aName.c_str());
4452
4453 if (fHotplug && !pAttach->i_getHotPluggable())
4454 return setError(VBOX_E_NOT_SUPPORTED,
4455 tr("The device slot %d on port %d of controller '%s' does not support hot-plugging"),
4456 aDevice, aControllerPort, aName.c_str());
4457
4458 /*
4459 * The VM has to detach the device before we delete any implicit diffs.
4460 * If this fails we can roll back without loosing data.
4461 */
4462 if (fHotplug || fSilent)
4463 {
4464 alock.release();
4465 hrc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4466 alock.acquire();
4467 }
4468 if (FAILED(hrc)) return hrc;
4469
4470 /* If we are here everything went well and we can delete the implicit now. */
4471 hrc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4472
4473 alock.release();
4474
4475 /* Save modified registries, but skip this machine as it's the caller's
4476 * job to save its settings like all other settings changes. */
4477 mParent->i_unmarkRegistryModified(i_getId());
4478 mParent->i_saveModifiedRegistries();
4479
4480 if (SUCCEEDED(hrc))
4481 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4482
4483 return hrc;
4484}
4485
4486HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4487 LONG aDevice, BOOL aPassthrough)
4488{
4489 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4490 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4491
4492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4493
4494 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4495 if (FAILED(hrc)) return hrc;
4496
4497 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4498
4499 /* Check for an existing controller. */
4500 ComObjPtr<StorageController> ctl;
4501 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4502 if (FAILED(hrc)) return hrc;
4503
4504 StorageControllerType_T ctrlType;
4505 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
4506 if (FAILED(hrc))
4507 return setError(E_FAIL,
4508 tr("Could not get type of controller '%s'"),
4509 aName.c_str());
4510
4511 bool fSilent = false;
4512 Utf8Str strReconfig;
4513
4514 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4515 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4516 if ( mData->mMachineState == MachineState_Paused
4517 && strReconfig == "1")
4518 fSilent = true;
4519
4520 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4521 bool fHotplug = false;
4522 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4523 fHotplug = true;
4524
4525 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4526 return setError(VBOX_E_INVALID_VM_STATE,
4527 tr("Controller '%s' does not support hot-plugging which is required to change the passthrough setting while the VM is running"),
4528 aName.c_str());
4529
4530 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4531 aName,
4532 aControllerPort,
4533 aDevice);
4534 if (!pAttach)
4535 return setError(VBOX_E_OBJECT_NOT_FOUND,
4536 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4537 aDevice, aControllerPort, aName.c_str());
4538
4539
4540 i_setModified(IsModified_Storage);
4541 mMediumAttachments.backup();
4542
4543 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4544
4545 if (pAttach->i_getType() != DeviceType_DVD)
4546 return setError(E_INVALIDARG,
4547 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4548 aDevice, aControllerPort, aName.c_str());
4549
4550 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4551
4552 pAttach->i_updatePassthrough(!!aPassthrough);
4553
4554 attLock.release();
4555 alock.release();
4556 hrc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4557 if (SUCCEEDED(hrc) && fValueChanged)
4558 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4559
4560 return hrc;
4561}
4562
4563HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4564 LONG aDevice, BOOL aTemporaryEject)
4565{
4566
4567 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4568 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4569
4570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4571
4572 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4573 if (FAILED(hrc)) return hrc;
4574
4575 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4576 aName,
4577 aControllerPort,
4578 aDevice);
4579 if (!pAttach)
4580 return setError(VBOX_E_OBJECT_NOT_FOUND,
4581 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4582 aDevice, aControllerPort, aName.c_str());
4583
4584
4585 i_setModified(IsModified_Storage);
4586 mMediumAttachments.backup();
4587
4588 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4589
4590 if (pAttach->i_getType() != DeviceType_DVD)
4591 return setError(E_INVALIDARG,
4592 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4593 aDevice, aControllerPort, aName.c_str());
4594 pAttach->i_updateTempEject(!!aTemporaryEject);
4595
4596 return S_OK;
4597}
4598
4599HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4600 LONG aDevice, BOOL aNonRotational)
4601{
4602
4603 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4604 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4605
4606 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4607
4608 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4609 if (FAILED(hrc)) return hrc;
4610
4611 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4612
4613 if (Global::IsOnlineOrTransient(mData->mMachineState))
4614 return setError(VBOX_E_INVALID_VM_STATE,
4615 tr("Invalid machine state: %s"),
4616 Global::stringifyMachineState(mData->mMachineState));
4617
4618 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4619 aName,
4620 aControllerPort,
4621 aDevice);
4622 if (!pAttach)
4623 return setError(VBOX_E_OBJECT_NOT_FOUND,
4624 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4625 aDevice, aControllerPort, aName.c_str());
4626
4627
4628 i_setModified(IsModified_Storage);
4629 mMediumAttachments.backup();
4630
4631 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4632
4633 if (pAttach->i_getType() != DeviceType_HardDisk)
4634 return setError(E_INVALIDARG,
4635 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"),
4636 aDevice, aControllerPort, aName.c_str());
4637 pAttach->i_updateNonRotational(!!aNonRotational);
4638
4639 return S_OK;
4640}
4641
4642HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4643 LONG aDevice, BOOL aDiscard)
4644{
4645
4646 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4647 aName.c_str(), aControllerPort, aDevice, aDiscard));
4648
4649 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4650
4651 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4652 if (FAILED(hrc)) return hrc;
4653
4654 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4655
4656 if (Global::IsOnlineOrTransient(mData->mMachineState))
4657 return setError(VBOX_E_INVALID_VM_STATE,
4658 tr("Invalid machine state: %s"),
4659 Global::stringifyMachineState(mData->mMachineState));
4660
4661 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4662 aName,
4663 aControllerPort,
4664 aDevice);
4665 if (!pAttach)
4666 return setError(VBOX_E_OBJECT_NOT_FOUND,
4667 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4668 aDevice, aControllerPort, aName.c_str());
4669
4670
4671 i_setModified(IsModified_Storage);
4672 mMediumAttachments.backup();
4673
4674 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4675
4676 if (pAttach->i_getType() != DeviceType_HardDisk)
4677 return setError(E_INVALIDARG,
4678 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"),
4679 aDevice, aControllerPort, aName.c_str());
4680 pAttach->i_updateDiscard(!!aDiscard);
4681
4682 return S_OK;
4683}
4684
4685HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4686 LONG aDevice, BOOL aHotPluggable)
4687{
4688 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4689 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4690
4691 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4692
4693 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4694 if (FAILED(hrc)) return hrc;
4695
4696 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4697
4698 if (Global::IsOnlineOrTransient(mData->mMachineState))
4699 return setError(VBOX_E_INVALID_VM_STATE,
4700 tr("Invalid machine state: %s"),
4701 Global::stringifyMachineState(mData->mMachineState));
4702
4703 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4704 aName,
4705 aControllerPort,
4706 aDevice);
4707 if (!pAttach)
4708 return setError(VBOX_E_OBJECT_NOT_FOUND,
4709 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4710 aDevice, aControllerPort, aName.c_str());
4711
4712 /* Check for an existing controller. */
4713 ComObjPtr<StorageController> ctl;
4714 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4715 if (FAILED(hrc)) return hrc;
4716
4717 StorageControllerType_T ctrlType;
4718 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
4719 if (FAILED(hrc))
4720 return setError(E_FAIL,
4721 tr("Could not get type of controller '%s'"),
4722 aName.c_str());
4723
4724 if (!i_isControllerHotplugCapable(ctrlType))
4725 return setError(VBOX_E_NOT_SUPPORTED,
4726 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4727 aName.c_str());
4728
4729 /* silently ignore attempts to modify the hot-plug status of USB devices */
4730 if (ctrlType == StorageControllerType_USB)
4731 return S_OK;
4732
4733 i_setModified(IsModified_Storage);
4734 mMediumAttachments.backup();
4735
4736 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4737
4738 if (pAttach->i_getType() == DeviceType_Floppy)
4739 return setError(E_INVALIDARG,
4740 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"),
4741 aDevice, aControllerPort, aName.c_str());
4742 pAttach->i_updateHotPluggable(!!aHotPluggable);
4743
4744 return S_OK;
4745}
4746
4747HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4748 LONG aDevice)
4749{
4750 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4751 aName.c_str(), aControllerPort, aDevice));
4752
4753 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4754}
4755
4756HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4757 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4758{
4759 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4760 aName.c_str(), aControllerPort, aDevice));
4761
4762 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4763
4764 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
4765 if (FAILED(hrc)) return hrc;
4766
4767 if (Global::IsOnlineOrTransient(mData->mMachineState))
4768 return setError(VBOX_E_INVALID_VM_STATE,
4769 tr("Invalid machine state: %s"),
4770 Global::stringifyMachineState(mData->mMachineState));
4771
4772 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4773 aName,
4774 aControllerPort,
4775 aDevice);
4776 if (!pAttach)
4777 return setError(VBOX_E_OBJECT_NOT_FOUND,
4778 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4779 aDevice, aControllerPort, aName.c_str());
4780
4781
4782 i_setModified(IsModified_Storage);
4783 mMediumAttachments.backup();
4784
4785 IBandwidthGroup *iB = aBandwidthGroup;
4786 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4787 if (aBandwidthGroup && group.isNull())
4788 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4789
4790 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4791
4792 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4793 if (strBandwidthGroupOld.isNotEmpty())
4794 {
4795 /* Get the bandwidth group object and release it - this must not fail. */
4796 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4797 hrc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4798 Assert(SUCCEEDED(hrc));
4799
4800 pBandwidthGroupOld->i_release();
4801 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4802 }
4803
4804 if (!group.isNull())
4805 {
4806 group->i_reference();
4807 pAttach->i_updateBandwidthGroup(group->i_getName());
4808 }
4809
4810 return S_OK;
4811}
4812
4813HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4814 LONG aControllerPort,
4815 LONG aDevice,
4816 DeviceType_T aType)
4817{
4818 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4819 aName.c_str(), aControllerPort, aDevice, aType));
4820
4821 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4822}
4823
4824
4825HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4826 LONG aControllerPort,
4827 LONG aDevice,
4828 BOOL aForce)
4829{
4830 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4831 aName.c_str(), aControllerPort, aForce));
4832
4833 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4834}
4835
4836HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4837 LONG aControllerPort,
4838 LONG aDevice,
4839 const ComPtr<IMedium> &aMedium,
4840 BOOL aForce)
4841{
4842 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4843 aName.c_str(), aControllerPort, aDevice, aForce));
4844
4845 // request the host lock first, since might be calling Host methods for getting host drives;
4846 // next, protect the media tree all the while we're in here, as well as our member variables
4847 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4848 this->lockHandle(),
4849 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4850
4851 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4852 if (FAILED(hrc)) return hrc;
4853
4854 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4855 aName,
4856 aControllerPort,
4857 aDevice);
4858 if (pAttach.isNull())
4859 return setError(VBOX_E_OBJECT_NOT_FOUND,
4860 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4861 aDevice, aControllerPort, aName.c_str());
4862
4863 /* Remember previously mounted medium. The medium before taking the
4864 * backup is not necessarily the same thing. */
4865 ComObjPtr<Medium> oldmedium;
4866 oldmedium = pAttach->i_getMedium();
4867
4868 IMedium *iM = aMedium;
4869 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4870 if (aMedium && pMedium.isNull())
4871 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4872
4873 /* Check if potential medium is already mounted */
4874 if (pMedium == oldmedium)
4875 return S_OK;
4876
4877 AutoCaller mediumCaller(pMedium);
4878 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
4879
4880 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4881 if (pMedium)
4882 {
4883 DeviceType_T mediumType = pAttach->i_getType();
4884 switch (mediumType)
4885 {
4886 case DeviceType_DVD:
4887 case DeviceType_Floppy:
4888 break;
4889
4890 default:
4891 return setError(VBOX_E_INVALID_OBJECT_STATE,
4892 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4893 aControllerPort,
4894 aDevice,
4895 aName.c_str());
4896 }
4897 }
4898
4899 i_setModified(IsModified_Storage);
4900 mMediumAttachments.backup();
4901
4902 {
4903 // The backup operation makes the pAttach reference point to the
4904 // old settings. Re-get the correct reference.
4905 pAttach = i_findAttachment(*mMediumAttachments.data(),
4906 aName,
4907 aControllerPort,
4908 aDevice);
4909 if (!oldmedium.isNull())
4910 oldmedium->i_removeBackReference(mData->mUuid);
4911 if (!pMedium.isNull())
4912 {
4913 pMedium->i_addBackReference(mData->mUuid);
4914
4915 mediumLock.release();
4916 multiLock.release();
4917 i_addMediumToRegistry(pMedium);
4918 multiLock.acquire();
4919 mediumLock.acquire();
4920 }
4921
4922 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4923 pAttach->i_updateMedium(pMedium);
4924 }
4925
4926 i_setModified(IsModified_Storage);
4927
4928 mediumLock.release();
4929 multiLock.release();
4930 hrc = i_onMediumChange(pAttach, aForce);
4931 multiLock.acquire();
4932 mediumLock.acquire();
4933
4934 /* On error roll back this change only. */
4935 if (FAILED(hrc))
4936 {
4937 if (!pMedium.isNull())
4938 pMedium->i_removeBackReference(mData->mUuid);
4939 pAttach = i_findAttachment(*mMediumAttachments.data(),
4940 aName,
4941 aControllerPort,
4942 aDevice);
4943 /* If the attachment is gone in the meantime, bail out. */
4944 if (pAttach.isNull())
4945 return hrc;
4946 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4947 if (!oldmedium.isNull())
4948 oldmedium->i_addBackReference(mData->mUuid);
4949 pAttach->i_updateMedium(oldmedium);
4950 }
4951
4952 mediumLock.release();
4953 multiLock.release();
4954
4955 /* Save modified registries, but skip this machine as it's the caller's
4956 * job to save its settings like all other settings changes. */
4957 mParent->i_unmarkRegistryModified(i_getId());
4958 mParent->i_saveModifiedRegistries();
4959
4960 return hrc;
4961}
4962HRESULT Machine::getMedium(const com::Utf8Str &aName,
4963 LONG aControllerPort,
4964 LONG aDevice,
4965 ComPtr<IMedium> &aMedium)
4966{
4967 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4968 aName.c_str(), aControllerPort, aDevice));
4969
4970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4971
4972 aMedium = NULL;
4973
4974 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4975 aName,
4976 aControllerPort,
4977 aDevice);
4978 if (pAttach.isNull())
4979 return setError(VBOX_E_OBJECT_NOT_FOUND,
4980 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4981 aDevice, aControllerPort, aName.c_str());
4982
4983 aMedium = pAttach->i_getMedium();
4984
4985 return S_OK;
4986}
4987
4988HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4989{
4990 if (aSlot < RT_ELEMENTS(mSerialPorts))
4991 {
4992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4993 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4994 return S_OK;
4995 }
4996 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4997}
4998
4999HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
5000{
5001 if (aSlot < RT_ELEMENTS(mParallelPorts))
5002 {
5003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5004 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5005 return S_OK;
5006 }
5007 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
5008}
5009
5010
5011HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
5012{
5013 /* Do not assert if slot is out of range, just return the advertised
5014 status. testdriver/vbox.py triggers this in logVmInfo. */
5015 if (aSlot >= mNetworkAdapters.size())
5016 return setError(E_INVALIDARG,
5017 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
5018 aSlot, mNetworkAdapters.size());
5019
5020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5021
5022 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
5023
5024 return S_OK;
5025}
5026
5027HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
5028{
5029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5030
5031 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
5032 size_t i = 0;
5033 for (settings::StringsMap::const_iterator
5034 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5035 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5036 ++it, ++i)
5037 aKeys[i] = it->first;
5038
5039 return S_OK;
5040}
5041
5042 /**
5043 * @note Locks this object for reading.
5044 */
5045HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
5046 com::Utf8Str &aValue)
5047{
5048 /* start with nothing found */
5049 aValue = "";
5050
5051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5052
5053 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5054 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5055 // found:
5056 aValue = it->second; // source is a Utf8Str
5057
5058 /* return the result to caller (may be empty) */
5059 return S_OK;
5060}
5061
5062 /**
5063 * @note Locks mParent for writing + this object for writing.
5064 */
5065HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5066{
5067 /* Because control characters in aKey have caused problems in the settings
5068 * they are rejected unless the key should be deleted. */
5069 if (!aValue.isEmpty())
5070 {
5071 for (size_t i = 0; i < aKey.length(); ++i)
5072 {
5073 char ch = aKey[i];
5074 if (RTLocCIsCntrl(ch))
5075 return E_INVALIDARG;
5076 }
5077 }
5078
5079 Utf8Str strOldValue; // empty
5080
5081 // locking note: we only hold the read lock briefly to look up the old value,
5082 // then release it and call the onExtraCanChange callbacks. There is a small
5083 // chance of a race insofar as the callback might be called twice if two callers
5084 // change the same key at the same time, but that's a much better solution
5085 // than the deadlock we had here before. The actual changing of the extradata
5086 // is then performed under the write lock and race-free.
5087
5088 // look up the old value first; if nothing has changed then we need not do anything
5089 {
5090 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5091
5092 // For snapshots don't even think about allowing changes, extradata
5093 // is global for a machine, so there is nothing snapshot specific.
5094 if (i_isSnapshotMachine())
5095 return setError(VBOX_E_INVALID_VM_STATE,
5096 tr("Cannot set extradata for a snapshot"));
5097
5098 // check if the right IMachine instance is used
5099 if (mData->mRegistered && !i_isSessionMachine())
5100 return setError(VBOX_E_INVALID_VM_STATE,
5101 tr("Cannot set extradata for an immutable machine"));
5102
5103 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5104 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5105 strOldValue = it->second;
5106 }
5107
5108 bool fChanged;
5109 if ((fChanged = (strOldValue != aValue)))
5110 {
5111 // ask for permission from all listeners outside the locks;
5112 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5113 // lock to copy the list of callbacks to invoke
5114 Bstr bstrError;
5115 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
5116 {
5117 const char *sep = bstrError.isEmpty() ? "" : ": ";
5118 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
5119 return setError(E_ACCESSDENIED,
5120 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5121 aKey.c_str(),
5122 aValue.c_str(),
5123 sep,
5124 bstrError.raw());
5125 }
5126
5127 // data is changing and change not vetoed: then write it out under the lock
5128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5129
5130 if (aValue.isEmpty())
5131 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5132 else
5133 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5134 // creates a new key if needed
5135
5136 bool fNeedsGlobalSaveSettings = false;
5137 // This saving of settings is tricky: there is no "old state" for the
5138 // extradata items at all (unlike all other settings), so the old/new
5139 // settings comparison would give a wrong result!
5140 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
5141
5142 if (fNeedsGlobalSaveSettings)
5143 {
5144 // save the global settings; for that we should hold only the VirtualBox lock
5145 alock.release();
5146 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5147 mParent->i_saveSettings();
5148 }
5149 }
5150
5151 // fire notification outside the lock
5152 if (fChanged)
5153 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
5154
5155 return S_OK;
5156}
5157
5158HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5159{
5160 aProgress = NULL;
5161 NOREF(aSettingsFilePath);
5162 ReturnComNotImplemented();
5163}
5164
5165HRESULT Machine::saveSettings()
5166{
5167 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5168
5169 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5170 if (FAILED(hrc)) return hrc;
5171
5172 /* the settings file path may never be null */
5173 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5174
5175 /* save all VM data excluding snapshots */
5176 bool fNeedsGlobalSaveSettings = false;
5177 hrc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
5178 mlock.release();
5179
5180 if (SUCCEEDED(hrc) && fNeedsGlobalSaveSettings)
5181 {
5182 // save the global settings; for that we should hold only the VirtualBox lock
5183 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5184 hrc = mParent->i_saveSettings();
5185 }
5186
5187 return hrc;
5188}
5189
5190
5191HRESULT Machine::discardSettings()
5192{
5193 /*
5194 * We need to take the machine list lock here as well as the machine one
5195 * or we'll get into trouble should any media stuff require rolling back.
5196 *
5197 * Details:
5198 *
5199 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5200 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5201 * 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]
5202 * 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
5203 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5204 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5205 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5206 * 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
5207 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5208 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5209 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5210 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5211 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5212 * 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]
5213 * 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] (*)
5214 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5215 * 0:005> k
5216 * # Child-SP RetAddr Call Site
5217 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5218 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5219 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5220 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5221 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5222 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5223 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5224 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5225 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5226 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5227 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5228 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5229 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5230 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5231 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5232 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5233 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5234 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5235 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5236 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5237 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5238 *
5239 */
5240 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5242
5243 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5244 if (FAILED(hrc)) return hrc;
5245
5246 /*
5247 * during this rollback, the session will be notified if data has
5248 * been actually changed
5249 */
5250 i_rollback(true /* aNotify */);
5251
5252 return S_OK;
5253}
5254
5255/** @note Locks objects! */
5256HRESULT Machine::unregister(AutoCaller &autoCaller,
5257 CleanupMode_T aCleanupMode,
5258 std::vector<ComPtr<IMedium> > &aMedia)
5259{
5260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5261
5262 Guid id(i_getId());
5263
5264 if (mData->mSession.mState != SessionState_Unlocked)
5265 return setError(VBOX_E_INVALID_OBJECT_STATE,
5266 tr("Cannot unregister the machine '%s' while it is locked"),
5267 mUserData->s.strName.c_str());
5268
5269 // wait for state dependents to drop to zero
5270 i_ensureNoStateDependencies(alock);
5271
5272 if (!mData->mAccessible)
5273 {
5274 // inaccessible machines can only be unregistered; uninitialize ourselves
5275 // here because currently there may be no unregistered that are inaccessible
5276 // (this state combination is not supported). Note releasing the caller and
5277 // leaving the lock before calling uninit()
5278 alock.release();
5279 autoCaller.release();
5280
5281 uninit();
5282
5283 mParent->i_unregisterMachine(this, CleanupMode_UnregisterOnly, id);
5284 // calls VirtualBox::i_saveSettings()
5285
5286 return S_OK;
5287 }
5288
5289 HRESULT hrc = S_OK;
5290 mData->llFilesToDelete.clear();
5291
5292 if (!mSSData->strStateFilePath.isEmpty())
5293 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5294
5295 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
5296 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
5297 mData->llFilesToDelete.push_back(strNVRAMFile);
5298
5299 // This list collects the medium objects from all medium attachments
5300 // which we will detach from the machine and its snapshots, in a specific
5301 // order which allows for closing all media without getting "media in use"
5302 // errors, simply by going through the list from the front to the back:
5303 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5304 // and must be closed before the parent media from the snapshots, or closing the parents
5305 // will fail because they still have children);
5306 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5307 // the root ("first") snapshot of the machine.
5308 MediaList llMedia;
5309
5310 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5311 && mMediumAttachments->size()
5312 )
5313 {
5314 // we have media attachments: detach them all and add the Medium objects to our list
5315 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5316 }
5317
5318 if (mData->mFirstSnapshot)
5319 {
5320 // add the media from the medium attachments of the snapshots to
5321 // llMedia as well, after the "main" machine media;
5322 // Snapshot::uninitAll() calls Machine::detachAllMedia() for each
5323 // snapshot machine, depth first.
5324
5325 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5326 MachineState_T oldState = mData->mMachineState;
5327 mData->mMachineState = MachineState_DeletingSnapshot;
5328
5329 // make a copy of the first snapshot reference so the refcount does not
5330 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
5331 // (would hang due to the AutoCaller voodoo)
5332 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5333
5334 // GO!
5335 pFirstSnapshot->i_uninitAll(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5336
5337 mData->mMachineState = oldState;
5338 }
5339
5340 if (FAILED(hrc))
5341 {
5342 i_rollbackMedia();
5343 return hrc;
5344 }
5345
5346 // commit all the media changes made above
5347 i_commitMedia();
5348
5349 mData->mRegistered = false;
5350
5351 // machine lock no longer needed
5352 alock.release();
5353
5354 /* Make sure that the settings of the current VM are not saved, because
5355 * they are rather crippled at this point to meet the cleanup expectations
5356 * and there's no point destroying the VM config on disk just because. */
5357 mParent->i_unmarkRegistryModified(id);
5358
5359 // return media to caller
5360 aMedia.resize(llMedia.size());
5361 size_t i = 0;
5362 for (MediaList::const_iterator
5363 it = llMedia.begin();
5364 it != llMedia.end();
5365 ++it, ++i)
5366 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5367
5368 mParent->i_unregisterMachine(this, aCleanupMode, id);
5369 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5370
5371 return S_OK;
5372}
5373
5374/**
5375 * Task record for deleting a machine config.
5376 */
5377class Machine::DeleteConfigTask
5378 : public Machine::Task
5379{
5380public:
5381 DeleteConfigTask(Machine *m,
5382 Progress *p,
5383 const Utf8Str &t,
5384 const RTCList<ComPtr<IMedium> > &llMediums,
5385 const StringsList &llFilesToDelete)
5386 : Task(m, p, t),
5387 m_llMediums(llMediums),
5388 m_llFilesToDelete(llFilesToDelete)
5389 {}
5390
5391private:
5392 void handler()
5393 {
5394 try
5395 {
5396 m_pMachine->i_deleteConfigHandler(*this);
5397 }
5398 catch (...)
5399 {
5400 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5401 }
5402 }
5403
5404 RTCList<ComPtr<IMedium> > m_llMediums;
5405 StringsList m_llFilesToDelete;
5406
5407 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5408};
5409
5410/**
5411 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5412 * SessionMachine::taskHandler().
5413 *
5414 * @note Locks this object for writing.
5415 *
5416 * @param task
5417 * @return
5418 */
5419void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5420{
5421 LogFlowThisFuncEnter();
5422
5423 AutoCaller autoCaller(this);
5424 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5425 if (FAILED(autoCaller.hrc()))
5426 {
5427 /* we might have been uninitialized because the session was accidentally
5428 * closed by the client, so don't assert */
5429 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
5430 task.m_pProgress->i_notifyComplete(hrc);
5431 LogFlowThisFuncLeave();
5432 return;
5433 }
5434
5435 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5436
5437 HRESULT hrc;
5438 try
5439 {
5440 ULONG uLogHistoryCount = 3;
5441 ComPtr<ISystemProperties> systemProperties;
5442 hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5443 if (FAILED(hrc)) throw hrc;
5444
5445 if (!systemProperties.isNull())
5446 {
5447 hrc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5448 if (FAILED(hrc)) throw hrc;
5449 }
5450
5451 MachineState_T oldState = mData->mMachineState;
5452 i_setMachineState(MachineState_SettingUp);
5453 alock.release();
5454 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5455 {
5456 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5457 {
5458 AutoCaller mac(pMedium);
5459 if (FAILED(mac.hrc())) throw mac.hrc();
5460 Utf8Str strLocation = pMedium->i_getLocationFull();
5461 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5462 hrc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5463 if (FAILED(hrc)) throw hrc;
5464 }
5465 if (pMedium->i_isMediumFormatFile())
5466 {
5467 ComPtr<IProgress> pProgress2;
5468 hrc = pMedium->DeleteStorage(pProgress2.asOutParam());
5469 if (FAILED(hrc)) throw hrc;
5470 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5471 if (FAILED(hrc)) throw hrc;
5472 }
5473
5474 /* Close the medium, deliberately without checking the return
5475 * code, and without leaving any trace in the error info, as
5476 * a failure here is a very minor issue, which shouldn't happen
5477 * as above we even managed to delete the medium. */
5478 {
5479 ErrorInfoKeeper eik;
5480 pMedium->Close();
5481 }
5482 }
5483 i_setMachineState(oldState);
5484 alock.acquire();
5485
5486 // delete the files pushed on the task list by Machine::Delete()
5487 // (this includes saved states of the machine and snapshots and
5488 // medium storage files from the IMedium list passed in, and the
5489 // machine XML file)
5490 for (StringsList::const_iterator
5491 it = task.m_llFilesToDelete.begin();
5492 it != task.m_llFilesToDelete.end();
5493 ++it)
5494 {
5495 const Utf8Str &strFile = *it;
5496 LogFunc(("Deleting file %s\n", strFile.c_str()));
5497 hrc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5498 if (FAILED(hrc)) throw hrc;
5499 i_deleteFile(strFile);
5500 }
5501
5502 hrc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5503 if (FAILED(hrc)) throw hrc;
5504
5505 /* delete the settings only when the file actually exists */
5506 if (mData->pMachineConfigFile->fileExists())
5507 {
5508 /* Delete any backup or uncommitted XML files. Ignore failures.
5509 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5510 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5511 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5512 i_deleteFile(otherXml, true /* fIgnoreFailures */);
5513 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5514 i_deleteFile(otherXml, true /* fIgnoreFailures */);
5515
5516 /* delete the Logs folder, nothing important should be left
5517 * there (we don't check for errors because the user might have
5518 * some private files there that we don't want to delete) */
5519 Utf8Str logFolder;
5520 getLogFolder(logFolder);
5521 Assert(logFolder.length());
5522 if (RTDirExists(logFolder.c_str()))
5523 {
5524 /* Delete all VBox.log[.N] files from the Logs folder
5525 * (this must be in sync with the rotation logic in
5526 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5527 * files that may have been created by the GUI. */
5528 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5529 i_deleteFile(log, true /* fIgnoreFailures */);
5530 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5531 i_deleteFile(log, true /* fIgnoreFailures */);
5532 for (ULONG i = uLogHistoryCount; i > 0; i--)
5533 {
5534 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5535 i_deleteFile(log, true /* fIgnoreFailures */);
5536 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5537 i_deleteFile(log, true /* fIgnoreFailures */);
5538 }
5539 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5540 i_deleteFile(log, true /* fIgnoreFailures */);
5541#if defined(RT_OS_WINDOWS)
5542 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5543 i_deleteFile(log, true /* fIgnoreFailures */);
5544 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5545 i_deleteFile(log, true /* fIgnoreFailures */);
5546#endif
5547
5548 RTDirRemove(logFolder.c_str());
5549 }
5550
5551 /* delete the Snapshots folder, nothing important should be left
5552 * there (we don't check for errors because the user might have
5553 * some private files there that we don't want to delete) */
5554 Utf8Str strFullSnapshotFolder;
5555 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5556 Assert(!strFullSnapshotFolder.isEmpty());
5557 if (RTDirExists(strFullSnapshotFolder.c_str()))
5558 RTDirRemove(strFullSnapshotFolder.c_str());
5559
5560 // delete the directory that contains the settings file, but only
5561 // if it matches the VM name
5562 Utf8Str settingsDir;
5563 if (i_isInOwnDir(&settingsDir))
5564 RTDirRemove(settingsDir.c_str());
5565 }
5566
5567 alock.release();
5568
5569 mParent->i_saveModifiedRegistries();
5570 }
5571 catch (HRESULT hrcXcpt)
5572 {
5573 hrc = hrcXcpt;
5574 }
5575
5576 task.m_pProgress->i_notifyComplete(hrc);
5577
5578 LogFlowThisFuncLeave();
5579}
5580
5581HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5582{
5583 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5584
5585 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5586 if (FAILED(hrc)) return hrc;
5587
5588 if (mData->mRegistered)
5589 return setError(VBOX_E_INVALID_VM_STATE,
5590 tr("Cannot delete settings of a registered machine"));
5591
5592 // collect files to delete
5593 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5594 // machine config file
5595 if (mData->pMachineConfigFile->fileExists())
5596 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5597 // backup of machine config file
5598 Utf8Str strTmp(mData->m_strConfigFileFull);
5599 strTmp.append("-prev");
5600 if (RTFileExists(strTmp.c_str()))
5601 llFilesToDelete.push_back(strTmp);
5602
5603 RTCList<ComPtr<IMedium> > llMediums;
5604 for (size_t i = 0; i < aMedia.size(); ++i)
5605 {
5606 IMedium *pIMedium(aMedia[i]);
5607 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5608 if (pMedium.isNull())
5609 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
5610 SafeArray<BSTR> ids;
5611 hrc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5612 if (FAILED(hrc)) return hrc;
5613 /* At this point the medium should not have any back references
5614 * anymore. If it has it is attached to another VM and *must* not
5615 * deleted. */
5616 if (ids.size() < 1)
5617 llMediums.append(pMedium);
5618 }
5619
5620 ComObjPtr<Progress> pProgress;
5621 pProgress.createObject();
5622 hrc = pProgress->init(i_getVirtualBox(),
5623 static_cast<IMachine*>(this) /* aInitiator */,
5624 tr("Deleting files"),
5625 true /* fCancellable */,
5626 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5627 tr("Collecting file inventory"));
5628 if (FAILED(hrc))
5629 return hrc;
5630
5631 /* create and start the task on a separate thread (note that it will not
5632 * start working until we release alock) */
5633 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5634 hrc = pTask->createThread();
5635 pTask = NULL;
5636 if (FAILED(hrc))
5637 return hrc;
5638
5639 pProgress.queryInterfaceTo(aProgress.asOutParam());
5640
5641 LogFlowFuncLeave();
5642
5643 return S_OK;
5644}
5645
5646HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5647{
5648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5649
5650 ComObjPtr<Snapshot> pSnapshot;
5651 HRESULT hrc;
5652
5653 if (aNameOrId.isEmpty())
5654 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5655 hrc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5656 else
5657 {
5658 Guid uuid(aNameOrId);
5659 if (uuid.isValid())
5660 hrc = i_findSnapshotById(uuid,