VirtualBox

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

Last change on this file since 98262 was 98262, checked in by vboxsync, 22 months ago

Main: rc() -> hrc()/vrc(). bugref:10223

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 602.1 KB
Line 
1/* $Id: MachineImpl.cpp 98262 2023-01-24 01:42:14Z 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 rc = initImpl(aParent, strConfigFile);
388 if (FAILED(rc)) return rc;
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 HRESULT 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 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
453 if (FAILED(rc)) return rc;
454
455 if (SUCCEEDED(rc))
456 {
457 // create an empty machine config
458 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
459
460 rc = initDataAndChildObjects();
461 }
462
463 if (SUCCEEDED(rc))
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 rc = COMSETTER(SnapshotFolder)(NULL);
488 AssertComRC(rc);
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 rc = aOsType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
503 AssertComRC(rc);
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 rc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
543#endif
544 if (SUCCEEDED(rc))
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(rc))
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, rc=%08X\n",
574 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
575 mData->mRegistered,
576 mData->mAccessible,
577 rc));
578
579 LogFlowThisFuncLeave();
580
581 return rc;
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 rc = initImpl(aParent, strConfigFile);
629 if (FAILED(rc)) return rc;
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 rc = i_registeredInit();
638 // this calls initDataAndChildObjects() and loadSettings()
639 }
640 else
641 {
642 // opening an unregistered VM (VirtualBox::OpenMachine()):
643 rc = initDataAndChildObjects();
644
645 if (SUCCEEDED(rc))
646 {
647 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
648 mData->mAccessible = TRUE;
649
650 try
651 {
652 // load and parse machine XML; this will throw on XML or logic errors
653 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
654 pCryptoIf,
655 strPassword.c_str());
656
657 // reject VM UUID duplicates, they can happen if someone
658 // tries to register an already known VM config again
659 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
660 true /* fPermitInaccessible */,
661 false /* aDoSetError */,
662 NULL) != VBOX_E_OBJECT_NOT_FOUND)
663 {
664 throw setError(E_FAIL,
665 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
666 mData->m_strConfigFile.c_str());
667 }
668
669 // use UUID from machine config
670 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
671
672#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
673 // No exception is thrown if config is encrypted, allowing us to get the uuid and the encryption fields.
674 // We fill in the encryptions fields, and the rest will be filled in if all data parsed.
675 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
676 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
677#endif
678
679 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
680 {
681 // We just set the inaccessible state and fill the error info allowing the caller
682 // to register the machine with encrypted config even if the password is incorrect
683 mData->mAccessible = FALSE;
684
685 /* fetch the current error info */
686 mData->mAccessError = com::ErrorInfo();
687
688 setError(VBOX_E_PASSWORD_INCORRECT,
689 tr("Decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
690 mData->pMachineConfigFile->uuid.raw());
691 }
692 else
693 {
694#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
695 if (strPassword.isNotEmpty())
696 {
697 size_t cbKey = strPassword.length() + 1; /* Include terminator */
698 const uint8_t *pbKey = (const uint8_t *)strPassword.c_str();
699 mData->mpKeyStore->addSecretKey(mData->mstrKeyId, pbKey, cbKey);
700 }
701#endif
702
703 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
704 NULL /* puuidRegistry */);
705 if (FAILED(rc)) throw rc;
706
707 /* At this point the changing of the current state modification
708 * flag is allowed. */
709 i_allowStateModification();
710
711 i_commit();
712 }
713 }
714 catch (HRESULT err)
715 {
716 /* we assume that error info is set by the thrower */
717 rc = err;
718 }
719 catch (...)
720 {
721 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
722 }
723 }
724 }
725
726 /* Confirm a successful initialization when it's the case */
727 if (SUCCEEDED(rc))
728 {
729 if (mData->mAccessible)
730 autoInitSpan.setSucceeded();
731 else
732 {
733 autoInitSpan.setLimited();
734
735 // uninit media from this machine's media registry, or else
736 // reloading the settings will fail
737 mParent->i_unregisterMachineMedia(i_getId());
738 }
739 }
740
741#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
742 if (pCryptoIf)
743 {
744 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
745 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
746 }
747#endif
748
749 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
750 "rc=%08X\n",
751 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
752 mData->mRegistered, mData->mAccessible, rc));
753
754 LogFlowThisFuncLeave();
755
756 return rc;
757}
758
759/**
760 * Initializes a new instance from a machine config that is already in memory
761 * (import OVF case). Since we are importing, the UUID in the machine
762 * config is ignored and we always generate a fresh one.
763 *
764 * @param aParent Associated parent object.
765 * @param strName Name for the new machine; this overrides what is specified in config.
766 * @param strSettingsFilename File name of .vbox file.
767 * @param config Machine configuration loaded and parsed from XML.
768 *
769 * @return Success indicator. if not S_OK, the machine object is invalid
770 */
771HRESULT Machine::init(VirtualBox *aParent,
772 const Utf8Str &strName,
773 const Utf8Str &strSettingsFilename,
774 const settings::MachineConfigFile &config)
775{
776 LogFlowThisFuncEnter();
777
778 /* Enclose the state transition NotReady->InInit->Ready */
779 AutoInitSpan autoInitSpan(this);
780 AssertReturn(autoInitSpan.isOk(), E_FAIL);
781
782 HRESULT rc = initImpl(aParent, strSettingsFilename);
783 if (FAILED(rc)) return rc;
784
785 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
786 if (FAILED(rc)) return rc;
787
788 rc = initDataAndChildObjects();
789
790 if (SUCCEEDED(rc))
791 {
792 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
793 mData->mAccessible = TRUE;
794
795 // create empty machine config for instance data
796 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
797
798 // generate fresh UUID, ignore machine config
799 unconst(mData->mUuid).create();
800
801 rc = i_loadMachineDataFromSettings(config,
802 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
803
804 // override VM name as well, it may be different
805 mUserData->s.strName = strName;
806
807 if (SUCCEEDED(rc))
808 {
809 /* At this point the changing of the current state modification
810 * flag is allowed. */
811 i_allowStateModification();
812
813 /* commit all changes made during the initialization */
814 i_commit();
815 }
816 }
817
818 /* Confirm a successful initialization when it's the case */
819 if (SUCCEEDED(rc))
820 {
821 if (mData->mAccessible)
822 autoInitSpan.setSucceeded();
823 else
824 {
825 /* Ignore all errors from unregistering, they would destroy
826- * the more interesting error information we already have,
827- * pinpointing the issue with the VM config. */
828 ErrorInfoKeeper eik;
829
830 autoInitSpan.setLimited();
831
832 // uninit media from this machine's media registry, or else
833 // reloading the settings will fail
834 mParent->i_unregisterMachineMedia(i_getId());
835 }
836 }
837
838 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
839 "rc=%08X\n",
840 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
841 mData->mRegistered, mData->mAccessible, rc));
842
843 LogFlowThisFuncLeave();
844
845 return rc;
846}
847
848/**
849 * Shared code between the various init() implementations.
850 * @param aParent The VirtualBox object.
851 * @param strConfigFile Settings file.
852 * @return
853 */
854HRESULT Machine::initImpl(VirtualBox *aParent,
855 const Utf8Str &strConfigFile)
856{
857 LogFlowThisFuncEnter();
858
859 AssertReturn(aParent, E_INVALIDARG);
860 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
861
862 HRESULT rc = S_OK;
863
864 /* share the parent weakly */
865 unconst(mParent) = aParent;
866
867 /* allocate the essential machine data structure (the rest will be
868 * allocated later by initDataAndChildObjects() */
869 mData.allocate();
870
871 /* memorize the config file name (as provided) */
872 mData->m_strConfigFile = strConfigFile;
873
874 /* get the full file name */
875 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
876 if (RT_FAILURE(vrc1))
877 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
878 tr("Invalid machine settings file name '%s' (%Rrc)"),
879 strConfigFile.c_str(),
880 vrc1);
881
882#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
883 /** @todo Only create when the machine is going to be encrypted. */
884 /* Non-pageable memory is not accessible for non-VM process */
885 mData->mpKeyStore = new SecretKeyStore(false /* fKeyBufNonPageable */);
886 AssertReturn(mData->mpKeyStore, E_OUTOFMEMORY);
887#endif
888
889 LogFlowThisFuncLeave();
890
891 return rc;
892}
893
894/**
895 * Tries to create a machine settings file in the path stored in the machine
896 * instance data. Used when a new machine is created to fail gracefully if
897 * the settings file could not be written (e.g. because machine dir is read-only).
898 * @return
899 */
900HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
901{
902 HRESULT rc = S_OK;
903
904 // when we create a new machine, we must be able to create the settings file
905 RTFILE f = NIL_RTFILE;
906 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
907 if ( RT_SUCCESS(vrc)
908 || vrc == VERR_SHARING_VIOLATION
909 )
910 {
911 if (RT_SUCCESS(vrc))
912 RTFileClose(f);
913 if (!fForceOverwrite)
914 rc = setError(VBOX_E_FILE_ERROR,
915 tr("Machine settings file '%s' already exists"),
916 mData->m_strConfigFileFull.c_str());
917 else
918 {
919 /* try to delete the config file, as otherwise the creation
920 * of a new settings file will fail. */
921 i_deleteFile(mData->m_strConfigFileFull.c_str(), false /* fIgnoreFailures */, tr("existing settings file"));
922 }
923 }
924 else if ( vrc != VERR_FILE_NOT_FOUND
925 && vrc != VERR_PATH_NOT_FOUND
926 )
927 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
928 tr("Invalid machine settings file name '%s' (%Rrc)"),
929 mData->m_strConfigFileFull.c_str(),
930 vrc);
931 return rc;
932}
933
934/**
935 * Initializes the registered machine by loading the settings file.
936 * This method is separated from #init() in order to make it possible to
937 * retry the operation after VirtualBox startup instead of refusing to
938 * startup the whole VirtualBox server in case if the settings file of some
939 * registered VM is invalid or inaccessible.
940 *
941 * @note Must be always called from this object's write lock
942 * (unless called from #init() that doesn't need any locking).
943 * @note Locks the mUSBController method for writing.
944 * @note Subclasses must not call this method.
945 */
946HRESULT Machine::i_registeredInit()
947{
948 AssertReturn(!i_isSessionMachine(), E_FAIL);
949 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
950 AssertReturn(mData->mUuid.isValid(), E_FAIL);
951 AssertReturn(!mData->mAccessible, E_FAIL);
952
953 HRESULT rc = initDataAndChildObjects();
954
955 if (SUCCEEDED(rc))
956 {
957 /* Temporarily reset the registered flag in order to let setters
958 * potentially called from loadSettings() succeed (isMutable() used in
959 * all setters will return FALSE for a Machine instance if mRegistered
960 * is TRUE). */
961 mData->mRegistered = FALSE;
962
963 PCVBOXCRYPTOIF pCryptoIf = NULL;
964 SecretKey *pKey = NULL;
965 const char *pszPassword = NULL;
966#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
967 /* Resolve password and cryptographic support interface if machine is encrypted. */
968 if (mData->mstrKeyId.isNotEmpty())
969 {
970 /* Get at the crpytographic interface. */
971 rc = mParent->i_retainCryptoIf(&pCryptoIf);
972 if (SUCCEEDED(rc))
973 {
974 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
975 if (RT_SUCCESS(vrc))
976 pszPassword = (const char *)pKey->getKeyBuffer();
977 else
978 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to retain key for key ID '%s' with %Rrc"),
979 mData->mstrKeyId.c_str(), vrc);
980 }
981 }
982#else
983 RT_NOREF(pKey);
984#endif
985
986 if (SUCCEEDED(rc))
987 {
988 try
989 {
990 // load and parse machine XML; this will throw on XML or logic errors
991 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
992 pCryptoIf, pszPassword);
993
994 if (mData->mUuid != mData->pMachineConfigFile->uuid)
995 throw setError(E_FAIL,
996 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
997 mData->pMachineConfigFile->uuid.raw(),
998 mData->m_strConfigFileFull.c_str(),
999 mData->mUuid.toString().c_str(),
1000 mParent->i_settingsFilePath().c_str());
1001
1002#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1003 // If config is encrypted, no exception is thrown allowing us to get the uuid and the encryption fields.
1004 // We fill in the encryptions fields, and the rest will be filled in if all data parsed
1005 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
1006 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
1007
1008 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
1009 rc = setError(VBOX_E_PASSWORD_INCORRECT,
1010 tr("Config decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
1011 mData->pMachineConfigFile->uuid.raw());
1012 else
1013#endif
1014 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
1015 NULL /* const Guid *puuidRegistry */);
1016 if (FAILED(rc)) throw rc;
1017 }
1018 catch (HRESULT err)
1019 {
1020 /* we assume that error info is set by the thrower */
1021 rc = err;
1022 }
1023 catch (...)
1024 {
1025 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
1026 }
1027
1028 /* Restore the registered flag (even on failure) */
1029 mData->mRegistered = TRUE;
1030 }
1031
1032#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1033 if (pCryptoIf)
1034 mParent->i_releaseCryptoIf(pCryptoIf);
1035 if (pKey)
1036 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
1037#endif
1038 }
1039
1040 if (SUCCEEDED(rc))
1041 {
1042 /* Set mAccessible to TRUE only if we successfully locked and loaded
1043 * the settings file */
1044 mData->mAccessible = TRUE;
1045
1046 /* commit all changes made during loading the settings file */
1047 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
1048 /// @todo r=klaus for some reason the settings loading logic backs up
1049 // the settings, and therefore a commit is needed. Should probably be changed.
1050 }
1051 else
1052 {
1053 /* If the machine is registered, then, instead of returning a
1054 * failure, we mark it as inaccessible and set the result to
1055 * success to give it a try later */
1056
1057 /* fetch the current error info */
1058 mData->mAccessError = com::ErrorInfo();
1059 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
1060
1061 /* rollback all changes */
1062 i_rollback(false /* aNotify */);
1063
1064 // uninit media from this machine's media registry, or else
1065 // reloading the settings will fail
1066 mParent->i_unregisterMachineMedia(i_getId());
1067
1068 /* uninitialize the common part to make sure all data is reset to
1069 * default (null) values */
1070 uninitDataAndChildObjects();
1071
1072 rc = S_OK;
1073 }
1074
1075 return rc;
1076}
1077
1078/**
1079 * Uninitializes the instance.
1080 * Called either from FinalRelease() or by the parent when it gets destroyed.
1081 *
1082 * @note The caller of this method must make sure that this object
1083 * a) doesn't have active callers on the current thread and b) is not locked
1084 * by the current thread; otherwise uninit() will hang either a) due to
1085 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
1086 * a dead-lock caused by this thread waiting for all callers on the other
1087 * threads are done but preventing them from doing so by holding a lock.
1088 */
1089void Machine::uninit()
1090{
1091 LogFlowThisFuncEnter();
1092
1093 Assert(!isWriteLockOnCurrentThread());
1094
1095 Assert(!uRegistryNeedsSaving);
1096 if (uRegistryNeedsSaving)
1097 {
1098 AutoCaller autoCaller(this);
1099 if (SUCCEEDED(autoCaller.hrc()))
1100 {
1101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1102 i_saveSettings(NULL, alock, Machine::SaveS_Force);
1103 }
1104 }
1105
1106 /* Enclose the state transition Ready->InUninit->NotReady */
1107 AutoUninitSpan autoUninitSpan(this);
1108 if (autoUninitSpan.uninitDone())
1109 return;
1110
1111 Assert(!i_isSnapshotMachine());
1112 Assert(!i_isSessionMachine());
1113 Assert(!!mData);
1114
1115 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
1116 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
1117
1118 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1119
1120 if (!mData->mSession.mMachine.isNull())
1121 {
1122 /* Theoretically, this can only happen if the VirtualBox server has been
1123 * terminated while there were clients running that owned open direct
1124 * sessions. Since in this case we are definitely called by
1125 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
1126 * won't happen on the client watcher thread (because it has a
1127 * VirtualBox caller for the duration of the
1128 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
1129 * cannot happen until the VirtualBox caller is released). This is
1130 * important, because SessionMachine::uninit() cannot correctly operate
1131 * after we return from this method (it expects the Machine instance is
1132 * still valid). We'll call it ourselves below.
1133 */
1134 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
1135 (SessionMachine*)mData->mSession.mMachine));
1136
1137 if (Global::IsOnlineOrTransient(mData->mMachineState))
1138 {
1139 Log1WarningThisFunc(("Setting state to Aborted!\n"));
1140 /* set machine state using SessionMachine reimplementation */
1141 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
1142 }
1143
1144 /*
1145 * Uninitialize SessionMachine using public uninit() to indicate
1146 * an unexpected uninitialization.
1147 */
1148 mData->mSession.mMachine->uninit();
1149 /* SessionMachine::uninit() must set mSession.mMachine to null */
1150 Assert(mData->mSession.mMachine.isNull());
1151 }
1152
1153 // uninit media from this machine's media registry, if they're still there
1154 Guid uuidMachine(i_getId());
1155
1156 /* the lock is no more necessary (SessionMachine is uninitialized) */
1157 alock.release();
1158
1159 /* XXX This will fail with
1160 * "cannot be closed because it is still attached to 1 virtual machines"
1161 * because at this point we did not call uninitDataAndChildObjects() yet
1162 * and therefore also removeBackReference() for all these mediums was not called! */
1163
1164 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
1165 mParent->i_unregisterMachineMedia(uuidMachine);
1166
1167 // has machine been modified?
1168 if (mData->flModifications)
1169 {
1170 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
1171 i_rollback(false /* aNotify */);
1172 }
1173
1174 if (mData->mAccessible)
1175 uninitDataAndChildObjects();
1176
1177#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1178 if (mData->mpKeyStore != NULL)
1179 delete mData->mpKeyStore;
1180#endif
1181
1182 /* free the essential data structure last */
1183 mData.free();
1184
1185 LogFlowThisFuncLeave();
1186}
1187
1188// Wrapped IMachine properties
1189/////////////////////////////////////////////////////////////////////////////
1190HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
1191{
1192 /* mParent is constant during life time, no need to lock */
1193 ComObjPtr<VirtualBox> pVirtualBox(mParent);
1194 aParent = pVirtualBox;
1195
1196 return S_OK;
1197}
1198
1199
1200HRESULT Machine::getAccessible(BOOL *aAccessible)
1201{
1202 /* In some cases (medium registry related), it is necessary to be able to
1203 * go through the list of all machines. Happens when an inaccessible VM
1204 * has a sensible medium registry. */
1205 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
1206 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1207
1208 HRESULT rc = S_OK;
1209
1210 if (!mData->mAccessible)
1211 {
1212 /* try to initialize the VM once more if not accessible */
1213
1214 AutoReinitSpan autoReinitSpan(this);
1215 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
1216
1217#ifdef DEBUG
1218 LogFlowThisFunc(("Dumping media backreferences\n"));
1219 mParent->i_dumpAllBackRefs();
1220#endif
1221
1222 if (mData->pMachineConfigFile)
1223 {
1224 // reset the XML file to force loadSettings() (called from i_registeredInit())
1225 // to parse it again; the file might have changed
1226 delete mData->pMachineConfigFile;
1227 mData->pMachineConfigFile = NULL;
1228 }
1229
1230 rc = i_registeredInit();
1231
1232 if (SUCCEEDED(rc) && mData->mAccessible)
1233 {
1234 autoReinitSpan.setSucceeded();
1235
1236 /* make sure interesting parties will notice the accessibility
1237 * state change */
1238 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
1239 mParent->i_onMachineDataChanged(mData->mUuid);
1240 }
1241 }
1242
1243 if (SUCCEEDED(rc))
1244 *aAccessible = mData->mAccessible;
1245
1246 LogFlowThisFuncLeave();
1247
1248 return rc;
1249}
1250
1251HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
1252{
1253 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1254
1255 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
1256 {
1257 /* return shortly */
1258 aAccessError = NULL;
1259 return S_OK;
1260 }
1261
1262 HRESULT rc = S_OK;
1263
1264 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1265 rc = errorInfo.createObject();
1266 if (SUCCEEDED(rc))
1267 {
1268 errorInfo->init(mData->mAccessError.getResultCode(),
1269 mData->mAccessError.getInterfaceID().ref(),
1270 Utf8Str(mData->mAccessError.getComponent()).c_str(),
1271 Utf8Str(mData->mAccessError.getText()));
1272 aAccessError = errorInfo;
1273 }
1274
1275 return rc;
1276}
1277
1278HRESULT Machine::getName(com::Utf8Str &aName)
1279{
1280 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1281
1282 aName = mUserData->s.strName;
1283
1284 return S_OK;
1285}
1286
1287HRESULT Machine::setName(const com::Utf8Str &aName)
1288{
1289 // prohibit setting a UUID only as the machine name, or else it can
1290 // never be found by findMachine()
1291 Guid test(aName);
1292
1293 if (test.isValid())
1294 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1295
1296 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1297
1298 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1299 if (FAILED(rc)) return rc;
1300
1301 i_setModified(IsModified_MachineData);
1302 mUserData.backup();
1303 mUserData->s.strName = aName;
1304
1305 return S_OK;
1306}
1307
1308HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1309{
1310 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1311
1312 aDescription = mUserData->s.strDescription;
1313
1314 return S_OK;
1315}
1316
1317HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1318{
1319 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1320
1321 // this can be done in principle in any state as it doesn't affect the VM
1322 // significantly, but play safe by not messing around while complex
1323 // activities are going on
1324 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1325 if (FAILED(rc)) return rc;
1326
1327 i_setModified(IsModified_MachineData);
1328 mUserData.backup();
1329 mUserData->s.strDescription = aDescription;
1330
1331 return S_OK;
1332}
1333
1334HRESULT Machine::getId(com::Guid &aId)
1335{
1336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1337
1338 aId = mData->mUuid;
1339
1340 return S_OK;
1341}
1342
1343HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1344{
1345 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1346 aGroups.resize(mUserData->s.llGroups.size());
1347 size_t i = 0;
1348 for (StringsList::const_iterator
1349 it = mUserData->s.llGroups.begin();
1350 it != mUserData->s.llGroups.end();
1351 ++it, ++i)
1352 aGroups[i] = (*it);
1353
1354 return S_OK;
1355}
1356
1357HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1358{
1359 StringsList llGroups;
1360 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1361 if (FAILED(rc))
1362 return rc;
1363
1364 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1365
1366 rc = i_checkStateDependency(MutableOrSavedStateDep);
1367 if (FAILED(rc)) return rc;
1368
1369 i_setModified(IsModified_MachineData);
1370 mUserData.backup();
1371 mUserData->s.llGroups = llGroups;
1372
1373 mParent->i_onMachineGroupsChanged(mData->mUuid);
1374 return S_OK;
1375}
1376
1377HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1378{
1379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1380
1381 aOSTypeId = mUserData->s.strOsType;
1382
1383 return S_OK;
1384}
1385
1386HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1387{
1388 /* look up the object by Id to check it is valid */
1389 ComObjPtr<GuestOSType> pGuestOSType;
1390 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1391
1392 /* when setting, always use the "etalon" value for consistency -- lookup
1393 * by ID is case-insensitive and the input value may have different case */
1394 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1395
1396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1397
1398 HRESULT rc = i_checkStateDependency(MutableStateDep);
1399 if (FAILED(rc)) return rc;
1400
1401 i_setModified(IsModified_MachineData);
1402 mUserData.backup();
1403 mUserData->s.strOsType = osTypeId;
1404
1405 return S_OK;
1406}
1407
1408HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1409{
1410 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1411
1412 *aFirmwareType = mHWData->mFirmwareType;
1413
1414 return S_OK;
1415}
1416
1417HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1418{
1419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1420
1421 HRESULT rc = i_checkStateDependency(MutableStateDep);
1422 if (FAILED(rc)) return rc;
1423
1424 i_setModified(IsModified_MachineData);
1425 mHWData.backup();
1426 mHWData->mFirmwareType = aFirmwareType;
1427 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1428 alock.release();
1429
1430 mNvramStore->i_updateNonVolatileStorageFile(strNVRAM);
1431
1432 return S_OK;
1433}
1434
1435HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1436{
1437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1438
1439 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1440
1441 return S_OK;
1442}
1443
1444HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1445{
1446 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1447
1448 HRESULT rc = i_checkStateDependency(MutableStateDep);
1449 if (FAILED(rc)) return rc;
1450
1451 i_setModified(IsModified_MachineData);
1452 mHWData.backup();
1453 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1454
1455 return S_OK;
1456}
1457
1458HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1459{
1460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1461
1462 *aPointingHIDType = mHWData->mPointingHIDType;
1463
1464 return S_OK;
1465}
1466
1467HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1468{
1469 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1470
1471 HRESULT rc = i_checkStateDependency(MutableStateDep);
1472 if (FAILED(rc)) return rc;
1473
1474 i_setModified(IsModified_MachineData);
1475 mHWData.backup();
1476 mHWData->mPointingHIDType = aPointingHIDType;
1477
1478 return S_OK;
1479}
1480
1481HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1482{
1483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1484
1485 *aChipsetType = mHWData->mChipsetType;
1486
1487 return S_OK;
1488}
1489
1490HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1491{
1492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1493
1494 HRESULT rc = i_checkStateDependency(MutableStateDep);
1495 if (FAILED(rc)) return rc;
1496
1497 if (aChipsetType != mHWData->mChipsetType)
1498 {
1499 i_setModified(IsModified_MachineData);
1500 mHWData.backup();
1501 mHWData->mChipsetType = aChipsetType;
1502
1503 // Resize network adapter array, to be finalized on commit/rollback.
1504 // We must not throw away entries yet, otherwise settings are lost
1505 // without a way to roll back.
1506 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1507 size_t oldCount = mNetworkAdapters.size();
1508 if (newCount > oldCount)
1509 {
1510 mNetworkAdapters.resize(newCount);
1511 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1512 {
1513 unconst(mNetworkAdapters[slot]).createObject();
1514 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1515 }
1516 }
1517 }
1518
1519 return S_OK;
1520}
1521
1522HRESULT Machine::getIommuType(IommuType_T *aIommuType)
1523{
1524 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1525
1526 *aIommuType = mHWData->mIommuType;
1527
1528 return S_OK;
1529}
1530
1531HRESULT Machine::setIommuType(IommuType_T aIommuType)
1532{
1533 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1534
1535 HRESULT rc = i_checkStateDependency(MutableStateDep);
1536 if (FAILED(rc)) return rc;
1537
1538 if (aIommuType != mHWData->mIommuType)
1539 {
1540 if (aIommuType == IommuType_Intel)
1541 {
1542#ifndef VBOX_WITH_IOMMU_INTEL
1543 LogRelFunc(("Setting Intel IOMMU when Intel IOMMU support not available!\n"));
1544 return E_UNEXPECTED;
1545#endif
1546 }
1547
1548 i_setModified(IsModified_MachineData);
1549 mHWData.backup();
1550 mHWData->mIommuType = aIommuType;
1551 }
1552
1553 return S_OK;
1554}
1555
1556HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1557{
1558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1559
1560 aParavirtDebug = mHWData->mParavirtDebug;
1561 return S_OK;
1562}
1563
1564HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1565{
1566 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1567
1568 HRESULT rc = i_checkStateDependency(MutableStateDep);
1569 if (FAILED(rc)) return rc;
1570
1571 /** @todo Parse/validate options? */
1572 if (aParavirtDebug != mHWData->mParavirtDebug)
1573 {
1574 i_setModified(IsModified_MachineData);
1575 mHWData.backup();
1576 mHWData->mParavirtDebug = aParavirtDebug;
1577 }
1578
1579 return S_OK;
1580}
1581
1582HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1583{
1584 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1585
1586 *aParavirtProvider = mHWData->mParavirtProvider;
1587
1588 return S_OK;
1589}
1590
1591HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1592{
1593 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1594
1595 HRESULT rc = i_checkStateDependency(MutableStateDep);
1596 if (FAILED(rc)) return rc;
1597
1598 if (aParavirtProvider != mHWData->mParavirtProvider)
1599 {
1600 i_setModified(IsModified_MachineData);
1601 mHWData.backup();
1602 mHWData->mParavirtProvider = aParavirtProvider;
1603 }
1604
1605 return S_OK;
1606}
1607
1608HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1609{
1610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1611
1612 *aParavirtProvider = mHWData->mParavirtProvider;
1613 switch (mHWData->mParavirtProvider)
1614 {
1615 case ParavirtProvider_None:
1616 case ParavirtProvider_HyperV:
1617 case ParavirtProvider_KVM:
1618 case ParavirtProvider_Minimal:
1619 break;
1620
1621 /* Resolve dynamic provider types to the effective types. */
1622 default:
1623 {
1624 ComObjPtr<GuestOSType> pGuestOSType;
1625 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1626 pGuestOSType);
1627 if (FAILED(hrc2) || pGuestOSType.isNull())
1628 {
1629 *aParavirtProvider = ParavirtProvider_None;
1630 break;
1631 }
1632
1633 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1634 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1635
1636 switch (mHWData->mParavirtProvider)
1637 {
1638 case ParavirtProvider_Legacy:
1639 {
1640 if (fOsXGuest)
1641 *aParavirtProvider = ParavirtProvider_Minimal;
1642 else
1643 *aParavirtProvider = ParavirtProvider_None;
1644 break;
1645 }
1646
1647 case ParavirtProvider_Default:
1648 {
1649 if (fOsXGuest)
1650 *aParavirtProvider = ParavirtProvider_Minimal;
1651 else if ( mUserData->s.strOsType == "Windows11_64"
1652 || mUserData->s.strOsType == "Windows10"
1653 || mUserData->s.strOsType == "Windows10_64"
1654 || mUserData->s.strOsType == "Windows81"
1655 || mUserData->s.strOsType == "Windows81_64"
1656 || mUserData->s.strOsType == "Windows8"
1657 || mUserData->s.strOsType == "Windows8_64"
1658 || mUserData->s.strOsType == "Windows7"
1659 || mUserData->s.strOsType == "Windows7_64"
1660 || mUserData->s.strOsType == "WindowsVista"
1661 || mUserData->s.strOsType == "WindowsVista_64"
1662 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1663 || mUserData->s.strOsType.startsWith("Windows201"))
1664 && mUserData->s.strOsType.endsWith("_64"))
1665 || mUserData->s.strOsType == "Windows2012"
1666 || mUserData->s.strOsType == "Windows2012_64"
1667 || mUserData->s.strOsType == "Windows2008"
1668 || mUserData->s.strOsType == "Windows2008_64")
1669 {
1670 *aParavirtProvider = ParavirtProvider_HyperV;
1671 }
1672 else if (guestTypeFamilyId == "Linux" &&
1673 mUserData->s.strOsType != "Linux22" && // Linux22 and Linux24{_64} excluded as they're too old
1674 mUserData->s.strOsType != "Linux24" && // to have any KVM paravirtualization support.
1675 mUserData->s.strOsType != "Linux24_64")
1676 {
1677 *aParavirtProvider = ParavirtProvider_KVM;
1678 }
1679 else
1680 *aParavirtProvider = ParavirtProvider_None;
1681 break;
1682 }
1683
1684 default: AssertFailedBreak(); /* Shut up MSC. */
1685 }
1686 break;
1687 }
1688 }
1689
1690 Assert( *aParavirtProvider == ParavirtProvider_None
1691 || *aParavirtProvider == ParavirtProvider_Minimal
1692 || *aParavirtProvider == ParavirtProvider_HyperV
1693 || *aParavirtProvider == ParavirtProvider_KVM);
1694 return S_OK;
1695}
1696
1697HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1698{
1699 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1700
1701 aHardwareVersion = mHWData->mHWVersion;
1702
1703 return S_OK;
1704}
1705
1706HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1707{
1708 /* check known version */
1709 Utf8Str hwVersion = aHardwareVersion;
1710 if ( hwVersion.compare("1") != 0
1711 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1712 return setError(E_INVALIDARG,
1713 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1714
1715 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1716
1717 HRESULT rc = i_checkStateDependency(MutableStateDep);
1718 if (FAILED(rc)) return rc;
1719
1720 i_setModified(IsModified_MachineData);
1721 mHWData.backup();
1722 mHWData->mHWVersion = aHardwareVersion;
1723
1724 return S_OK;
1725}
1726
1727HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1728{
1729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1730
1731 if (!mHWData->mHardwareUUID.isZero())
1732 aHardwareUUID = mHWData->mHardwareUUID;
1733 else
1734 aHardwareUUID = mData->mUuid;
1735
1736 return S_OK;
1737}
1738
1739HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1740{
1741 if (!aHardwareUUID.isValid())
1742 return E_INVALIDARG;
1743
1744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1745
1746 HRESULT rc = i_checkStateDependency(MutableStateDep);
1747 if (FAILED(rc)) return rc;
1748
1749 i_setModified(IsModified_MachineData);
1750 mHWData.backup();
1751 if (aHardwareUUID == mData->mUuid)
1752 mHWData->mHardwareUUID.clear();
1753 else
1754 mHWData->mHardwareUUID = aHardwareUUID;
1755
1756 return S_OK;
1757}
1758
1759HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1760{
1761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1762
1763 *aMemorySize = mHWData->mMemorySize;
1764
1765 return S_OK;
1766}
1767
1768HRESULT Machine::setMemorySize(ULONG aMemorySize)
1769{
1770 /* check RAM limits */
1771 if ( aMemorySize < MM_RAM_MIN_IN_MB
1772 || aMemorySize > MM_RAM_MAX_IN_MB
1773 )
1774 return setError(E_INVALIDARG,
1775 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1776 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1777
1778 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1779
1780 HRESULT rc = i_checkStateDependency(MutableStateDep);
1781 if (FAILED(rc)) return rc;
1782
1783 i_setModified(IsModified_MachineData);
1784 mHWData.backup();
1785 mHWData->mMemorySize = aMemorySize;
1786
1787 return S_OK;
1788}
1789
1790HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1791{
1792 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1793
1794 *aCPUCount = mHWData->mCPUCount;
1795
1796 return S_OK;
1797}
1798
1799HRESULT Machine::setCPUCount(ULONG aCPUCount)
1800{
1801 /* check CPU limits */
1802 if ( aCPUCount < SchemaDefs::MinCPUCount
1803 || aCPUCount > SchemaDefs::MaxCPUCount
1804 )
1805 return setError(E_INVALIDARG,
1806 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1807 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1808
1809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1810
1811 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1812 if (mHWData->mCPUHotPlugEnabled)
1813 {
1814 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1815 {
1816 if (mHWData->mCPUAttached[idx])
1817 return setError(E_INVALIDARG,
1818 tr("There is still a CPU attached to socket %lu."
1819 "Detach the CPU before removing the socket"),
1820 aCPUCount, idx+1);
1821 }
1822 }
1823
1824 HRESULT rc = i_checkStateDependency(MutableStateDep);
1825 if (FAILED(rc)) return rc;
1826
1827 i_setModified(IsModified_MachineData);
1828 mHWData.backup();
1829 mHWData->mCPUCount = aCPUCount;
1830
1831 return S_OK;
1832}
1833
1834HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1835{
1836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1837
1838 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1839
1840 return S_OK;
1841}
1842
1843HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1844{
1845 HRESULT rc = S_OK;
1846
1847 /* check throttle limits */
1848 if ( aCPUExecutionCap < 1
1849 || aCPUExecutionCap > 100
1850 )
1851 return setError(E_INVALIDARG,
1852 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1853 aCPUExecutionCap, 1, 100);
1854
1855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1856
1857 rc = i_checkStateDependency(MutableOrRunningStateDep);
1858 if (FAILED(rc)) return rc;
1859
1860 alock.release();
1861 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1862 alock.acquire();
1863 if (FAILED(rc)) return rc;
1864
1865 i_setModified(IsModified_MachineData);
1866 mHWData.backup();
1867 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1868
1869 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1870 if (Global::IsOnline(mData->mMachineState))
1871 i_saveSettings(NULL, alock);
1872
1873 return S_OK;
1874}
1875
1876HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1877{
1878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1879
1880 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1881
1882 return S_OK;
1883}
1884
1885HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1886{
1887 HRESULT rc = S_OK;
1888
1889 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1890
1891 rc = i_checkStateDependency(MutableStateDep);
1892 if (FAILED(rc)) return rc;
1893
1894 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1895 {
1896 if (aCPUHotPlugEnabled)
1897 {
1898 i_setModified(IsModified_MachineData);
1899 mHWData.backup();
1900
1901 /* Add the amount of CPUs currently attached */
1902 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1903 mHWData->mCPUAttached[i] = true;
1904 }
1905 else
1906 {
1907 /*
1908 * We can disable hotplug only if the amount of maximum CPUs is equal
1909 * to the amount of attached CPUs
1910 */
1911 unsigned cCpusAttached = 0;
1912 unsigned iHighestId = 0;
1913
1914 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1915 {
1916 if (mHWData->mCPUAttached[i])
1917 {
1918 cCpusAttached++;
1919 iHighestId = i;
1920 }
1921 }
1922
1923 if ( (cCpusAttached != mHWData->mCPUCount)
1924 || (iHighestId >= mHWData->mCPUCount))
1925 return setError(E_INVALIDARG,
1926 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1927
1928 i_setModified(IsModified_MachineData);
1929 mHWData.backup();
1930 }
1931 }
1932
1933 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1934
1935 return rc;
1936}
1937
1938HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1939{
1940 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1941
1942 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1943
1944 return S_OK;
1945}
1946
1947HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1948{
1949 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1950
1951 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1952 if (SUCCEEDED(hrc))
1953 {
1954 i_setModified(IsModified_MachineData);
1955 mHWData.backup();
1956 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1957 }
1958 return hrc;
1959}
1960
1961HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1962{
1963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1964 aCPUProfile = mHWData->mCpuProfile;
1965 return S_OK;
1966}
1967
1968HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1969{
1970 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1971 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1972 if (SUCCEEDED(hrc))
1973 {
1974 i_setModified(IsModified_MachineData);
1975 mHWData.backup();
1976 /* Empty equals 'host'. */
1977 if (aCPUProfile.isNotEmpty())
1978 mHWData->mCpuProfile = aCPUProfile;
1979 else
1980 mHWData->mCpuProfile = "host";
1981 }
1982 return hrc;
1983}
1984
1985HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1986{
1987#ifdef VBOX_WITH_USB_CARDREADER
1988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1989
1990 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1991
1992 return S_OK;
1993#else
1994 NOREF(aEmulatedUSBCardReaderEnabled);
1995 return E_NOTIMPL;
1996#endif
1997}
1998
1999HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
2000{
2001#ifdef VBOX_WITH_USB_CARDREADER
2002 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2003
2004 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2005 if (FAILED(rc)) return rc;
2006
2007 i_setModified(IsModified_MachineData);
2008 mHWData.backup();
2009 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
2010
2011 return S_OK;
2012#else
2013 NOREF(aEmulatedUSBCardReaderEnabled);
2014 return E_NOTIMPL;
2015#endif
2016}
2017
2018HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
2019{
2020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2021
2022 *aHPETEnabled = mHWData->mHPETEnabled;
2023
2024 return S_OK;
2025}
2026
2027HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
2028{
2029 HRESULT rc = S_OK;
2030
2031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2032
2033 rc = i_checkStateDependency(MutableStateDep);
2034 if (FAILED(rc)) return rc;
2035
2036 i_setModified(IsModified_MachineData);
2037 mHWData.backup();
2038
2039 mHWData->mHPETEnabled = aHPETEnabled;
2040
2041 return rc;
2042}
2043
2044/** @todo this method should not be public */
2045HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2046{
2047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2048
2049 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2050
2051 return S_OK;
2052}
2053
2054/**
2055 * Set the memory balloon size.
2056 *
2057 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2058 * we have to make sure that we never call IGuest from here.
2059 */
2060HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2061{
2062 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2063#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2064 /* check limits */
2065 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2066 return setError(E_INVALIDARG,
2067 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2068 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2069
2070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2071
2072 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
2073 if (FAILED(rc)) return rc;
2074
2075 i_setModified(IsModified_MachineData);
2076 mHWData.backup();
2077 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2078
2079 return S_OK;
2080#else
2081 NOREF(aMemoryBalloonSize);
2082 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2083#endif
2084}
2085
2086HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2087{
2088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2089
2090 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2091 return S_OK;
2092}
2093
2094HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2095{
2096#ifdef VBOX_WITH_PAGE_SHARING
2097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2098
2099 HRESULT rc = i_checkStateDependency(MutableStateDep);
2100 if (FAILED(rc)) return rc;
2101
2102 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2103 i_setModified(IsModified_MachineData);
2104 mHWData.backup();
2105 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2106 return S_OK;
2107#else
2108 NOREF(aPageFusionEnabled);
2109 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2110#endif
2111}
2112
2113HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2114{
2115 /* mBIOSSettings is constant during life time, no need to lock */
2116 aBIOSSettings = mBIOSSettings;
2117
2118 return S_OK;
2119}
2120
2121HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
2122{
2123 /* mTrustedPlatformModule is constant during life time, no need to lock */
2124 aTrustedPlatformModule = mTrustedPlatformModule;
2125
2126 return S_OK;
2127}
2128
2129HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
2130{
2131 /* mNvramStore is constant during life time, no need to lock */
2132 aNvramStore = mNvramStore;
2133
2134 return S_OK;
2135}
2136
2137HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
2138{
2139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2140
2141 aRecordingSettings = mRecordingSettings;
2142
2143 return S_OK;
2144}
2145
2146HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
2147{
2148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2149
2150 aGraphicsAdapter = mGraphicsAdapter;
2151
2152 return S_OK;
2153}
2154
2155HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2156{
2157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2158
2159 switch (aProperty)
2160 {
2161 case CPUPropertyType_PAE:
2162 *aValue = mHWData->mPAEEnabled;
2163 break;
2164
2165 case CPUPropertyType_LongMode:
2166 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2167 *aValue = TRUE;
2168 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2169 *aValue = FALSE;
2170#if HC_ARCH_BITS == 64
2171 else
2172 *aValue = TRUE;
2173#else
2174 else
2175 {
2176 *aValue = FALSE;
2177
2178 ComObjPtr<GuestOSType> pGuestOSType;
2179 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2180 pGuestOSType);
2181 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
2182 {
2183 if (pGuestOSType->i_is64Bit())
2184 {
2185 ComObjPtr<Host> pHost = mParent->i_host();
2186 alock.release();
2187
2188 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2189 if (FAILED(hrc2))
2190 *aValue = FALSE;
2191 }
2192 }
2193 }
2194#endif
2195 break;
2196
2197 case CPUPropertyType_TripleFaultReset:
2198 *aValue = mHWData->mTripleFaultReset;
2199 break;
2200
2201 case CPUPropertyType_APIC:
2202 *aValue = mHWData->mAPIC;
2203 break;
2204
2205 case CPUPropertyType_X2APIC:
2206 *aValue = mHWData->mX2APIC;
2207 break;
2208
2209 case CPUPropertyType_IBPBOnVMExit:
2210 *aValue = mHWData->mIBPBOnVMExit;
2211 break;
2212
2213 case CPUPropertyType_IBPBOnVMEntry:
2214 *aValue = mHWData->mIBPBOnVMEntry;
2215 break;
2216
2217 case CPUPropertyType_SpecCtrl:
2218 *aValue = mHWData->mSpecCtrl;
2219 break;
2220
2221 case CPUPropertyType_SpecCtrlByHost:
2222 *aValue = mHWData->mSpecCtrlByHost;
2223 break;
2224
2225 case CPUPropertyType_HWVirt:
2226 *aValue = mHWData->mNestedHWVirt;
2227 break;
2228
2229 case CPUPropertyType_L1DFlushOnEMTScheduling:
2230 *aValue = mHWData->mL1DFlushOnSched;
2231 break;
2232
2233 case CPUPropertyType_L1DFlushOnVMEntry:
2234 *aValue = mHWData->mL1DFlushOnVMEntry;
2235 break;
2236
2237 case CPUPropertyType_MDSClearOnEMTScheduling:
2238 *aValue = mHWData->mMDSClearOnSched;
2239 break;
2240
2241 case CPUPropertyType_MDSClearOnVMEntry:
2242 *aValue = mHWData->mMDSClearOnVMEntry;
2243 break;
2244
2245 default:
2246 return E_INVALIDARG;
2247 }
2248 return S_OK;
2249}
2250
2251HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2252{
2253 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2254
2255 HRESULT rc = i_checkStateDependency(MutableStateDep);
2256 if (FAILED(rc)) return rc;
2257
2258 switch (aProperty)
2259 {
2260 case CPUPropertyType_PAE:
2261 i_setModified(IsModified_MachineData);
2262 mHWData.backup();
2263 mHWData->mPAEEnabled = !!aValue;
2264 break;
2265
2266 case CPUPropertyType_LongMode:
2267 i_setModified(IsModified_MachineData);
2268 mHWData.backup();
2269 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2270 break;
2271
2272 case CPUPropertyType_TripleFaultReset:
2273 i_setModified(IsModified_MachineData);
2274 mHWData.backup();
2275 mHWData->mTripleFaultReset = !!aValue;
2276 break;
2277
2278 case CPUPropertyType_APIC:
2279 if (mHWData->mX2APIC)
2280 aValue = TRUE;
2281 i_setModified(IsModified_MachineData);
2282 mHWData.backup();
2283 mHWData->mAPIC = !!aValue;
2284 break;
2285
2286 case CPUPropertyType_X2APIC:
2287 i_setModified(IsModified_MachineData);
2288 mHWData.backup();
2289 mHWData->mX2APIC = !!aValue;
2290 if (aValue)
2291 mHWData->mAPIC = !!aValue;
2292 break;
2293
2294 case CPUPropertyType_IBPBOnVMExit:
2295 i_setModified(IsModified_MachineData);
2296 mHWData.backup();
2297 mHWData->mIBPBOnVMExit = !!aValue;
2298 break;
2299
2300 case CPUPropertyType_IBPBOnVMEntry:
2301 i_setModified(IsModified_MachineData);
2302 mHWData.backup();
2303 mHWData->mIBPBOnVMEntry = !!aValue;
2304 break;
2305
2306 case CPUPropertyType_SpecCtrl:
2307 i_setModified(IsModified_MachineData);
2308 mHWData.backup();
2309 mHWData->mSpecCtrl = !!aValue;
2310 break;
2311
2312 case CPUPropertyType_SpecCtrlByHost:
2313 i_setModified(IsModified_MachineData);
2314 mHWData.backup();
2315 mHWData->mSpecCtrlByHost = !!aValue;
2316 break;
2317
2318 case CPUPropertyType_HWVirt:
2319 i_setModified(IsModified_MachineData);
2320 mHWData.backup();
2321 mHWData->mNestedHWVirt = !!aValue;
2322 break;
2323
2324 case CPUPropertyType_L1DFlushOnEMTScheduling:
2325 i_setModified(IsModified_MachineData);
2326 mHWData.backup();
2327 mHWData->mL1DFlushOnSched = !!aValue;
2328 break;
2329
2330 case CPUPropertyType_L1DFlushOnVMEntry:
2331 i_setModified(IsModified_MachineData);
2332 mHWData.backup();
2333 mHWData->mL1DFlushOnVMEntry = !!aValue;
2334 break;
2335
2336 case CPUPropertyType_MDSClearOnEMTScheduling:
2337 i_setModified(IsModified_MachineData);
2338 mHWData.backup();
2339 mHWData->mMDSClearOnSched = !!aValue;
2340 break;
2341
2342 case CPUPropertyType_MDSClearOnVMEntry:
2343 i_setModified(IsModified_MachineData);
2344 mHWData.backup();
2345 mHWData->mMDSClearOnVMEntry = !!aValue;
2346 break;
2347
2348 default:
2349 return E_INVALIDARG;
2350 }
2351 return S_OK;
2352}
2353
2354HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2355 ULONG *aValEcx, ULONG *aValEdx)
2356{
2357 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2358 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2359 {
2360 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2361 it != mHWData->mCpuIdLeafList.end();
2362 ++it)
2363 {
2364 if (aOrdinal == 0)
2365 {
2366 const settings::CpuIdLeaf &rLeaf= *it;
2367 *aIdx = rLeaf.idx;
2368 *aSubIdx = rLeaf.idxSub;
2369 *aValEax = rLeaf.uEax;
2370 *aValEbx = rLeaf.uEbx;
2371 *aValEcx = rLeaf.uEcx;
2372 *aValEdx = rLeaf.uEdx;
2373 return S_OK;
2374 }
2375 aOrdinal--;
2376 }
2377 }
2378 return E_INVALIDARG;
2379}
2380
2381HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2382{
2383 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2384
2385 /*
2386 * Search the list.
2387 */
2388 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2389 {
2390 const settings::CpuIdLeaf &rLeaf= *it;
2391 if ( rLeaf.idx == aIdx
2392 && ( aSubIdx == UINT32_MAX
2393 || rLeaf.idxSub == aSubIdx) )
2394 {
2395 *aValEax = rLeaf.uEax;
2396 *aValEbx = rLeaf.uEbx;
2397 *aValEcx = rLeaf.uEcx;
2398 *aValEdx = rLeaf.uEdx;
2399 return S_OK;
2400 }
2401 }
2402
2403 return E_INVALIDARG;
2404}
2405
2406
2407HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2408{
2409 /*
2410 * Validate input before taking locks and checking state.
2411 */
2412 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2413 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2414 if ( aIdx >= UINT32_C(0x20)
2415 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2416 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2417 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2418
2419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2420 HRESULT rc = i_checkStateDependency(MutableStateDep);
2421 if (FAILED(rc)) return rc;
2422
2423 /*
2424 * Impose a maximum number of leaves.
2425 */
2426 if (mHWData->mCpuIdLeafList.size() > 256)
2427 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2428
2429 /*
2430 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2431 */
2432 i_setModified(IsModified_MachineData);
2433 mHWData.backup();
2434
2435 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2436 {
2437 settings::CpuIdLeaf &rLeaf= *it;
2438 if ( rLeaf.idx == aIdx
2439 && ( aSubIdx == UINT32_MAX
2440 || rLeaf.idxSub == aSubIdx) )
2441 it = mHWData->mCpuIdLeafList.erase(it);
2442 else
2443 ++it;
2444 }
2445
2446 settings::CpuIdLeaf NewLeaf;
2447 NewLeaf.idx = aIdx;
2448 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2449 NewLeaf.uEax = aValEax;
2450 NewLeaf.uEbx = aValEbx;
2451 NewLeaf.uEcx = aValEcx;
2452 NewLeaf.uEdx = aValEdx;
2453 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2454 return S_OK;
2455}
2456
2457HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2458{
2459 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2460
2461 HRESULT rc = i_checkStateDependency(MutableStateDep);
2462 if (FAILED(rc)) return rc;
2463
2464 /*
2465 * Do the removal.
2466 */
2467 bool fModified = mHWData.isBackedUp();
2468 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2469 {
2470 settings::CpuIdLeaf &rLeaf= *it;
2471 if ( rLeaf.idx == aIdx
2472 && ( aSubIdx == UINT32_MAX
2473 || rLeaf.idxSub == aSubIdx) )
2474 {
2475 if (!fModified)
2476 {
2477 fModified = true;
2478 i_setModified(IsModified_MachineData);
2479 mHWData.backup();
2480 // Start from the beginning, since mHWData.backup() creates
2481 // a new list, causing iterator mixup. This makes sure that
2482 // the settings are not unnecessarily marked as modified,
2483 // at the price of extra list walking.
2484 it = mHWData->mCpuIdLeafList.begin();
2485 }
2486 else
2487 it = mHWData->mCpuIdLeafList.erase(it);
2488 }
2489 else
2490 ++it;
2491 }
2492
2493 return S_OK;
2494}
2495
2496HRESULT Machine::removeAllCPUIDLeaves()
2497{
2498 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2499
2500 HRESULT rc = i_checkStateDependency(MutableStateDep);
2501 if (FAILED(rc)) return rc;
2502
2503 if (mHWData->mCpuIdLeafList.size() > 0)
2504 {
2505 i_setModified(IsModified_MachineData);
2506 mHWData.backup();
2507
2508 mHWData->mCpuIdLeafList.clear();
2509 }
2510
2511 return S_OK;
2512}
2513HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2514{
2515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2516
2517 switch(aProperty)
2518 {
2519 case HWVirtExPropertyType_Enabled:
2520 *aValue = mHWData->mHWVirtExEnabled;
2521 break;
2522
2523 case HWVirtExPropertyType_VPID:
2524 *aValue = mHWData->mHWVirtExVPIDEnabled;
2525 break;
2526
2527 case HWVirtExPropertyType_NestedPaging:
2528 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2529 break;
2530
2531 case HWVirtExPropertyType_UnrestrictedExecution:
2532 *aValue = mHWData->mHWVirtExUXEnabled;
2533 break;
2534
2535 case HWVirtExPropertyType_LargePages:
2536 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2537 break;
2538
2539 case HWVirtExPropertyType_Force:
2540 *aValue = mHWData->mHWVirtExForceEnabled;
2541 break;
2542
2543 case HWVirtExPropertyType_UseNativeApi:
2544 *aValue = mHWData->mHWVirtExUseNativeApi;
2545 break;
2546
2547 case HWVirtExPropertyType_VirtVmsaveVmload:
2548 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2549 break;
2550
2551 default:
2552 return E_INVALIDARG;
2553 }
2554 return S_OK;
2555}
2556
2557HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2558{
2559 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2560
2561 HRESULT rc = i_checkStateDependency(MutableStateDep);
2562 if (FAILED(rc)) return rc;
2563
2564 switch (aProperty)
2565 {
2566 case HWVirtExPropertyType_Enabled:
2567 i_setModified(IsModified_MachineData);
2568 mHWData.backup();
2569 mHWData->mHWVirtExEnabled = !!aValue;
2570 break;
2571
2572 case HWVirtExPropertyType_VPID:
2573 i_setModified(IsModified_MachineData);
2574 mHWData.backup();
2575 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2576 break;
2577
2578 case HWVirtExPropertyType_NestedPaging:
2579 i_setModified(IsModified_MachineData);
2580 mHWData.backup();
2581 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2582 break;
2583
2584 case HWVirtExPropertyType_UnrestrictedExecution:
2585 i_setModified(IsModified_MachineData);
2586 mHWData.backup();
2587 mHWData->mHWVirtExUXEnabled = !!aValue;
2588 break;
2589
2590 case HWVirtExPropertyType_LargePages:
2591 i_setModified(IsModified_MachineData);
2592 mHWData.backup();
2593 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2594 break;
2595
2596 case HWVirtExPropertyType_Force:
2597 i_setModified(IsModified_MachineData);
2598 mHWData.backup();
2599 mHWData->mHWVirtExForceEnabled = !!aValue;
2600 break;
2601
2602 case HWVirtExPropertyType_UseNativeApi:
2603 i_setModified(IsModified_MachineData);
2604 mHWData.backup();
2605 mHWData->mHWVirtExUseNativeApi = !!aValue;
2606 break;
2607
2608 case HWVirtExPropertyType_VirtVmsaveVmload:
2609 i_setModified(IsModified_MachineData);
2610 mHWData.backup();
2611 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2612 break;
2613
2614 default:
2615 return E_INVALIDARG;
2616 }
2617
2618 return S_OK;
2619}
2620
2621HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2622{
2623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2624
2625 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2626
2627 return S_OK;
2628}
2629
2630HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2631{
2632 /** @todo (r=dmik):
2633 * 1. Allow to change the name of the snapshot folder containing snapshots
2634 * 2. Rename the folder on disk instead of just changing the property
2635 * value (to be smart and not to leave garbage). Note that it cannot be
2636 * done here because the change may be rolled back. Thus, the right
2637 * place is #saveSettings().
2638 */
2639
2640 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2641
2642 HRESULT rc = i_checkStateDependency(MutableStateDep);
2643 if (FAILED(rc)) return rc;
2644
2645 if (!mData->mCurrentSnapshot.isNull())
2646 return setError(E_FAIL,
2647 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2648
2649 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2650
2651 if (strSnapshotFolder.isEmpty())
2652 strSnapshotFolder = "Snapshots";
2653 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2654 if (RT_FAILURE(vrc))
2655 return setErrorBoth(E_FAIL, vrc,
2656 tr("Invalid snapshot folder '%s' (%Rrc)"),
2657 strSnapshotFolder.c_str(), vrc);
2658
2659 i_setModified(IsModified_MachineData);
2660 mUserData.backup();
2661
2662 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2663
2664 return S_OK;
2665}
2666
2667HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2668{
2669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2670
2671 aMediumAttachments.resize(mMediumAttachments->size());
2672 size_t i = 0;
2673 for (MediumAttachmentList::const_iterator
2674 it = mMediumAttachments->begin();
2675 it != mMediumAttachments->end();
2676 ++it, ++i)
2677 aMediumAttachments[i] = *it;
2678
2679 return S_OK;
2680}
2681
2682HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2683{
2684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2685
2686 Assert(!!mVRDEServer);
2687
2688 aVRDEServer = mVRDEServer;
2689
2690 return S_OK;
2691}
2692
2693HRESULT Machine::getAudioSettings(ComPtr<IAudioSettings> &aAudioSettings)
2694{
2695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2696
2697 aAudioSettings = mAudioSettings;
2698
2699 return S_OK;
2700}
2701
2702HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2703{
2704#ifdef VBOX_WITH_VUSB
2705 clearError();
2706 MultiResult rc(S_OK);
2707
2708# ifdef VBOX_WITH_USB
2709 rc = mParent->i_host()->i_checkUSBProxyService();
2710 if (FAILED(rc)) return rc;
2711# endif
2712
2713 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2714
2715 aUSBControllers.resize(mUSBControllers->size());
2716 size_t i = 0;
2717 for (USBControllerList::const_iterator
2718 it = mUSBControllers->begin();
2719 it != mUSBControllers->end();
2720 ++it, ++i)
2721 aUSBControllers[i] = *it;
2722
2723 return S_OK;
2724#else
2725 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2726 * extended error info to indicate that USB is simply not available
2727 * (w/o treating it as a failure), for example, as in OSE */
2728 NOREF(aUSBControllers);
2729 ReturnComNotImplemented();
2730#endif /* VBOX_WITH_VUSB */
2731}
2732
2733HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2734{
2735#ifdef VBOX_WITH_VUSB
2736 clearError();
2737 MultiResult rc(S_OK);
2738
2739# ifdef VBOX_WITH_USB
2740 rc = mParent->i_host()->i_checkUSBProxyService();
2741 if (FAILED(rc)) return rc;
2742# endif
2743
2744 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2745
2746 aUSBDeviceFilters = mUSBDeviceFilters;
2747 return rc;
2748#else
2749 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2750 * extended error info to indicate that USB is simply not available
2751 * (w/o treating it as a failure), for example, as in OSE */
2752 NOREF(aUSBDeviceFilters);
2753 ReturnComNotImplemented();
2754#endif /* VBOX_WITH_VUSB */
2755}
2756
2757HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2758{
2759 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2760
2761 aSettingsFilePath = mData->m_strConfigFileFull;
2762
2763 return S_OK;
2764}
2765
2766HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2767{
2768 RT_NOREF(aSettingsFilePath);
2769 ReturnComNotImplemented();
2770}
2771
2772HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2773{
2774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2775
2776 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2777 if (FAILED(rc)) return rc;
2778
2779 if (!mData->pMachineConfigFile->fileExists())
2780 // this is a new machine, and no config file exists yet:
2781 *aSettingsModified = TRUE;
2782 else
2783 *aSettingsModified = (mData->flModifications != 0);
2784
2785 return S_OK;
2786}
2787
2788HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2789{
2790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2791
2792 *aSessionState = mData->mSession.mState;
2793
2794 return S_OK;
2795}
2796
2797HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2798{
2799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2800
2801 aSessionName = mData->mSession.mName;
2802
2803 return S_OK;
2804}
2805
2806HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2807{
2808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2809
2810 *aSessionPID = mData->mSession.mPID;
2811
2812 return S_OK;
2813}
2814
2815HRESULT Machine::getState(MachineState_T *aState)
2816{
2817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2818
2819 *aState = mData->mMachineState;
2820 Assert(mData->mMachineState != MachineState_Null);
2821
2822 return S_OK;
2823}
2824
2825HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2826{
2827 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2828
2829 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2830
2831 return S_OK;
2832}
2833
2834HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2835{
2836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2837
2838 aStateFilePath = mSSData->strStateFilePath;
2839
2840 return S_OK;
2841}
2842
2843HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2844{
2845 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2846
2847 i_getLogFolder(aLogFolder);
2848
2849 return S_OK;
2850}
2851
2852HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2853{
2854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2855
2856 aCurrentSnapshot = mData->mCurrentSnapshot;
2857
2858 return S_OK;
2859}
2860
2861HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2862{
2863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2864
2865 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2866 ? 0
2867 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2868
2869 return S_OK;
2870}
2871
2872HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2873{
2874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2875
2876 /* Note: for machines with no snapshots, we always return FALSE
2877 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2878 * reasons :) */
2879
2880 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2881 ? FALSE
2882 : mData->mCurrentStateModified;
2883
2884 return S_OK;
2885}
2886
2887HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2888{
2889 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2890
2891 aSharedFolders.resize(mHWData->mSharedFolders.size());
2892 size_t i = 0;
2893 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2894 it = mHWData->mSharedFolders.begin();
2895 it != mHWData->mSharedFolders.end();
2896 ++it, ++i)
2897 aSharedFolders[i] = *it;
2898
2899 return S_OK;
2900}
2901
2902HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2903{
2904 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2905
2906 *aClipboardMode = mHWData->mClipboardMode;
2907
2908 return S_OK;
2909}
2910
2911HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2912{
2913 HRESULT rc = S_OK;
2914
2915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2916
2917 rc = i_checkStateDependency(MutableOrRunningStateDep);
2918 if (FAILED(rc)) return rc;
2919
2920 alock.release();
2921 rc = i_onClipboardModeChange(aClipboardMode);
2922 alock.acquire();
2923 if (FAILED(rc)) return rc;
2924
2925 i_setModified(IsModified_MachineData);
2926 mHWData.backup();
2927 mHWData->mClipboardMode = aClipboardMode;
2928
2929 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2930 if (Global::IsOnline(mData->mMachineState))
2931 i_saveSettings(NULL, alock);
2932
2933 return S_OK;
2934}
2935
2936HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2937{
2938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2939
2940 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2941
2942 return S_OK;
2943}
2944
2945HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2946{
2947 HRESULT rc = S_OK;
2948
2949 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2950
2951 rc = i_checkStateDependency(MutableOrRunningStateDep);
2952 if (FAILED(rc)) return rc;
2953
2954 alock.release();
2955 rc = i_onClipboardFileTransferModeChange(aEnabled);
2956 alock.acquire();
2957 if (FAILED(rc)) return rc;
2958
2959 i_setModified(IsModified_MachineData);
2960 mHWData.backup();
2961 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2962
2963 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2964 if (Global::IsOnline(mData->mMachineState))
2965 i_saveSettings(NULL, alock);
2966
2967 return S_OK;
2968}
2969
2970HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2971{
2972 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2973
2974 *aDnDMode = mHWData->mDnDMode;
2975
2976 return S_OK;
2977}
2978
2979HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2980{
2981 HRESULT rc = S_OK;
2982
2983 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2984
2985 rc = i_checkStateDependency(MutableOrRunningStateDep);
2986 if (FAILED(rc)) return rc;
2987
2988 alock.release();
2989 rc = i_onDnDModeChange(aDnDMode);
2990
2991 alock.acquire();
2992 if (FAILED(rc)) return rc;
2993
2994 i_setModified(IsModified_MachineData);
2995 mHWData.backup();
2996 mHWData->mDnDMode = aDnDMode;
2997
2998 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2999 if (Global::IsOnline(mData->mMachineState))
3000 i_saveSettings(NULL, alock);
3001
3002 return S_OK;
3003}
3004
3005HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
3006{
3007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3008
3009 aStorageControllers.resize(mStorageControllers->size());
3010 size_t i = 0;
3011 for (StorageControllerList::const_iterator
3012 it = mStorageControllers->begin();
3013 it != mStorageControllers->end();
3014 ++it, ++i)
3015 aStorageControllers[i] = *it;
3016
3017 return S_OK;
3018}
3019
3020HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
3021{
3022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3023
3024 *aEnabled = mUserData->s.fTeleporterEnabled;
3025
3026 return S_OK;
3027}
3028
3029HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
3030{
3031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3032
3033 /* Only allow it to be set to true when PoweredOff or Aborted.
3034 (Clearing it is always permitted.) */
3035 if ( aTeleporterEnabled
3036 && mData->mRegistered
3037 && ( !i_isSessionMachine()
3038 || ( mData->mMachineState != MachineState_PoweredOff
3039 && mData->mMachineState != MachineState_Teleported
3040 && mData->mMachineState != MachineState_Aborted
3041 )
3042 )
3043 )
3044 return setError(VBOX_E_INVALID_VM_STATE,
3045 tr("The machine is not powered off (state is %s)"),
3046 Global::stringifyMachineState(mData->mMachineState));
3047
3048 i_setModified(IsModified_MachineData);
3049 mUserData.backup();
3050 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
3051
3052 return S_OK;
3053}
3054
3055HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
3056{
3057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3058
3059 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
3060
3061 return S_OK;
3062}
3063
3064HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
3065{
3066 if (aTeleporterPort >= _64K)
3067 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
3068
3069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3070
3071 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3072 if (FAILED(rc)) return rc;
3073
3074 i_setModified(IsModified_MachineData);
3075 mUserData.backup();
3076 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3077
3078 return S_OK;
3079}
3080
3081HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3082{
3083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3084
3085 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3086
3087 return S_OK;
3088}
3089
3090HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3091{
3092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3093
3094 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3095 if (FAILED(rc)) return rc;
3096
3097 i_setModified(IsModified_MachineData);
3098 mUserData.backup();
3099 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3100
3101 return S_OK;
3102}
3103
3104HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3105{
3106 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3107 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3108
3109 return S_OK;
3110}
3111
3112HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3113{
3114 /*
3115 * Hash the password first.
3116 */
3117 com::Utf8Str aT = aTeleporterPassword;
3118
3119 if (!aT.isEmpty())
3120 {
3121 if (VBoxIsPasswordHashed(&aT))
3122 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3123 VBoxHashPassword(&aT);
3124 }
3125
3126 /*
3127 * Do the update.
3128 */
3129 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3130 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3131 if (SUCCEEDED(hrc))
3132 {
3133 i_setModified(IsModified_MachineData);
3134 mUserData.backup();
3135 mUserData->s.strTeleporterPassword = aT;
3136 }
3137
3138 return hrc;
3139}
3140
3141HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3142{
3143 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3144
3145 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3146
3147 return S_OK;
3148}
3149
3150HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3151{
3152 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3153
3154 /* Only allow it to be set to true when PoweredOff or Aborted.
3155 (Clearing it is always permitted.) */
3156 if ( aRTCUseUTC
3157 && mData->mRegistered
3158 && ( !i_isSessionMachine()
3159 || ( mData->mMachineState != MachineState_PoweredOff
3160 && mData->mMachineState != MachineState_Teleported
3161 && mData->mMachineState != MachineState_Aborted
3162 )
3163 )
3164 )
3165 return setError(VBOX_E_INVALID_VM_STATE,
3166 tr("The machine is not powered off (state is %s)"),
3167 Global::stringifyMachineState(mData->mMachineState));
3168
3169 i_setModified(IsModified_MachineData);
3170 mUserData.backup();
3171 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3172
3173 return S_OK;
3174}
3175
3176HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3177{
3178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3179
3180 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3181
3182 return S_OK;
3183}
3184
3185HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3186{
3187 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3188
3189 HRESULT rc = i_checkStateDependency(MutableStateDep);
3190 if (FAILED(rc)) return rc;
3191
3192 i_setModified(IsModified_MachineData);
3193 mHWData.backup();
3194 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3195
3196 return S_OK;
3197}
3198
3199HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3200{
3201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3202
3203 *aIOCacheSize = mHWData->mIOCacheSize;
3204
3205 return S_OK;
3206}
3207
3208HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3209{
3210 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3211
3212 HRESULT rc = i_checkStateDependency(MutableStateDep);
3213 if (FAILED(rc)) return rc;
3214
3215 i_setModified(IsModified_MachineData);
3216 mHWData.backup();
3217 mHWData->mIOCacheSize = aIOCacheSize;
3218
3219 return S_OK;
3220}
3221
3222HRESULT Machine::getStateKeyId(com::Utf8Str &aKeyId)
3223{
3224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3225
3226#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3227 aKeyId = mSSData->strStateKeyId;
3228#else
3229 aKeyId = com::Utf8Str::Empty;
3230#endif
3231
3232 return S_OK;
3233}
3234
3235HRESULT Machine::getStateKeyStore(com::Utf8Str &aKeyStore)
3236{
3237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3238
3239#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3240 aKeyStore = mSSData->strStateKeyStore;
3241#else
3242 aKeyStore = com::Utf8Str::Empty;
3243#endif
3244
3245 return S_OK;
3246}
3247
3248HRESULT Machine::getLogKeyId(com::Utf8Str &aKeyId)
3249{
3250 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3251
3252#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3253 aKeyId = mData->mstrLogKeyId;
3254#else
3255 aKeyId = com::Utf8Str::Empty;
3256#endif
3257
3258 return S_OK;
3259}
3260
3261HRESULT Machine::getLogKeyStore(com::Utf8Str &aKeyStore)
3262{
3263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3264
3265#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3266 aKeyStore = mData->mstrLogKeyStore;
3267#else
3268 aKeyStore = com::Utf8Str::Empty;
3269#endif
3270
3271 return S_OK;
3272}
3273
3274HRESULT Machine::getGuestDebugControl(ComPtr<IGuestDebugControl> &aGuestDebugControl)
3275{
3276 mGuestDebugControl.queryInterfaceTo(aGuestDebugControl.asOutParam());
3277
3278 return S_OK;
3279}
3280
3281
3282/**
3283 * @note Locks objects!
3284 */
3285HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3286 LockType_T aLockType)
3287{
3288 /* check the session state */
3289 SessionState_T state;
3290 HRESULT rc = aSession->COMGETTER(State)(&state);
3291 if (FAILED(rc)) return rc;
3292
3293 if (state != SessionState_Unlocked)
3294 return setError(VBOX_E_INVALID_OBJECT_STATE,
3295 tr("The given session is busy"));
3296
3297 // get the client's IInternalSessionControl interface
3298 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3299 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
3300 E_INVALIDARG);
3301
3302 // session name (only used in some code paths)
3303 Utf8Str strSessionName;
3304
3305 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3306
3307 if (!mData->mRegistered)
3308 return setError(E_UNEXPECTED,
3309 tr("The machine '%s' is not registered"),
3310 mUserData->s.strName.c_str());
3311
3312 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3313
3314 SessionState_T oldState = mData->mSession.mState;
3315 /* Hack: in case the session is closing and there is a progress object
3316 * which allows waiting for the session to be closed, take the opportunity
3317 * and do a limited wait (max. 1 second). This helps a lot when the system
3318 * is busy and thus session closing can take a little while. */
3319 if ( mData->mSession.mState == SessionState_Unlocking
3320 && mData->mSession.mProgress)
3321 {
3322 alock.release();
3323 mData->mSession.mProgress->WaitForCompletion(1000);
3324 alock.acquire();
3325 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3326 }
3327
3328 // try again now
3329 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3330 // (i.e. session machine exists)
3331 && (aLockType == LockType_Shared) // caller wants a shared link to the
3332 // existing session that holds the write lock:
3333 )
3334 {
3335 // OK, share the session... we are now dealing with three processes:
3336 // 1) VBoxSVC (where this code runs);
3337 // 2) process C: the caller's client process (who wants a shared session);
3338 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3339
3340 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3341 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3342 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3343 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3344 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3345
3346 /*
3347 * Release the lock before calling the client process. It's safe here
3348 * since the only thing to do after we get the lock again is to add
3349 * the remote control to the list (which doesn't directly influence
3350 * anything).
3351 */
3352 alock.release();
3353
3354 // get the console of the session holding the write lock (this is a remote call)
3355 ComPtr<IConsole> pConsoleW;
3356 if (mData->mSession.mLockType == LockType_VM)
3357 {
3358 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3359 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3360 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3361 if (FAILED(rc))
3362 // the failure may occur w/o any error info (from RPC), so provide one
3363 return setError(VBOX_E_VM_ERROR,
3364 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3365 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3366 }
3367
3368 // share the session machine and W's console with the caller's session
3369 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3370 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3371 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3372
3373 if (FAILED(rc))
3374 // the failure may occur w/o any error info (from RPC), so provide one
3375 return setError(VBOX_E_VM_ERROR,
3376 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3377 alock.acquire();
3378
3379 // need to revalidate the state after acquiring the lock again
3380 if (mData->mSession.mState != SessionState_Locked)
3381 {
3382 pSessionControl->Uninitialize();
3383 return setError(VBOX_E_INVALID_SESSION_STATE,
3384 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3385 mUserData->s.strName.c_str());
3386 }
3387
3388 // add the caller's session to the list
3389 mData->mSession.mRemoteControls.push_back(pSessionControl);
3390 }
3391 else if ( mData->mSession.mState == SessionState_Locked
3392 || mData->mSession.mState == SessionState_Unlocking
3393 )
3394 {
3395 // sharing not permitted, or machine still unlocking:
3396 return setError(VBOX_E_INVALID_OBJECT_STATE,
3397 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3398 mUserData->s.strName.c_str());
3399 }
3400 else
3401 {
3402 // machine is not locked: then write-lock the machine (create the session machine)
3403
3404 // must not be busy
3405 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3406
3407 // get the caller's session PID
3408 RTPROCESS pid = NIL_RTPROCESS;
3409 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3410 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3411 Assert(pid != NIL_RTPROCESS);
3412
3413 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3414
3415 if (fLaunchingVMProcess)
3416 {
3417 if (mData->mSession.mPID == NIL_RTPROCESS)
3418 {
3419 // two or more clients racing for a lock, the one which set the
3420 // session state to Spawning will win, the others will get an
3421 // error as we can't decide here if waiting a little would help
3422 // (only for shared locks this would avoid an error)
3423 return setError(VBOX_E_INVALID_OBJECT_STATE,
3424 tr("The machine '%s' already has a lock request pending"),
3425 mUserData->s.strName.c_str());
3426 }
3427
3428 // this machine is awaiting for a spawning session to be opened:
3429 // then the calling process must be the one that got started by
3430 // LaunchVMProcess()
3431
3432 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3433 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3434
3435#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3436 /* Hardened windows builds spawns three processes when a VM is
3437 launched, the 3rd one is the one that will end up here. */
3438 RTPROCESS pidParent;
3439 int vrc = RTProcQueryParent(pid, &pidParent);
3440 if (RT_SUCCESS(vrc))
3441 vrc = RTProcQueryParent(pidParent, &pidParent);
3442 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3443 || vrc == VERR_ACCESS_DENIED)
3444 {
3445 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3446 mData->mSession.mPID = pid;
3447 }
3448#endif
3449
3450 if (mData->mSession.mPID != pid)
3451 return setError(E_ACCESSDENIED,
3452 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3453 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3454 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3455 }
3456
3457 // create the mutable SessionMachine from the current machine
3458 ComObjPtr<SessionMachine> sessionMachine;
3459 sessionMachine.createObject();
3460 rc = sessionMachine->init(this);
3461 AssertComRC(rc);
3462
3463 /* NOTE: doing return from this function after this point but
3464 * before the end is forbidden since it may call SessionMachine::uninit()
3465 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3466 * lock while still holding the Machine lock in alock so that a deadlock
3467 * is possible due to the wrong lock order. */
3468
3469 if (SUCCEEDED(rc))
3470 {
3471 /*
3472 * Set the session state to Spawning to protect against subsequent
3473 * attempts to open a session and to unregister the machine after
3474 * we release the lock.
3475 */
3476 SessionState_T origState = mData->mSession.mState;
3477 mData->mSession.mState = SessionState_Spawning;
3478
3479#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3480 /* Get the client token ID to be passed to the client process */
3481 Utf8Str strTokenId;
3482 sessionMachine->i_getTokenId(strTokenId);
3483 Assert(!strTokenId.isEmpty());
3484#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3485 /* Get the client token to be passed to the client process */
3486 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3487 /* The token is now "owned" by pToken, fix refcount */
3488 if (!pToken.isNull())
3489 pToken->Release();
3490#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3491
3492 /*
3493 * Release the lock before calling the client process -- it will call
3494 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3495 * because the state is Spawning, so that LaunchVMProcess() and
3496 * LockMachine() calls will fail. This method, called before we
3497 * acquire the lock again, will fail because of the wrong PID.
3498 *
3499 * Note that mData->mSession.mRemoteControls accessed outside
3500 * the lock may not be modified when state is Spawning, so it's safe.
3501 */
3502 alock.release();
3503
3504 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3505#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3506 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3507#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3508 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3509 /* Now the token is owned by the client process. */
3510 pToken.setNull();
3511#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3512 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3513
3514 /* The failure may occur w/o any error info (from RPC), so provide one */
3515 if (FAILED(rc))
3516 setError(VBOX_E_VM_ERROR,
3517 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3518
3519 // get session name, either to remember or to compare against
3520 // the already known session name.
3521 {
3522 Bstr bstrSessionName;
3523 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3524 if (SUCCEEDED(rc2))
3525 strSessionName = bstrSessionName;
3526 }
3527
3528 if ( SUCCEEDED(rc)
3529 && fLaunchingVMProcess
3530 )
3531 {
3532 /* complete the remote session initialization */
3533
3534 /* get the console from the direct session */
3535 ComPtr<IConsole> console;
3536 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3537 ComAssertComRC(rc);
3538
3539 if (SUCCEEDED(rc) && !console)
3540 {
3541 ComAssert(!!console);
3542 rc = E_FAIL;
3543 }
3544
3545 /* assign machine & console to the remote session */
3546 if (SUCCEEDED(rc))
3547 {
3548 /*
3549 * after LaunchVMProcess(), the first and the only
3550 * entry in remoteControls is that remote session
3551 */
3552 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3553 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3554 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3555
3556 /* The failure may occur w/o any error info (from RPC), so provide one */
3557 if (FAILED(rc))
3558 setError(VBOX_E_VM_ERROR,
3559 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3560 }
3561
3562 if (FAILED(rc))
3563 pSessionControl->Uninitialize();
3564 }
3565
3566 /* acquire the lock again */
3567 alock.acquire();
3568
3569 /* Restore the session state */
3570 mData->mSession.mState = origState;
3571 }
3572
3573 // finalize spawning anyway (this is why we don't return on errors above)
3574 if (fLaunchingVMProcess)
3575 {
3576 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3577 /* Note that the progress object is finalized later */
3578 /** @todo Consider checking mData->mSession.mProgress for cancellation
3579 * around here. */
3580
3581 /* We don't reset mSession.mPID here because it is necessary for
3582 * SessionMachine::uninit() to reap the child process later. */
3583
3584 if (FAILED(rc))
3585 {
3586 /* Close the remote session, remove the remote control from the list
3587 * and reset session state to Closed (@note keep the code in sync
3588 * with the relevant part in checkForSpawnFailure()). */
3589
3590 Assert(mData->mSession.mRemoteControls.size() == 1);
3591 if (mData->mSession.mRemoteControls.size() == 1)
3592 {
3593 ErrorInfoKeeper eik;
3594 mData->mSession.mRemoteControls.front()->Uninitialize();
3595 }
3596
3597 mData->mSession.mRemoteControls.clear();
3598 mData->mSession.mState = SessionState_Unlocked;
3599 }
3600 }
3601 else
3602 {
3603 /* memorize PID of the directly opened session */
3604 if (SUCCEEDED(rc))
3605 mData->mSession.mPID = pid;
3606 }
3607
3608 if (SUCCEEDED(rc))
3609 {
3610 mData->mSession.mLockType = aLockType;
3611 /* memorize the direct session control and cache IUnknown for it */
3612 mData->mSession.mDirectControl = pSessionControl;
3613 mData->mSession.mState = SessionState_Locked;
3614 if (!fLaunchingVMProcess)
3615 mData->mSession.mName = strSessionName;
3616 /* associate the SessionMachine with this Machine */
3617 mData->mSession.mMachine = sessionMachine;
3618
3619 /* request an IUnknown pointer early from the remote party for later
3620 * identity checks (it will be internally cached within mDirectControl
3621 * at least on XPCOM) */
3622 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3623 NOREF(unk);
3624
3625#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3626 if (aLockType == LockType_VM)
3627 {
3628 /* get the console from the direct session */
3629 ComPtr<IConsole> console;
3630 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3631 ComAssertComRC(rc);
3632 /* send passswords to console */
3633 for (SecretKeyStore::SecretKeyMap::iterator it = mData->mpKeyStore->begin();
3634 it != mData->mpKeyStore->end();
3635 ++it)
3636 {
3637 SecretKey *pKey = it->second;
3638 pKey->retain();
3639 console->AddEncryptionPassword(Bstr(it->first).raw(),
3640 Bstr((const char*)pKey->getKeyBuffer()).raw(),
3641 TRUE);
3642 pKey->release();
3643 }
3644
3645 }
3646#endif
3647 }
3648
3649 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3650 * would break the lock order */
3651 alock.release();
3652
3653 /* uninitialize the created session machine on failure */
3654 if (FAILED(rc))
3655 sessionMachine->uninit();
3656 }
3657
3658 if (SUCCEEDED(rc))
3659 {
3660 /*
3661 * tell the client watcher thread to update the set of
3662 * machines that have open sessions
3663 */
3664 mParent->i_updateClientWatcher();
3665
3666 if (oldState != SessionState_Locked)
3667 /* fire an event */
3668 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3669 }
3670
3671 return rc;
3672}
3673
3674/**
3675 * @note Locks objects!
3676 */
3677HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3678 const com::Utf8Str &aName,
3679 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3680 ComPtr<IProgress> &aProgress)
3681{
3682 Utf8Str strFrontend(aName);
3683 /* "emergencystop" doesn't need the session, so skip the checks/interface
3684 * retrieval. This code doesn't quite fit in here, but introducing a
3685 * special API method would be even more effort, and would require explicit
3686 * support by every API client. It's better to hide the feature a bit. */
3687 if (strFrontend != "emergencystop")
3688 CheckComArgNotNull(aSession);
3689
3690 HRESULT rc = S_OK;
3691 if (strFrontend.isEmpty())
3692 {
3693 Bstr bstrFrontend;
3694 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3695 if (FAILED(rc))
3696 return rc;
3697 strFrontend = bstrFrontend;
3698 if (strFrontend.isEmpty())
3699 {
3700 ComPtr<ISystemProperties> systemProperties;
3701 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3702 if (FAILED(rc))
3703 return rc;
3704 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3705 if (FAILED(rc))
3706 return rc;
3707 strFrontend = bstrFrontend;
3708 }
3709 /* paranoia - emergencystop is not a valid default */
3710 if (strFrontend == "emergencystop")
3711 strFrontend = Utf8Str::Empty;
3712 }
3713 /* default frontend: Qt GUI */
3714 if (strFrontend.isEmpty())
3715 strFrontend = "GUI/Qt";
3716
3717 if (strFrontend != "emergencystop")
3718 {
3719 /* check the session state */
3720 SessionState_T state;
3721 rc = aSession->COMGETTER(State)(&state);
3722 if (FAILED(rc))
3723 return rc;
3724
3725 if (state != SessionState_Unlocked)
3726 return setError(VBOX_E_INVALID_OBJECT_STATE,
3727 tr("The given session is busy"));
3728
3729 /* get the IInternalSessionControl interface */
3730 ComPtr<IInternalSessionControl> control(aSession);
3731 ComAssertMsgRet(!control.isNull(),
3732 ("No IInternalSessionControl interface"),
3733 E_INVALIDARG);
3734
3735 /* get the teleporter enable state for the progress object init. */
3736 BOOL fTeleporterEnabled;
3737 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3738 if (FAILED(rc))
3739 return rc;
3740
3741 /* create a progress object */
3742 ComObjPtr<ProgressProxy> progress;
3743 progress.createObject();
3744 rc = progress->init(mParent,
3745 static_cast<IMachine*>(this),
3746 Bstr(tr("Starting VM")).raw(),
3747 TRUE /* aCancelable */,
3748 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3749 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3750 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3751 2 /* uFirstOperationWeight */,
3752 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3753
3754 if (SUCCEEDED(rc))
3755 {
3756 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3757 if (SUCCEEDED(rc))
3758 {
3759 aProgress = progress;
3760
3761 /* signal the client watcher thread */
3762 mParent->i_updateClientWatcher();
3763
3764 /* fire an event */
3765 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3766 }
3767 }
3768 }
3769 else
3770 {
3771 /* no progress object - either instant success or failure */
3772 aProgress = NULL;
3773
3774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3775
3776 if (mData->mSession.mState != SessionState_Locked)
3777 return setError(VBOX_E_INVALID_OBJECT_STATE,
3778 tr("The machine '%s' is not locked by a session"),
3779 mUserData->s.strName.c_str());
3780
3781 /* must have a VM process associated - do not kill normal API clients
3782 * with an open session */
3783 if (!Global::IsOnline(mData->mMachineState))
3784 return setError(VBOX_E_INVALID_OBJECT_STATE,
3785 tr("The machine '%s' does not have a VM process"),
3786 mUserData->s.strName.c_str());
3787
3788 /* forcibly terminate the VM process */
3789 if (mData->mSession.mPID != NIL_RTPROCESS)
3790 RTProcTerminate(mData->mSession.mPID);
3791
3792 /* signal the client watcher thread, as most likely the client has
3793 * been terminated */
3794 mParent->i_updateClientWatcher();
3795 }
3796
3797 return rc;
3798}
3799
3800HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3801{
3802 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3803 return setError(E_INVALIDARG,
3804 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3805 aPosition, SchemaDefs::MaxBootPosition);
3806
3807 if (aDevice == DeviceType_USB)
3808 return setError(E_NOTIMPL,
3809 tr("Booting from USB device is currently not supported"));
3810
3811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3812
3813 HRESULT rc = i_checkStateDependency(MutableStateDep);
3814 if (FAILED(rc)) return rc;
3815
3816 i_setModified(IsModified_MachineData);
3817 mHWData.backup();
3818 mHWData->mBootOrder[aPosition - 1] = aDevice;
3819
3820 return S_OK;
3821}
3822
3823HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3824{
3825 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3826 return setError(E_INVALIDARG,
3827 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3828 aPosition, SchemaDefs::MaxBootPosition);
3829
3830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3831
3832 *aDevice = mHWData->mBootOrder[aPosition - 1];
3833
3834 return S_OK;
3835}
3836
3837HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3838 LONG aControllerPort,
3839 LONG aDevice,
3840 DeviceType_T aType,
3841 const ComPtr<IMedium> &aMedium)
3842{
3843 IMedium *aM = aMedium;
3844 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3845 aName.c_str(), aControllerPort, aDevice, aType, aM));
3846
3847 // request the host lock first, since might be calling Host methods for getting host drives;
3848 // next, protect the media tree all the while we're in here, as well as our member variables
3849 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3850 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3851
3852 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3853 if (FAILED(rc)) return rc;
3854
3855 /// @todo NEWMEDIA implicit machine registration
3856 if (!mData->mRegistered)
3857 return setError(VBOX_E_INVALID_OBJECT_STATE,
3858 tr("Cannot attach storage devices to an unregistered machine"));
3859
3860 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3861
3862 /* Check for an existing controller. */
3863 ComObjPtr<StorageController> ctl;
3864 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3865 if (FAILED(rc)) return rc;
3866
3867 StorageControllerType_T ctrlType;
3868 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3869 if (FAILED(rc))
3870 return setError(E_FAIL,
3871 tr("Could not get type of controller '%s'"),
3872 aName.c_str());
3873
3874 bool fSilent = false;
3875
3876 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3877 Utf8Str const strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3878 if ( mData->mMachineState == MachineState_Paused
3879 && strReconfig == "1")
3880 fSilent = true;
3881
3882 /* Check that the controller can do hot-plugging if we attach the device while the VM is running. */
3883 bool fHotplug = false;
3884 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3885 fHotplug = true;
3886
3887 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3888 return setError(VBOX_E_INVALID_VM_STATE,
3889 tr("Controller '%s' does not support hot-plugging"),
3890 aName.c_str());
3891
3892 // check that the port and device are not out of range
3893 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3894 if (FAILED(rc)) return rc;
3895
3896 /* check if the device slot is already busy */
3897 MediumAttachment *pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3898 aName,
3899 aControllerPort,
3900 aDevice);
3901 if (pAttachTemp)
3902 {
3903 Medium *pMedium = pAttachTemp->i_getMedium();
3904 if (pMedium)
3905 {
3906 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3907 return setError(VBOX_E_OBJECT_IN_USE,
3908 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3909 pMedium->i_getLocationFull().c_str(),
3910 aControllerPort,
3911 aDevice,
3912 aName.c_str());
3913 }
3914 else
3915 return setError(VBOX_E_OBJECT_IN_USE,
3916 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3917 aControllerPort, aDevice, aName.c_str());
3918 }
3919
3920 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3921 if (aMedium && medium.isNull())
3922 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3923
3924 AutoCaller mediumCaller(medium);
3925 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3926
3927 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3928
3929 pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium);
3930 if ( pAttachTemp
3931 && !medium.isNull()
3932 && ( medium->i_getType() != MediumType_Readonly
3933 || medium->i_getDeviceType() != DeviceType_DVD)
3934 )
3935 return setError(VBOX_E_OBJECT_IN_USE,
3936 tr("Medium '%s' is already attached to this virtual machine"),
3937 medium->i_getLocationFull().c_str());
3938
3939 if (!medium.isNull())
3940 {
3941 MediumType_T mtype = medium->i_getType();
3942 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3943 // For DVDs it's not written to the config file, so needs no global config
3944 // version bump. For floppies it's a new attribute "type", which is ignored
3945 // by older VirtualBox version, so needs no global config version bump either.
3946 // For hard disks this type is not accepted.
3947 if (mtype == MediumType_MultiAttach)
3948 {
3949 // This type is new with VirtualBox 4.0 and therefore requires settings
3950 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3951 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3952 // two reasons: The medium type is a property of the media registry tree, which
3953 // can reside in the global config file (for pre-4.0 media); we would therefore
3954 // possibly need to bump the global config version. We don't want to do that though
3955 // because that might make downgrading to pre-4.0 impossible.
3956 // As a result, we can only use these two new types if the medium is NOT in the
3957 // global registry:
3958 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3959 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3960 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3961 )
3962 return setError(VBOX_E_INVALID_OBJECT_STATE,
3963 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3964 "to machines that were created with VirtualBox 4.0 or later"),
3965 medium->i_getLocationFull().c_str());
3966 }
3967 }
3968
3969 bool fIndirect = false;
3970 if (!medium.isNull())
3971 fIndirect = medium->i_isReadOnly();
3972 bool associate = true;
3973
3974 do
3975 {
3976 if ( aType == DeviceType_HardDisk
3977 && mMediumAttachments.isBackedUp())
3978 {
3979 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3980
3981 /* check if the medium was attached to the VM before we started
3982 * changing attachments in which case the attachment just needs to
3983 * be restored */
3984 pAttachTemp = i_findAttachment(oldAtts, medium);
3985 if (pAttachTemp)
3986 {
3987 AssertReturn(!fIndirect, E_FAIL);
3988
3989 /* see if it's the same bus/channel/device */
3990 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3991 {
3992 /* the simplest case: restore the whole attachment
3993 * and return, nothing else to do */
3994 mMediumAttachments->push_back(pAttachTemp);
3995
3996 /* Reattach the medium to the VM. */
3997 if (fHotplug || fSilent)
3998 {
3999 mediumLock.release();
4000 treeLock.release();
4001 alock.release();
4002
4003 MediumLockList *pMediumLockList(new MediumLockList());
4004
4005 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4006 medium /* pToLockWrite */,
4007 false /* fMediumLockWriteAll */,
4008 NULL,
4009 *pMediumLockList);
4010 alock.acquire();
4011 if (FAILED(rc))
4012 delete pMediumLockList;
4013 else
4014 {
4015 Assert(mData->mSession.mLockedMedia.IsLocked());
4016 mData->mSession.mLockedMedia.Unlock();
4017 alock.release();
4018 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4019 mData->mSession.mLockedMedia.Lock();
4020 alock.acquire();
4021 }
4022 alock.release();
4023
4024 if (SUCCEEDED(rc))
4025 {
4026 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4027 /* Remove lock list in case of error. */
4028 if (FAILED(rc))
4029 {
4030 mData->mSession.mLockedMedia.Unlock();
4031 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4032 mData->mSession.mLockedMedia.Lock();
4033 }
4034 }
4035 }
4036
4037 return S_OK;
4038 }
4039
4040 /* bus/channel/device differ; we need a new attachment object,
4041 * but don't try to associate it again */
4042 associate = false;
4043 break;
4044 }
4045 }
4046
4047 /* go further only if the attachment is to be indirect */
4048 if (!fIndirect)
4049 break;
4050
4051 /* perform the so called smart attachment logic for indirect
4052 * attachments. Note that smart attachment is only applicable to base
4053 * hard disks. */
4054
4055 if (medium->i_getParent().isNull())
4056 {
4057 /* first, investigate the backup copy of the current hard disk
4058 * attachments to make it possible to re-attach existing diffs to
4059 * another device slot w/o losing their contents */
4060 if (mMediumAttachments.isBackedUp())
4061 {
4062 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4063
4064 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4065 uint32_t foundLevel = 0;
4066
4067 for (MediumAttachmentList::const_iterator
4068 it = oldAtts.begin();
4069 it != oldAtts.end();
4070 ++it)
4071 {
4072 uint32_t level = 0;
4073 MediumAttachment *pAttach = *it;
4074 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4075 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4076 if (pMedium.isNull())
4077 continue;
4078
4079 if (pMedium->i_getBase(&level) == medium)
4080 {
4081 /* skip the hard disk if its currently attached (we
4082 * cannot attach the same hard disk twice) */
4083 if (i_findAttachment(*mMediumAttachments.data(),
4084 pMedium))
4085 continue;
4086
4087 /* matched device, channel and bus (i.e. attached to the
4088 * same place) will win and immediately stop the search;
4089 * otherwise the attachment that has the youngest
4090 * descendant of medium will be used
4091 */
4092 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4093 {
4094 /* the simplest case: restore the whole attachment
4095 * and return, nothing else to do */
4096 mMediumAttachments->push_back(*it);
4097
4098 /* Reattach the medium to the VM. */
4099 if (fHotplug || fSilent)
4100 {
4101 mediumLock.release();
4102 treeLock.release();
4103 alock.release();
4104
4105 MediumLockList *pMediumLockList(new MediumLockList());
4106
4107 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4108 medium /* pToLockWrite */,
4109 false /* fMediumLockWriteAll */,
4110 NULL,
4111 *pMediumLockList);
4112 alock.acquire();
4113 if (FAILED(rc))
4114 delete pMediumLockList;
4115 else
4116 {
4117 Assert(mData->mSession.mLockedMedia.IsLocked());
4118 mData->mSession.mLockedMedia.Unlock();
4119 alock.release();
4120 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4121 mData->mSession.mLockedMedia.Lock();
4122 alock.acquire();
4123 }
4124 alock.release();
4125
4126 if (SUCCEEDED(rc))
4127 {
4128 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4129 /* Remove lock list in case of error. */
4130 if (FAILED(rc))
4131 {
4132 mData->mSession.mLockedMedia.Unlock();
4133 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4134 mData->mSession.mLockedMedia.Lock();
4135 }
4136 }
4137 }
4138
4139 return S_OK;
4140 }
4141 else if ( foundIt == oldAtts.end()
4142 || level > foundLevel /* prefer younger */
4143 )
4144 {
4145 foundIt = it;
4146 foundLevel = level;
4147 }
4148 }
4149 }
4150
4151 if (foundIt != oldAtts.end())
4152 {
4153 /* use the previously attached hard disk */
4154 medium = (*foundIt)->i_getMedium();
4155 mediumCaller.attach(medium);
4156 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
4157 mediumLock.attach(medium);
4158 /* not implicit, doesn't require association with this VM */
4159 fIndirect = false;
4160 associate = false;
4161 /* go right to the MediumAttachment creation */
4162 break;
4163 }
4164 }
4165
4166 /* must give up the medium lock and medium tree lock as below we
4167 * go over snapshots, which needs a lock with higher lock order. */
4168 mediumLock.release();
4169 treeLock.release();
4170
4171 /* then, search through snapshots for the best diff in the given
4172 * hard disk's chain to base the new diff on */
4173
4174 ComObjPtr<Medium> base;
4175 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4176 while (snap)
4177 {
4178 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4179
4180 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4181
4182 MediumAttachment *pAttachFound = NULL;
4183 uint32_t foundLevel = 0;
4184
4185 for (MediumAttachmentList::const_iterator
4186 it = snapAtts.begin();
4187 it != snapAtts.end();
4188 ++it)
4189 {
4190 MediumAttachment *pAttach = *it;
4191 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4192 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4193 if (pMedium.isNull())
4194 continue;
4195
4196 uint32_t level = 0;
4197 if (pMedium->i_getBase(&level) == medium)
4198 {
4199 /* matched device, channel and bus (i.e. attached to the
4200 * same place) will win and immediately stop the search;
4201 * otherwise the attachment that has the youngest
4202 * descendant of medium will be used
4203 */
4204 if ( pAttach->i_getDevice() == aDevice
4205 && pAttach->i_getPort() == aControllerPort
4206 && pAttach->i_getControllerName() == aName
4207 )
4208 {
4209 pAttachFound = pAttach;
4210 break;
4211 }
4212 else if ( !pAttachFound
4213 || level > foundLevel /* prefer younger */
4214 )
4215 {
4216 pAttachFound = pAttach;
4217 foundLevel = level;
4218 }
4219 }
4220 }
4221
4222 if (pAttachFound)
4223 {
4224 base = pAttachFound->i_getMedium();
4225 break;
4226 }
4227
4228 snap = snap->i_getParent();
4229 }
4230
4231 /* re-lock medium tree and the medium, as we need it below */
4232 treeLock.acquire();
4233 mediumLock.acquire();
4234
4235 /* found a suitable diff, use it as a base */
4236 if (!base.isNull())
4237 {
4238 medium = base;
4239 mediumCaller.attach(medium);
4240 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
4241 mediumLock.attach(medium);
4242 }
4243 }
4244
4245 Utf8Str strFullSnapshotFolder;
4246 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4247
4248 ComObjPtr<Medium> diff;
4249 diff.createObject();
4250 // store this diff in the same registry as the parent
4251 Guid uuidRegistryParent;
4252 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4253 {
4254 // parent image has no registry: this can happen if we're attaching a new immutable
4255 // image that has not yet been attached (medium then points to the base and we're
4256 // creating the diff image for the immutable, and the parent is not yet registered);
4257 // put the parent in the machine registry then
4258 mediumLock.release();
4259 treeLock.release();
4260 alock.release();
4261 i_addMediumToRegistry(medium);
4262 alock.acquire();
4263 treeLock.acquire();
4264 mediumLock.acquire();
4265 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4266 }
4267 rc = diff->init(mParent,
4268 medium->i_getPreferredDiffFormat(),
4269 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4270 uuidRegistryParent,
4271 DeviceType_HardDisk);
4272 if (FAILED(rc)) return rc;
4273
4274 /* Apply the normal locking logic to the entire chain. */
4275 MediumLockList *pMediumLockList(new MediumLockList());
4276 mediumLock.release();
4277 treeLock.release();
4278 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4279 diff /* pToLockWrite */,
4280 false /* fMediumLockWriteAll */,
4281 medium,
4282 *pMediumLockList);
4283 treeLock.acquire();
4284 mediumLock.acquire();
4285 if (SUCCEEDED(rc))
4286 {
4287 mediumLock.release();
4288 treeLock.release();
4289 rc = pMediumLockList->Lock();
4290 treeLock.acquire();
4291 mediumLock.acquire();
4292 if (FAILED(rc))
4293 setError(rc,
4294 tr("Could not lock medium when creating diff '%s'"),
4295 diff->i_getLocationFull().c_str());
4296 else
4297 {
4298 /* will release the lock before the potentially lengthy
4299 * operation, so protect with the special state */
4300 MachineState_T oldState = mData->mMachineState;
4301 i_setMachineState(MachineState_SettingUp);
4302
4303 mediumLock.release();
4304 treeLock.release();
4305 alock.release();
4306
4307 rc = medium->i_createDiffStorage(diff,
4308 medium->i_getPreferredDiffVariant(),
4309 pMediumLockList,
4310 NULL /* aProgress */,
4311 true /* aWait */,
4312 false /* aNotify */);
4313
4314 alock.acquire();
4315 treeLock.acquire();
4316 mediumLock.acquire();
4317
4318 i_setMachineState(oldState);
4319 }
4320 }
4321
4322 /* Unlock the media and free the associated memory. */
4323 delete pMediumLockList;
4324
4325 if (FAILED(rc)) return rc;
4326
4327 /* use the created diff for the actual attachment */
4328 medium = diff;
4329 mediumCaller.attach(medium);
4330 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
4331 mediumLock.attach(medium);
4332 }
4333 while (0);
4334
4335 ComObjPtr<MediumAttachment> attachment;
4336 attachment.createObject();
4337 rc = attachment->init(this,
4338 medium,
4339 aName,
4340 aControllerPort,
4341 aDevice,
4342 aType,
4343 fIndirect,
4344 false /* fPassthrough */,
4345 false /* fTempEject */,
4346 false /* fNonRotational */,
4347 false /* fDiscard */,
4348 fHotplug || ctrlType == StorageControllerType_USB /* fHotPluggable */,
4349 Utf8Str::Empty);
4350 if (FAILED(rc)) return rc;
4351
4352 if (associate && !medium.isNull())
4353 {
4354 // as the last step, associate the medium to the VM
4355 rc = medium->i_addBackReference(mData->mUuid);
4356 // here we can fail because of Deleting, or being in process of creating a Diff
4357 if (FAILED(rc)) return rc;
4358
4359 mediumLock.release();
4360 treeLock.release();
4361 alock.release();
4362 i_addMediumToRegistry(medium);
4363 alock.acquire();
4364 treeLock.acquire();
4365 mediumLock.acquire();
4366 }
4367
4368 /* success: finally remember the attachment */
4369 i_setModified(IsModified_Storage);
4370 mMediumAttachments.backup();
4371 mMediumAttachments->push_back(attachment);
4372
4373 mediumLock.release();
4374 treeLock.release();
4375 alock.release();
4376
4377 if (fHotplug || fSilent)
4378 {
4379 if (!medium.isNull())
4380 {
4381 MediumLockList *pMediumLockList(new MediumLockList());
4382
4383 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4384 medium /* pToLockWrite */,
4385 false /* fMediumLockWriteAll */,
4386 NULL,
4387 *pMediumLockList);
4388 alock.acquire();
4389 if (FAILED(rc))
4390 delete pMediumLockList;
4391 else
4392 {
4393 Assert(mData->mSession.mLockedMedia.IsLocked());
4394 mData->mSession.mLockedMedia.Unlock();
4395 alock.release();
4396 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4397 mData->mSession.mLockedMedia.Lock();
4398 alock.acquire();
4399 }
4400 alock.release();
4401 }
4402
4403 if (SUCCEEDED(rc))
4404 {
4405 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4406 /* Remove lock list in case of error. */
4407 if (FAILED(rc))
4408 {
4409 mData->mSession.mLockedMedia.Unlock();
4410 mData->mSession.mLockedMedia.Remove(attachment);
4411 mData->mSession.mLockedMedia.Lock();
4412 }
4413 }
4414 }
4415
4416 /* Save modified registries, but skip this machine as it's the caller's
4417 * job to save its settings like all other settings changes. */
4418 mParent->i_unmarkRegistryModified(i_getId());
4419 mParent->i_saveModifiedRegistries();
4420
4421 if (SUCCEEDED(rc))
4422 {
4423 if (fIndirect && medium != aM)
4424 mParent->i_onMediumConfigChanged(medium);
4425 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4426 }
4427
4428 return rc;
4429}
4430
4431HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4432 LONG aDevice)
4433{
4434 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4435 aName.c_str(), aControllerPort, aDevice));
4436
4437 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4438
4439 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4440 if (FAILED(rc)) return rc;
4441
4442 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4443
4444 /* Check for an existing controller. */
4445 ComObjPtr<StorageController> ctl;
4446 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4447 if (FAILED(rc)) return rc;
4448
4449 StorageControllerType_T ctrlType;
4450 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4451 if (FAILED(rc))
4452 return setError(E_FAIL,
4453 tr("Could not get type of controller '%s'"),
4454 aName.c_str());
4455
4456 bool fSilent = false;
4457 Utf8Str strReconfig;
4458
4459 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4460 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4461 if ( mData->mMachineState == MachineState_Paused
4462 && strReconfig == "1")
4463 fSilent = true;
4464
4465 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4466 bool fHotplug = false;
4467 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4468 fHotplug = true;
4469
4470 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4471 return setError(VBOX_E_INVALID_VM_STATE,
4472 tr("Controller '%s' does not support hot-plugging"),
4473 aName.c_str());
4474
4475 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4476 aName,
4477 aControllerPort,
4478 aDevice);
4479 if (!pAttach)
4480 return setError(VBOX_E_OBJECT_NOT_FOUND,
4481 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4482 aDevice, aControllerPort, aName.c_str());
4483
4484 if (fHotplug && !pAttach->i_getHotPluggable())
4485 return setError(VBOX_E_NOT_SUPPORTED,
4486 tr("The device slot %d on port %d of controller '%s' does not support hot-plugging"),
4487 aDevice, aControllerPort, aName.c_str());
4488
4489 /*
4490 * The VM has to detach the device before we delete any implicit diffs.
4491 * If this fails we can roll back without loosing data.
4492 */
4493 if (fHotplug || fSilent)
4494 {
4495 alock.release();
4496 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4497 alock.acquire();
4498 }
4499 if (FAILED(rc)) return rc;
4500
4501 /* If we are here everything went well and we can delete the implicit now. */
4502 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4503
4504 alock.release();
4505
4506 /* Save modified registries, but skip this machine as it's the caller's
4507 * job to save its settings like all other settings changes. */
4508 mParent->i_unmarkRegistryModified(i_getId());
4509 mParent->i_saveModifiedRegistries();
4510
4511 if (SUCCEEDED(rc))
4512 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4513
4514 return rc;
4515}
4516
4517HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4518 LONG aDevice, BOOL aPassthrough)
4519{
4520 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4521 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4522
4523 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4524
4525 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4526 if (FAILED(rc)) return rc;
4527
4528 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4529
4530 /* Check for an existing controller. */
4531 ComObjPtr<StorageController> ctl;
4532 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4533 if (FAILED(rc)) return rc;
4534
4535 StorageControllerType_T ctrlType;
4536 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4537 if (FAILED(rc))
4538 return setError(E_FAIL,
4539 tr("Could not get type of controller '%s'"),
4540 aName.c_str());
4541
4542 bool fSilent = false;
4543 Utf8Str strReconfig;
4544
4545 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4546 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4547 if ( mData->mMachineState == MachineState_Paused
4548 && strReconfig == "1")
4549 fSilent = true;
4550
4551 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4552 bool fHotplug = false;
4553 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4554 fHotplug = true;
4555
4556 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4557 return setError(VBOX_E_INVALID_VM_STATE,
4558 tr("Controller '%s' does not support hot-plugging which is required to change the passthrough setting while the VM is running"),
4559 aName.c_str());
4560
4561 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4562 aName,
4563 aControllerPort,
4564 aDevice);
4565 if (!pAttach)
4566 return setError(VBOX_E_OBJECT_NOT_FOUND,
4567 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4568 aDevice, aControllerPort, aName.c_str());
4569
4570
4571 i_setModified(IsModified_Storage);
4572 mMediumAttachments.backup();
4573
4574 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4575
4576 if (pAttach->i_getType() != DeviceType_DVD)
4577 return setError(E_INVALIDARG,
4578 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4579 aDevice, aControllerPort, aName.c_str());
4580
4581 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4582
4583 pAttach->i_updatePassthrough(!!aPassthrough);
4584
4585 attLock.release();
4586 alock.release();
4587 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4588 if (SUCCEEDED(rc) && fValueChanged)
4589 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4590
4591 return rc;
4592}
4593
4594HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4595 LONG aDevice, BOOL aTemporaryEject)
4596{
4597
4598 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4599 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4600
4601 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4602
4603 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4604 if (FAILED(rc)) return rc;
4605
4606 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4607 aName,
4608 aControllerPort,
4609 aDevice);
4610 if (!pAttach)
4611 return setError(VBOX_E_OBJECT_NOT_FOUND,
4612 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4613 aDevice, aControllerPort, aName.c_str());
4614
4615
4616 i_setModified(IsModified_Storage);
4617 mMediumAttachments.backup();
4618
4619 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4620
4621 if (pAttach->i_getType() != DeviceType_DVD)
4622 return setError(E_INVALIDARG,
4623 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4624 aDevice, aControllerPort, aName.c_str());
4625 pAttach->i_updateTempEject(!!aTemporaryEject);
4626
4627 return S_OK;
4628}
4629
4630HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4631 LONG aDevice, BOOL aNonRotational)
4632{
4633
4634 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4635 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4636
4637 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4638
4639 HRESULT rc = i_checkStateDependency(MutableStateDep);
4640 if (FAILED(rc)) return rc;
4641
4642 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4643
4644 if (Global::IsOnlineOrTransient(mData->mMachineState))
4645 return setError(VBOX_E_INVALID_VM_STATE,
4646 tr("Invalid machine state: %s"),
4647 Global::stringifyMachineState(mData->mMachineState));
4648
4649 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4650 aName,
4651 aControllerPort,
4652 aDevice);
4653 if (!pAttach)
4654 return setError(VBOX_E_OBJECT_NOT_FOUND,
4655 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4656 aDevice, aControllerPort, aName.c_str());
4657
4658
4659 i_setModified(IsModified_Storage);
4660 mMediumAttachments.backup();
4661
4662 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4663
4664 if (pAttach->i_getType() != DeviceType_HardDisk)
4665 return setError(E_INVALIDARG,
4666 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4667 aDevice, aControllerPort, aName.c_str());
4668 pAttach->i_updateNonRotational(!!aNonRotational);
4669
4670 return S_OK;
4671}
4672
4673HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4674 LONG aDevice, BOOL aDiscard)
4675{
4676
4677 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4678 aName.c_str(), aControllerPort, aDevice, aDiscard));
4679
4680 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4681
4682 HRESULT rc = i_checkStateDependency(MutableStateDep);
4683 if (FAILED(rc)) return rc;
4684
4685 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4686
4687 if (Global::IsOnlineOrTransient(mData->mMachineState))
4688 return setError(VBOX_E_INVALID_VM_STATE,
4689 tr("Invalid machine state: %s"),
4690 Global::stringifyMachineState(mData->mMachineState));
4691
4692 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4693 aName,
4694 aControllerPort,
4695 aDevice);
4696 if (!pAttach)
4697 return setError(VBOX_E_OBJECT_NOT_FOUND,
4698 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4699 aDevice, aControllerPort, aName.c_str());
4700
4701
4702 i_setModified(IsModified_Storage);
4703 mMediumAttachments.backup();
4704
4705 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4706
4707 if (pAttach->i_getType() != DeviceType_HardDisk)
4708 return setError(E_INVALIDARG,
4709 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"),
4710 aDevice, aControllerPort, aName.c_str());
4711 pAttach->i_updateDiscard(!!aDiscard);
4712
4713 return S_OK;
4714}
4715
4716HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4717 LONG aDevice, BOOL aHotPluggable)
4718{
4719 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4720 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4721
4722 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4723
4724 HRESULT rc = i_checkStateDependency(MutableStateDep);
4725 if (FAILED(rc)) return rc;
4726
4727 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4728
4729 if (Global::IsOnlineOrTransient(mData->mMachineState))
4730 return setError(VBOX_E_INVALID_VM_STATE,
4731 tr("Invalid machine state: %s"),
4732 Global::stringifyMachineState(mData->mMachineState));
4733
4734 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4735 aName,
4736 aControllerPort,
4737 aDevice);
4738 if (!pAttach)
4739 return setError(VBOX_E_OBJECT_NOT_FOUND,
4740 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4741 aDevice, aControllerPort, aName.c_str());
4742
4743 /* Check for an existing controller. */
4744 ComObjPtr<StorageController> ctl;
4745 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4746 if (FAILED(rc)) return rc;
4747
4748 StorageControllerType_T ctrlType;
4749 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4750 if (FAILED(rc))
4751 return setError(E_FAIL,
4752 tr("Could not get type of controller '%s'"),
4753 aName.c_str());
4754
4755 if (!i_isControllerHotplugCapable(ctrlType))
4756 return setError(VBOX_E_NOT_SUPPORTED,
4757 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4758 aName.c_str());
4759
4760 /* silently ignore attempts to modify the hot-plug status of USB devices */
4761 if (ctrlType == StorageControllerType_USB)
4762 return S_OK;
4763
4764 i_setModified(IsModified_Storage);
4765 mMediumAttachments.backup();
4766
4767 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4768
4769 if (pAttach->i_getType() == DeviceType_Floppy)
4770 return setError(E_INVALIDARG,
4771 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"),
4772 aDevice, aControllerPort, aName.c_str());
4773 pAttach->i_updateHotPluggable(!!aHotPluggable);
4774
4775 return S_OK;
4776}
4777
4778HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4779 LONG aDevice)
4780{
4781 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4782 aName.c_str(), aControllerPort, aDevice));
4783
4784 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4785}
4786
4787HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4788 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4789{
4790 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4791 aName.c_str(), aControllerPort, aDevice));
4792
4793 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4794
4795 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4796 if (FAILED(rc)) return rc;
4797
4798 if (Global::IsOnlineOrTransient(mData->mMachineState))
4799 return setError(VBOX_E_INVALID_VM_STATE,
4800 tr("Invalid machine state: %s"),
4801 Global::stringifyMachineState(mData->mMachineState));
4802
4803 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4804 aName,
4805 aControllerPort,
4806 aDevice);
4807 if (!pAttach)
4808 return setError(VBOX_E_OBJECT_NOT_FOUND,
4809 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4810 aDevice, aControllerPort, aName.c_str());
4811
4812
4813 i_setModified(IsModified_Storage);
4814 mMediumAttachments.backup();
4815
4816 IBandwidthGroup *iB = aBandwidthGroup;
4817 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4818 if (aBandwidthGroup && group.isNull())
4819 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4820
4821 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4822
4823 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4824 if (strBandwidthGroupOld.isNotEmpty())
4825 {
4826 /* Get the bandwidth group object and release it - this must not fail. */
4827 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4828 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4829 Assert(SUCCEEDED(rc));
4830
4831 pBandwidthGroupOld->i_release();
4832 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4833 }
4834
4835 if (!group.isNull())
4836 {
4837 group->i_reference();
4838 pAttach->i_updateBandwidthGroup(group->i_getName());
4839 }
4840
4841 return S_OK;
4842}
4843
4844HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4845 LONG aControllerPort,
4846 LONG aDevice,
4847 DeviceType_T aType)
4848{
4849 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4850 aName.c_str(), aControllerPort, aDevice, aType));
4851
4852 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4853}
4854
4855
4856HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4857 LONG aControllerPort,
4858 LONG aDevice,
4859 BOOL aForce)
4860{
4861 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4862 aName.c_str(), aControllerPort, aForce));
4863
4864 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4865}
4866
4867HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4868 LONG aControllerPort,
4869 LONG aDevice,
4870 const ComPtr<IMedium> &aMedium,
4871 BOOL aForce)
4872{
4873 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4874 aName.c_str(), aControllerPort, aDevice, aForce));
4875
4876 // request the host lock first, since might be calling Host methods for getting host drives;
4877 // next, protect the media tree all the while we're in here, as well as our member variables
4878 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4879 this->lockHandle(),
4880 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4881
4882 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4883 if (FAILED(hrc)) return hrc;
4884
4885 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4886 aName,
4887 aControllerPort,
4888 aDevice);
4889 if (pAttach.isNull())
4890 return setError(VBOX_E_OBJECT_NOT_FOUND,
4891 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4892 aDevice, aControllerPort, aName.c_str());
4893
4894 /* Remember previously mounted medium. The medium before taking the
4895 * backup is not necessarily the same thing. */
4896 ComObjPtr<Medium> oldmedium;
4897 oldmedium = pAttach->i_getMedium();
4898
4899 IMedium *iM = aMedium;
4900 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4901 if (aMedium && pMedium.isNull())
4902 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4903
4904 /* Check if potential medium is already mounted */
4905 if (pMedium == oldmedium)
4906 return S_OK;
4907
4908 AutoCaller mediumCaller(pMedium);
4909 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
4910
4911 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4912 if (pMedium)
4913 {
4914 DeviceType_T mediumType = pAttach->i_getType();
4915 switch (mediumType)
4916 {
4917 case DeviceType_DVD:
4918 case DeviceType_Floppy:
4919 break;
4920
4921 default:
4922 return setError(VBOX_E_INVALID_OBJECT_STATE,
4923 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4924 aControllerPort,
4925 aDevice,
4926 aName.c_str());
4927 }
4928 }
4929
4930 i_setModified(IsModified_Storage);
4931 mMediumAttachments.backup();
4932
4933 {
4934 // The backup operation makes the pAttach reference point to the
4935 // old settings. Re-get the correct reference.
4936 pAttach = i_findAttachment(*mMediumAttachments.data(),
4937 aName,
4938 aControllerPort,
4939 aDevice);
4940 if (!oldmedium.isNull())
4941 oldmedium->i_removeBackReference(mData->mUuid);
4942 if (!pMedium.isNull())
4943 {
4944 pMedium->i_addBackReference(mData->mUuid);
4945
4946 mediumLock.release();
4947 multiLock.release();
4948 i_addMediumToRegistry(pMedium);
4949 multiLock.acquire();
4950 mediumLock.acquire();
4951 }
4952
4953 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4954 pAttach->i_updateMedium(pMedium);
4955 }
4956
4957 i_setModified(IsModified_Storage);
4958
4959 mediumLock.release();
4960 multiLock.release();
4961 HRESULT rc = i_onMediumChange(pAttach, aForce);
4962 multiLock.acquire();
4963 mediumLock.acquire();
4964
4965 /* On error roll back this change only. */
4966 if (FAILED(rc))
4967 {
4968 if (!pMedium.isNull())
4969 pMedium->i_removeBackReference(mData->mUuid);
4970 pAttach = i_findAttachment(*mMediumAttachments.data(),
4971 aName,
4972 aControllerPort,
4973 aDevice);
4974 /* If the attachment is gone in the meantime, bail out. */
4975 if (pAttach.isNull())
4976 return rc;
4977 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4978 if (!oldmedium.isNull())
4979 oldmedium->i_addBackReference(mData->mUuid);
4980 pAttach->i_updateMedium(oldmedium);
4981 }
4982
4983 mediumLock.release();
4984 multiLock.release();
4985
4986 /* Save modified registries, but skip this machine as it's the caller's
4987 * job to save its settings like all other settings changes. */
4988 mParent->i_unmarkRegistryModified(i_getId());
4989 mParent->i_saveModifiedRegistries();
4990
4991 return rc;
4992}
4993HRESULT Machine::getMedium(const com::Utf8Str &aName,
4994 LONG aControllerPort,
4995 LONG aDevice,
4996 ComPtr<IMedium> &aMedium)
4997{
4998 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4999 aName.c_str(), aControllerPort, aDevice));
5000
5001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5002
5003 aMedium = NULL;
5004
5005 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5006 aName,
5007 aControllerPort,
5008 aDevice);
5009 if (pAttach.isNull())
5010 return setError(VBOX_E_OBJECT_NOT_FOUND,
5011 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5012 aDevice, aControllerPort, aName.c_str());
5013
5014 aMedium = pAttach->i_getMedium();
5015
5016 return S_OK;
5017}
5018
5019HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
5020{
5021 if (aSlot < RT_ELEMENTS(mSerialPorts))
5022 {
5023 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5024 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5025 return S_OK;
5026 }
5027 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
5028}
5029
5030HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
5031{
5032 if (aSlot < RT_ELEMENTS(mParallelPorts))
5033 {
5034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5035 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5036 return S_OK;
5037 }
5038 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
5039}
5040
5041
5042HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
5043{
5044 /* Do not assert if slot is out of range, just return the advertised
5045 status. testdriver/vbox.py triggers this in logVmInfo. */
5046 if (aSlot >= mNetworkAdapters.size())
5047 return setError(E_INVALIDARG,
5048 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
5049 aSlot, mNetworkAdapters.size());
5050
5051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5052
5053 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
5054
5055 return S_OK;
5056}
5057
5058HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
5059{
5060 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5061
5062 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
5063 size_t i = 0;
5064 for (settings::StringsMap::const_iterator
5065 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5066 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5067 ++it, ++i)
5068 aKeys[i] = it->first;
5069
5070 return S_OK;
5071}
5072
5073 /**
5074 * @note Locks this object for reading.
5075 */
5076HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
5077 com::Utf8Str &aValue)
5078{
5079 /* start with nothing found */
5080 aValue = "";
5081
5082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5083
5084 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5085 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5086 // found:
5087 aValue = it->second; // source is a Utf8Str
5088
5089 /* return the result to caller (may be empty) */
5090 return S_OK;
5091}
5092
5093 /**
5094 * @note Locks mParent for writing + this object for writing.
5095 */
5096HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5097{
5098 /* Because control characters in aKey have caused problems in the settings
5099 * they are rejected unless the key should be deleted. */
5100 if (!aValue.isEmpty())
5101 {
5102 for (size_t i = 0; i < aKey.length(); ++i)
5103 {
5104 char ch = aKey[i];
5105 if (RTLocCIsCntrl(ch))
5106 return E_INVALIDARG;
5107 }
5108 }
5109
5110 Utf8Str strOldValue; // empty
5111
5112 // locking note: we only hold the read lock briefly to look up the old value,
5113 // then release it and call the onExtraCanChange callbacks. There is a small
5114 // chance of a race insofar as the callback might be called twice if two callers
5115 // change the same key at the same time, but that's a much better solution
5116 // than the deadlock we had here before. The actual changing of the extradata
5117 // is then performed under the write lock and race-free.
5118
5119 // look up the old value first; if nothing has changed then we need not do anything
5120 {
5121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5122
5123 // For snapshots don't even think about allowing changes, extradata
5124 // is global for a machine, so there is nothing snapshot specific.
5125 if (i_isSnapshotMachine())
5126 return setError(VBOX_E_INVALID_VM_STATE,
5127 tr("Cannot set extradata for a snapshot"));
5128
5129 // check if the right IMachine instance is used
5130 if (mData->mRegistered && !i_isSessionMachine())
5131 return setError(VBOX_E_INVALID_VM_STATE,
5132 tr("Cannot set extradata for an immutable machine"));
5133
5134 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5135 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5136 strOldValue = it->second;
5137 }
5138
5139 bool fChanged;
5140 if ((fChanged = (strOldValue != aValue)))
5141 {
5142 // ask for permission from all listeners outside the locks;
5143 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5144 // lock to copy the list of callbacks to invoke
5145 Bstr bstrError;
5146 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
5147 {
5148 const char *sep = bstrError.isEmpty() ? "" : ": ";
5149 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
5150 return setError(E_ACCESSDENIED,
5151 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5152 aKey.c_str(),
5153 aValue.c_str(),
5154 sep,
5155 bstrError.raw());
5156 }
5157
5158 // data is changing and change not vetoed: then write it out under the lock
5159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5160
5161 if (aValue.isEmpty())
5162 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5163 else
5164 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5165 // creates a new key if needed
5166
5167 bool fNeedsGlobalSaveSettings = false;
5168 // This saving of settings is tricky: there is no "old state" for the
5169 // extradata items at all (unlike all other settings), so the old/new
5170 // settings comparison would give a wrong result!
5171 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
5172
5173 if (fNeedsGlobalSaveSettings)
5174 {
5175 // save the global settings; for that we should hold only the VirtualBox lock
5176 alock.release();
5177 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5178 mParent->i_saveSettings();
5179 }
5180 }
5181
5182 // fire notification outside the lock
5183 if (fChanged)
5184 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
5185
5186 return S_OK;
5187}
5188
5189HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5190{
5191 aProgress = NULL;
5192 NOREF(aSettingsFilePath);
5193 ReturnComNotImplemented();
5194}
5195
5196HRESULT Machine::saveSettings()
5197{
5198 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5199
5200 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5201 if (FAILED(rc)) return rc;
5202
5203 /* the settings file path may never be null */
5204 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5205
5206 /* save all VM data excluding snapshots */
5207 bool fNeedsGlobalSaveSettings = false;
5208 rc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
5209 mlock.release();
5210
5211 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5212 {
5213 // save the global settings; for that we should hold only the VirtualBox lock
5214 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5215 rc = mParent->i_saveSettings();
5216 }
5217
5218 return rc;
5219}
5220
5221
5222HRESULT Machine::discardSettings()
5223{
5224 /*
5225 * We need to take the machine list lock here as well as the machine one
5226 * or we'll get into trouble should any media stuff require rolling back.
5227 *
5228 * Details:
5229 *
5230 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5231 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5232 * 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]
5233 * 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
5234 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5235 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5236 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5237 * 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
5238 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5239 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5240 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5241 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5242 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5243 * 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]
5244 * 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] (*)
5245 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5246 * 0:005> k
5247 * # Child-SP RetAddr Call Site
5248 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5249 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5250 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5251 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5252 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5253 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5254 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5255 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5256 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5257 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5258 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5259 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5260 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5261 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5262 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5263 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5264 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5265 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5266 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5267 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5268 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5269 *
5270 */
5271 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5273
5274 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5275 if (FAILED(rc)) return rc;
5276
5277 /*
5278 * during this rollback, the session will be notified if data has
5279 * been actually changed
5280 */
5281 i_rollback(true /* aNotify */);
5282
5283 return S_OK;
5284}
5285
5286/** @note Locks objects! */
5287HRESULT Machine::unregister(AutoCaller &autoCaller,
5288 CleanupMode_T aCleanupMode,
5289 std::vector<ComPtr<IMedium> > &aMedia)
5290{
5291 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5292
5293 Guid id(i_getId());
5294
5295 if (mData->mSession.mState != SessionState_Unlocked)
5296 return setError(VBOX_E_INVALID_OBJECT_STATE,
5297 tr("Cannot unregister the machine '%s' while it is locked"),
5298 mUserData->s.strName.c_str());
5299
5300 // wait for state dependents to drop to zero
5301 i_ensureNoStateDependencies(alock);
5302
5303 if (!mData->mAccessible)
5304 {
5305 // inaccessible machines can only be unregistered; uninitialize ourselves
5306 // here because currently there may be no unregistered that are inaccessible
5307 // (this state combination is not supported). Note releasing the caller and
5308 // leaving the lock before calling uninit()
5309 alock.release();
5310 autoCaller.release();
5311
5312 uninit();
5313
5314 mParent->i_unregisterMachine(this, CleanupMode_UnregisterOnly, id);
5315 // calls VirtualBox::i_saveSettings()
5316
5317 return S_OK;
5318 }
5319
5320 HRESULT rc = S_OK;
5321 mData->llFilesToDelete.clear();
5322
5323 if (!mSSData->strStateFilePath.isEmpty())
5324 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5325
5326 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
5327 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
5328 mData->llFilesToDelete.push_back(strNVRAMFile);
5329
5330 // This list collects the medium objects from all medium attachments
5331 // which we will detach from the machine and its snapshots, in a specific
5332 // order which allows for closing all media without getting "media in use"
5333 // errors, simply by going through the list from the front to the back:
5334 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5335 // and must be closed before the parent media from the snapshots, or closing the parents
5336 // will fail because they still have children);
5337 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5338 // the root ("first") snapshot of the machine.
5339 MediaList llMedia;
5340
5341 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5342 && mMediumAttachments->size()
5343 )
5344 {
5345 // we have media attachments: detach them all and add the Medium objects to our list
5346 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5347 }
5348
5349 if (mData->mFirstSnapshot)
5350 {
5351 // add the media from the medium attachments of the snapshots to
5352 // llMedia as well, after the "main" machine media;
5353 // Snapshot::uninitAll() calls Machine::detachAllMedia() for each
5354 // snapshot machine, depth first.
5355
5356 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5357 MachineState_T oldState = mData->mMachineState;
5358 mData->mMachineState = MachineState_DeletingSnapshot;
5359
5360 // make a copy of the first snapshot reference so the refcount does not
5361 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
5362 // (would hang due to the AutoCaller voodoo)
5363 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5364
5365 // GO!
5366 pFirstSnapshot->i_uninitAll(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5367
5368 mData->mMachineState = oldState;
5369 }
5370
5371 if (FAILED(rc))
5372 {
5373 i_rollbackMedia();
5374 return rc;
5375 }
5376
5377 // commit all the media changes made above
5378 i_commitMedia();
5379
5380 mData->mRegistered = false;
5381
5382 // machine lock no longer needed
5383 alock.release();
5384
5385 /* Make sure that the settings of the current VM are not saved, because
5386 * they are rather crippled at this point to meet the cleanup expectations
5387 * and there's no point destroying the VM config on disk just because. */
5388 mParent->i_unmarkRegistryModified(id);
5389
5390 // return media to caller
5391 aMedia.resize(llMedia.size());
5392 size_t i = 0;
5393 for (MediaList::const_iterator
5394 it = llMedia.begin();
5395 it != llMedia.end();
5396 ++it, ++i)
5397 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5398
5399 mParent->i_unregisterMachine(this, aCleanupMode, id);
5400 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5401
5402 return S_OK;
5403}
5404
5405/**
5406 * Task record for deleting a machine config.
5407 */
5408class Machine::DeleteConfigTask
5409 : public Machine::Task
5410{
5411public:
5412 DeleteConfigTask(Machine *m,
5413 Progress *p,
5414 const Utf8Str &t,
5415 const RTCList<ComPtr<IMedium> > &llMediums,
5416 const StringsList &llFilesToDelete)
5417 : Task(m, p, t),
5418 m_llMediums(llMediums),
5419 m_llFilesToDelete(llFilesToDelete)
5420 {}
5421
5422private:
5423 void handler()
5424 {
5425 try
5426 {
5427 m_pMachine->i_deleteConfigHandler(*this);
5428 }
5429 catch (...)
5430 {
5431 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5432 }
5433 }
5434
5435 RTCList<ComPtr<IMedium> > m_llMediums;
5436 StringsList m_llFilesToDelete;
5437
5438 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5439};
5440
5441/**
5442 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5443 * SessionMachine::taskHandler().
5444 *
5445 * @note Locks this object for writing.
5446 *
5447 * @param task
5448 * @return
5449 */
5450void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5451{
5452 LogFlowThisFuncEnter();
5453
5454 AutoCaller autoCaller(this);
5455 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5456 if (FAILED(autoCaller.hrc()))
5457 {
5458 /* we might have been uninitialized because the session was accidentally
5459 * closed by the client, so don't assert */
5460 HRESULT rc = setError(E_FAIL,
5461 tr("The session has been accidentally closed"));
5462 task.m_pProgress->i_notifyComplete(rc);
5463 LogFlowThisFuncLeave();
5464 return;
5465 }
5466
5467 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5468
5469 HRESULT rc = S_OK;
5470
5471 try
5472 {
5473 ULONG uLogHistoryCount = 3;
5474 ComPtr<ISystemProperties> systemProperties;
5475 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5476 if (FAILED(rc)) throw rc;
5477
5478 if (!systemProperties.isNull())
5479 {
5480 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5481 if (FAILED(rc)) throw rc;
5482 }
5483
5484 MachineState_T oldState = mData->mMachineState;
5485 i_setMachineState(MachineState_SettingUp);
5486 alock.release();
5487 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5488 {
5489 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5490 {
5491 AutoCaller mac(pMedium);
5492 if (FAILED(mac.hrc())) throw mac.hrc();
5493 Utf8Str strLocation = pMedium->i_getLocationFull();
5494 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5495 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5496 if (FAILED(rc)) throw rc;
5497 }
5498 if (pMedium->i_isMediumFormatFile())
5499 {
5500 ComPtr<IProgress> pProgress2;
5501 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5502 if (FAILED(rc)) throw rc;
5503 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5504 if (FAILED(rc)) throw rc;
5505 }
5506
5507 /* Close the medium, deliberately without checking the return
5508 * code, and without leaving any trace in the error info, as
5509 * a failure here is a very minor issue, which shouldn't happen
5510 * as above we even managed to delete the medium. */
5511 {
5512 ErrorInfoKeeper eik;
5513 pMedium->Close();
5514 }
5515 }
5516 i_setMachineState(oldState);
5517 alock.acquire();
5518
5519 // delete the files pushed on the task list by Machine::Delete()
5520 // (this includes saved states of the machine and snapshots and
5521 // medium storage files from the IMedium list passed in, and the
5522 // machine XML file)
5523 for (StringsList::const_iterator
5524 it = task.m_llFilesToDelete.begin();
5525 it != task.m_llFilesToDelete.end();
5526 ++it)
5527 {
5528 const Utf8Str &strFile = *it;
5529 LogFunc(("Deleting file %s\n", strFile.c_str()));
5530 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5531 if (FAILED(rc)) throw rc;
5532 i_deleteFile(strFile);
5533 }
5534
5535 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5536 if (FAILED(rc)) throw rc;
5537
5538 /* delete the settings only when the file actually exists */
5539 if (mData->pMachineConfigFile->fileExists())
5540 {
5541 /* Delete any backup or uncommitted XML files. Ignore failures.
5542 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5543 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5544 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5545 i_deleteFile(otherXml, true /* fIgnoreFailures */);
5546 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5547 i_deleteFile(otherXml, true /* fIgnoreFailures */);
5548
5549 /* delete the Logs folder, nothing important should be left
5550 * there (we don't check for errors because the user might have
5551 * some private files there that we don't want to delete) */
5552 Utf8Str logFolder;
5553 getLogFolder(logFolder);
5554 Assert(logFolder.length());
5555 if (RTDirExists(logFolder.c_str()))
5556 {
5557 /* Delete all VBox.log[.N] files from the Logs folder
5558 * (this must be in sync with the rotation logic in
5559 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5560 * files that may have been created by the GUI. */
5561 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5562 i_deleteFile(log, true /* fIgnoreFailures */);
5563 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5564 i_deleteFile(log, true /* fIgnoreFailures */);
5565 for (ULONG i = uLogHistoryCount; i > 0; i--)
5566 {
5567 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5568 i_deleteFile(log, true /* fIgnoreFailures */);
5569 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5570 i_deleteFile(log, true /* fIgnoreFailures */);
5571 }
5572 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5573 i_deleteFile(log, true /* fIgnoreFailures */);
5574#if defined(RT_OS_WINDOWS)
5575 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5576 i_deleteFile(log, true /* fIgnoreFailures */);
5577 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5578 i_deleteFile(log, true /* fIgnoreFailures */);
5579#endif
5580
5581 RTDirRemove(logFolder.c_str());
5582 }
5583
5584 /* delete the Snapshots folder, nothing important should be left
5585 * there (we don't check for errors because the user might have
5586 * some private files there that we don't want to delete) */
5587 Utf8Str strFullSnapshotFolder;
5588 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5589 Assert(!strFullSnapshotFolder.isEmpty());
5590 if (RTDirExists(strFullSnapshotFolder.c_str()))
5591 RTDirRemove(strFullSnapshotFolder.c_str());
5592
5593 // delete the directory that contains the settings file, but only
5594 // if it matches the VM name
5595 Utf8Str settingsDir;
5596 if (i_isInOwnDir(&settingsDir))
5597 RTDirRemove(settingsDir.c_str());
5598 }
5599
5600 alock.release();
5601
5602 mParent->i_saveModifiedRegistries();
5603 }
5604 catch (HRESULT aRC) { rc = aRC; }
5605
5606 task.m_pProgress->i_notifyComplete(rc);
5607
5608 LogFlowThisFuncLeave();
5609}
5610
5611HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5612{
5613 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5614
5615 HRESULT rc = i_checkStateDependency(MutableStateDep);
5616 if (FAILED(rc)) return rc;
5617
5618 if (mData->mRegistered)
5619 return setError(VBOX_E_INVALID_VM_STATE,
5620 tr("Cannot delete settings of a registered machine"));
5621
5622 // collect files to delete
5623 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5624 // machine config file
5625 if (mData->pMachineConfigFile->fileExists())
5626 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5627 // backup of machine config file
5628 Utf8Str strTmp(mData->m_strConfigFileFull);
5629 strTmp.append("-prev");
5630 if (RTFileExists(strTmp.c_str()))
5631 llFilesToDelete.push_back(strTmp);
5632
5633 RTCList<ComPtr<IMedium> > llMediums;
5634 for (size_t i = 0; i < aMedia.size(); ++i)
5635 {
5636 IMedium *pIMedium(aMedia[i]);
5637 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5638 if (pMedium.isNull())
5639 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
5640 SafeArray<BSTR> ids;
5641 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5642 if (FAILED(rc)) return rc;
5643 /* At this point the medium should not have any back references
5644 * anymore. If it has it is attached to another VM and *must* not
5645 * deleted. */
5646 if (ids.size() < 1)
5647 llMediums.append(pMedium);
5648 }
5649
5650 ComObjPtr<Progress> pProgress;
5651 pProgress.createObject();
5652 rc = pProgress->init(i_getVirtualBox(),
5653 static_cast<IMachine*>(this) /* aInitiator */,
5654 tr("Deleting files"),
5655 true /* fCancellable */,
5656 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5657 tr("Collecting file inventory"));
5658 if (FAILED(rc))
5659 return rc;
5660
5661 /* create and start the task on a separate thread (note that it will not
5662 * start working until we release alock) */
5663 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5664 rc = pTask->createThread();
5665 pTask = NULL;
5666 if (FAILED(rc))
5667 return rc;
5668
5669 pProgress.queryInterfaceTo(aProgress.asOutParam());
5670
5671 LogFlowFuncLeave();
5672
5673 return S_OK;
5674}
5675
5676HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5677{
5678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5679
5680 ComObjPtr<Snapshot> pSnapshot;
5681 HRESULT rc;
5682
5683 if (aNameOrId.isEmpty())
5684 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5685 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5686 else
5687 {
5688 Guid uuid(aNameOrId);
5689 if (uuid.isValid())
5690 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5691 else
5692 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5693 }
5694 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5695
5696 return rc;
5697}
5698
5699HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5700 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5701{
5702 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5703
5704 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5705 if (FAILED(rc)) return rc;
5706
5707 ComObjPtr<SharedFolder> sharedFolder;
5708 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5709 if (SUCCEEDED(rc))
5710 return setError(VBOX_E_OBJECT_IN_USE,
5711 tr("Shared folder named '%s' already exists"),
5712 aName.c_str());
5713
5714 sharedFolder.createObject();
5715 rc = sharedFolder->init(i_getMachine(),
5716 aName,
5717 aHostPath,
5718 !!aWritable,
5719 !!aAutomount,
5720 aAutoMountPoint,
5721 true /* fFailOnError */);
5722 if (FAILED(rc)) return rc;
5723
5724 i_setModified(IsModified_SharedFolders);
5725 mHWData.backup();
5726 mHWData->mSharedFolders.push_back(sharedFolder);
5727
5728 /* inform the direct session if any */
5729 alock.release();
5730 i_onSharedFolderChange();
5731
5732 return S_OK;
5733}
5734
5735HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5736{
5737 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5738
5739 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5740 if (FAILED(rc)) return rc;
5741
5742 ComObjPtr<SharedFolder> sharedFolder;
5743 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5744 if (FAILED(rc)) return rc;
5745
5746 i_setModified(IsModified_SharedFolders);
5747 mHWData.backup();
5748 mHWData->mSharedFolders.remove(sharedFolder);
5749
5750 /* inform the direct session if any */
5751 alock.release();
5752 i_onSharedFolderChange();
5753
5754 return S_OK;
5755}
5756
5757HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5758{
5759 /* start with No */
5760 *aCanShow = FALSE;
5761
5762 ComPtr<IInternalSessionControl> directControl;
5763 {
5764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5765
5766 if (mData->mSession.mState != SessionState_Locked)
5767 return setError(VBOX_E_INVALID_VM_STATE,
5768 tr("Machine is not locked for session (session state: %s)"),
5769 Global::stringifySessionState(mData->mSession.mState));
5770
5771 if (mData->mSession.mLockType == LockType_VM)
5772 directControl = mData->mSession.mDirectControl;
5773 }
5774
5775 /* ignore calls made after #OnSessionEnd() is called */
5776 if (!directControl)
5777 return S_OK;
5778
5779 LONG64 dummy;
5780 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5781}
5782
5783HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5784{
5785 ComPtr<IInternalSessionControl> directControl;
5786 {
5787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5788
5789 if (mData->mSession.mState != SessionState_Locked)
5790 return setError(E_FAIL,
5791 tr("Machine is not locked for session (session state: %s)"),
5792 Global::stringifySessionState(mData->mSession.mState));
5793
5794 if (mData->mSession.mLockType == LockType_VM)
5795 directControl = mData->mSession.mDirectControl;
5796 }
5797
5798 /* ignore calls made after #OnSessionEnd() is called */
5799 if (!directControl)
5800 return S_OK;
5801
5802 BOOL dummy;
5803 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5804}
5805
5806#ifdef VBOX_WITH_GUEST_PROPS
5807/**
5808 * Look up a guest property in VBoxSVC's internal structures.
5809 */
5810HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5811 com::Utf8Str &aValue,
5812 LONG64 *aTimestamp,
5813 com::Utf8Str &aFlags) const
5814{
5815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5816
5817 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5818 if (it != mHWData->mGuestProperties.end())
5819 {
5820 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5821 aValue = it->second.strValue;
5822 *aTimestamp = it->second.mTimestamp;
5823 GuestPropWriteFlags(it->second.mFlags, szFlags);
5824 aFlags = Utf8Str(szFlags);
5825 }
5826
5827 return S_OK;
5828}
5829
5830/**
5831 * Query the VM that a guest property belongs to for the property.
5832 * @returns E_ACCESSDENIED if the VM process is not available or not
5833 * currently handling queries and the lookup should then be done in
5834 * VBoxSVC.
5835 */
5836HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5837 com::Utf8Str &aValue,
5838 LONG64 *aTimestamp,
5839 com::Utf8Str &aFlags) const
5840{
5841 HRESULT rc = S_OK;
5842 Bstr bstrValue;
5843 Bstr bstrFlags;
5844
5845 ComPtr<IInternalSessionControl> directControl;
5846 {
5847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5848 if (mData->mSession.mLockType == LockType_VM)
5849 directControl = mData->mSession.mDirectControl;
5850 }
5851
5852 /* ignore calls made after #OnSessionEnd() is called */
5853 if (!directControl)
5854 rc = E_ACCESSDENIED;
5855 else
5856 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5857 0 /* accessMode */,
5858 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5859
5860 aValue = bstrValue;
5861 aFlags = bstrFlags;
5862
5863 return rc;
5864}
5865#endif // VBOX_WITH_GUEST_PROPS
5866
5867HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5868 com::Utf8Str &aValue,
5869 LONG64 *aTimestamp,
5870 com::Utf8Str &aFlags)
5871{
5872#ifndef VBOX_WITH_GUEST_PROPS
5873 ReturnComNotImplemented();
5874#else // VBOX_WITH_GUEST_PROPS
5875
5876 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5877
5878 if (rc == E_ACCESSDENIED)
5879 /* The VM is not running or the service is not (yet) accessible */
5880 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5881 return rc;
5882#endif // VBOX_WITH_GUEST_PROPS
5883}
5884
5885HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5886{
5887 LONG64 dummyTimestamp;
5888 com::Utf8Str dummyFlags;
5889 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5890 return rc;
5891
5892}
5893HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5894{
5895 com::Utf8Str dummyFlags;
5896 com::Utf8Str dummyValue;
5897 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5898 return rc;
5899}
5900
5901#ifdef VBOX_WITH_GUEST_PROPS
5902/**
5903 * Set a guest property in VBoxSVC's internal structures.
5904 */
5905HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5906 const com::Utf8Str &aFlags, bool fDelete)
5907{
5908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5909 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5910 if (FAILED(rc)) return rc;
5911
5912 try
5913 {
5914 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5915 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5916 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5917
5918 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
5919 return setError(E_INVALIDARG, tr("Properties with TRANSIENT or TRANSRESET flag cannot be set or modified if VM is not running"));
5920
5921 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5922 if (it == mHWData->mGuestProperties.end())
5923 {
5924 if (!fDelete)
5925 {
5926 i_setModified(IsModified_MachineData);
5927 mHWData.backupEx();
5928
5929 RTTIMESPEC time;
5930 HWData::GuestProperty prop;
5931 prop.strValue = aValue;
5932 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5933 prop.mFlags = fFlags;
5934 mHWData->mGuestProperties[aName] = prop;
5935 }
5936 }
5937 else
5938 {
5939 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5940 {
5941 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5942 }
5943 else
5944 {
5945 i_setModified(IsModified_MachineData);
5946 mHWData.backupEx();
5947
5948 /* The backupEx() operation invalidates our iterator,
5949 * so get a new one. */
5950 it = mHWData->mGuestProperties.find(aName);
5951 Assert(it != mHWData->mGuestProperties.end());
5952
5953 if (!fDelete)
5954 {
5955 RTTIMESPEC time;
5956 it->second.strValue = aValue;
5957 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5958 it->second.mFlags = fFlags;
5959 }
5960 else
5961 mHWData->mGuestProperties.erase(it);
5962 }
5963 }
5964
5965 if (SUCCEEDED(rc))
5966 {
5967 alock.release();
5968
5969 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fDelete);
5970 }
5971 }
5972 catch (std::bad_alloc &)
5973 {
5974 rc = E_OUTOFMEMORY;
5975 }
5976
5977 return rc;
5978}
5979
5980/**
5981 * Set a property on the VM that that property belongs to.
5982 * @returns E_ACCESSDENIED if the VM process is not available or not
5983 * currently handling queries and the setting should then be done in
5984 * VBoxSVC.
5985 */
5986HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5987 const com::Utf8Str &aFlags, bool fDelete)
5988{
5989 HRESULT rc;
5990
5991 try
5992 {
5993 ComPtr<IInternalSessionControl> directControl;
5994 {
5995 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5996 if (mData->mSession.mLockType == LockType_VM)
5997 directControl = mData->mSession.mDirectControl;
5998 }
5999
6000 Bstr dummy1; /* will not be changed (setter) */
6001 Bstr dummy2; /* will not be changed (setter) */
6002 LONG64 dummy64;
6003 if (!directControl)
6004 rc = E_ACCESSDENIED;
6005 else
6006 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6007 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
6008 fDelete ? 2 : 1 /* accessMode */,
6009 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
6010 }
6011 catch (std::bad_alloc &)
6012 {
6013 rc = E_OUTOFMEMORY;
6014 }
6015
6016 return rc;
6017}
6018#endif // VBOX_WITH_GUEST_PROPS
6019
6020HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
6021 const com::Utf8Str &aFlags)
6022{
6023#ifndef VBOX_WITH_GUEST_PROPS
6024 ReturnComNotImplemented();
6025#else // VBOX_WITH_GUEST_PROPS
6026
6027 int vrc = GuestPropValidateName(aProperty.c_str(), aProperty.length() + 1 /* '\0' */);
6028 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
6029
6030 vrc = GuestPropValidateValue(aValue.c_str(), aValue.length() + 1 /* '\0' */);
6031 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
6032
6033 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
6034 if (rc == E_ACCESSDENIED)
6035 /* The VM is not running or the service is not (yet) accessible */
6036 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
6037 return rc;
6038#endif // VBOX_WITH_GUEST_PROPS
6039}
6040
6041HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
6042{
6043 return setGuestProperty(aProperty, aValue, "");
6044}
6045
6046HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
6047{
6048#ifndef VBOX_WITH_GUEST_PROPS
6049 ReturnComNotImplemented();
6050#else // VBOX_WITH_GUEST_PROPS
6051 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
6052 if (rc == E_ACCESSDENIED)
6053 /* The VM is not running or the service is not (yet) accessible */
6054 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
6055 return rc;
6056#endif // VBOX_WITH_GUEST_PROPS
6057}
6058
6059#ifdef VBOX_WITH_GUEST_PROPS
6060/**
6061 * Enumerate the guest properties in VBoxSVC's internal structures.
6062 */
6063HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
6064 std::vector<com::Utf8Str> &aNames,
6065 std::vector<com::Utf8Str> &aValues,
6066 std::vector<LONG64> &aTimestamps,
6067 std::vector<com::Utf8Str> &aFlags)
6068{
6069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6070 Utf8Str strPatterns(aPatterns);
6071
6072 /*
6073 * Look for matching patterns and build up a list.
6074 */
6075 HWData::GuestPropertyMap propMap;
6076 for (HWData::GuestPropertyMap::const_iterator
6077 it = mHWData->mGuestProperties.begin();
6078 it != mHWData->mGuestProperties.end();
6079 ++it)
6080 {
6081 if ( strPatterns.isEmpty()
6082 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6083 RTSTR_MAX,
6084 it->first.c_str(),
6085 RTSTR_MAX,
6086 NULL)
6087 )
6088 propMap.insert(*it);
6089 }
6090
6091 alock.release();
6092
6093 /*
6094 * And build up the arrays for returning the property information.
6095 */
6096 size_t cEntries = propMap.size();
6097
6098 aNames.resize(cEntries);
6099 aValues.resize(cEntries);
6100 aTimestamps.resize(cEntries);
6101 aFlags.resize(cEntries);
6102
6103 size_t i = 0;
6104 for (HWData::GuestPropertyMap::const_iterator
6105 it = propMap.begin();
6106 it != propMap.end();
6107 ++it, ++i)
6108 {
6109 aNames[i] = it->first;
6110 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
6111 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
6112
6113 aValues[i] = it->second.strValue;
6114 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
6115 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
6116
6117 aTimestamps[i] = it->second.mTimestamp;
6118
6119 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6120 GuestPropWriteFlags(it->second.mFlags, szFlags);
6121 aFlags[i] = szFlags;
6122 }
6123
6124 return S_OK;
6125}
6126
6127/**
6128 * Enumerate the properties managed by a VM.
6129 * @returns E_ACCESSDENIED if the VM process is not available or not
6130 * currently handling queries and the setting should then be done in
6131 * VBoxSVC.
6132 */
6133HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6134 std::vector<com::Utf8Str> &aNames,
6135 std::vector<com::Utf8Str> &aValues,
6136 std::vector<LONG64> &aTimestamps,
6137 std::vector<com::Utf8Str> &aFlags)
6138{
6139 HRESULT rc;
6140 ComPtr<IInternalSessionControl> directControl;
6141 {
6142 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6143 if (mData->mSession.mLockType == LockType_VM)
6144 directControl = mData->mSession.mDirectControl;
6145 }
6146
6147 com::SafeArray<BSTR> bNames;
6148 com::SafeArray<BSTR> bValues;
6149 com::SafeArray<LONG64> bTimestamps;
6150 com::SafeArray<BSTR> bFlags;
6151
6152 if (!directControl)
6153 rc = E_ACCESSDENIED;
6154 else
6155 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6156 ComSafeArrayAsOutParam(bNames),
6157 ComSafeArrayAsOutParam(bValues),
6158 ComSafeArrayAsOutParam(bTimestamps),
6159 ComSafeArrayAsOutParam(bFlags));
6160 size_t i;
6161 aNames.resize(bNames.size());
6162 for (i = 0; i < bNames.size(); ++i)
6163 aNames[i] = Utf8Str(bNames[i]);
6164 aValues.resize(bValues.size());
6165 for (i = 0; i < bValues.size(); ++i)
6166 aValues[i] = Utf8Str(bValues[i]);
6167 aTimestamps.resize(bTimestamps.size());
6168 for (i = 0; i < bTimestamps.size(); ++i)
6169 aTimestamps[i] = bTimestamps[i];
6170 aFlags.resize(bFlags.size());
6171 for (i = 0; i < bFlags.size(); ++i)
6172 aFlags[i] = Utf8Str(bFlags[i]);
6173
6174 return rc;
6175}
6176#endif // VBOX_WITH_GUEST_PROPS
6177HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6178 std::vector<com::Utf8Str> &aNames,
6179 std::vector<com::Utf8Str> &aValues,
6180 std::vector<LONG64> &aTimestamps,
6181 std::vector<com::Utf8Str> &aFlags)
6182{
6183#ifndef VBOX_WITH_GUEST_PROPS
6184 ReturnComNotImplemented();
6185#else // VBOX_WITH_GUEST_PROPS
6186
6187 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6188
6189 if (rc == E_ACCESSDENIED)
6190 /* The VM is not running or the service is not (yet) accessible */
6191 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6192 return rc;
6193#endif // VBOX_WITH_GUEST_PROPS
6194}
6195
6196HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6197 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6198{
6199 MediumAttachmentList atts;
6200
6201 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6202 if (FAILED(rc)) return rc;
6203
6204 aMediumAttachments.resize(atts.size());
6205 size_t i = 0;
6206 for (MediumAttachmentList::const_iterator
6207 it = atts.begin();
6208 it != atts.end();
6209 ++it, ++i)
6210 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6211
6212 return S_OK;
6213}
6214
6215HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6216 LONG aControllerPort,
6217 LONG aDevice,
6218 ComPtr<IMediumAttachment> &aAttachment)
6219{
6220 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6221 aName.c_str(), aControllerPort, aDevice));
6222
6223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6224
6225 aAttachment = NULL;
6226
6227 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6228 aName,
6229 aControllerPort,
6230 aDevice);
6231 if (pAttach.isNull())
6232 return setError(VBOX_E_OBJECT_NOT_FOUND,
6233 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6234 aDevice, aControllerPort, aName.c_str());
6235
6236 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6237
6238 return S_OK;
6239}
6240
6241
6242HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6243 StorageBus_T aConnectionType,
6244 ComPtr<IStorageController> &aController)
6245{
6246 if ( (aConnectionType <= StorageBus_Null)
6247 || (aConnectionType > StorageBus_VirtioSCSI))
6248 return setError(E_INVALIDARG,
6249 tr("Invalid connection type: %d"),
6250 aConnectionType);
6251
6252 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6253
6254 HRESULT rc = i_checkStateDependency(MutableStateDep);
6255 if (FAILED(rc)) return rc;
6256
6257 /* try to find one with the name first. */
6258 ComObjPtr<StorageController> ctrl;
6259
6260 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6261 if (SUCCEEDED(rc))
6262 return setError(VBOX_E_OBJECT_IN_USE,
6263 tr("Storage controller named '%s' already exists"),
6264 aName.c_str());
6265
6266 ctrl.createObject();
6267
6268 /* get a new instance number for the storage controller */
6269 ULONG ulInstance = 0;
6270 bool fBootable = true;
6271 for (StorageControllerList::const_iterator
6272 it = mStorageControllers->begin();
6273 it != mStorageControllers->end();
6274 ++it)
6275 {
6276 if ((*it)->i_getStorageBus() == aConnectionType)
6277 {
6278 ULONG ulCurInst = (*it)->i_getInstance();
6279
6280 if (ulCurInst >= ulInstance)
6281 ulInstance = ulCurInst + 1;
6282
6283 /* Only one controller of each type can be marked as bootable. */
6284 if ((*it)->i_getBootable())
6285 fBootable = false;
6286 }
6287 }
6288
6289 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6290 if (FAILED(rc)) return rc;
6291
6292 i_setModified(IsModified_Storage);
6293 mStorageControllers.backup();
6294 mStorageControllers->push_back(ctrl);
6295
6296 ctrl.queryInterfaceTo(aController.asOutParam());
6297
6298 /* inform the direct session if any */
6299 alock.release();
6300 i_onStorageControllerChange(i_getId(), aName);
6301
6302 return S_OK;
6303}
6304
6305HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6306 ComPtr<IStorageController> &aStorageController)
6307{
6308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6309
6310 ComObjPtr<StorageController> ctrl;
6311
6312 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6313 if (SUCCEEDED(rc))
6314 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6315
6316 return rc;
6317}
6318
6319HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6320 ULONG aInstance,
6321 ComPtr<IStorageController> &aStorageController)
6322{
6323 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6324
6325 for (StorageControllerList::const_iterator
6326 it = mStorageControllers->begin();
6327 it != mStorageControllers->end();
6328 ++it)
6329 {
6330 if ( (*it)->i_getStorageBus() == aConnectionType
6331 && (*it)->i_getInstance() == aInstance)
6332 {
6333 (*it).queryInterfaceTo(aStorageController.asOutParam());
6334 return S_OK;
6335 }
6336 }
6337
6338 return setError(VBOX_E_OBJECT_NOT_FOUND,
6339 tr("Could not find a storage controller with instance number '%lu'"),
6340 aInstance);
6341}
6342
6343HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6344{
6345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6346
6347 HRESULT rc = i_checkStateDependency(MutableStateDep);
6348 if (FAILED(rc)) return rc;
6349
6350 ComObjPtr<StorageController> ctrl;
6351
6352 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6353 if (SUCCEEDED(rc))
6354 {
6355 /* Ensure that only one controller of each type is marked as bootable. */
6356 if (aBootable == TRUE)
6357 {
6358 for (StorageControllerList::const_iterator
6359 it = mStorageControllers->begin();
6360 it != mStorageControllers->end();
6361 ++it)
6362 {
6363 ComObjPtr<StorageController> aCtrl = (*it);
6364
6365 if ( (aCtrl->i_getName() != aName)
6366 && aCtrl->i_getBootable() == TRUE
6367 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6368 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6369 {
6370 aCtrl->i_setBootable(FALSE);
6371 break;
6372 }
6373 }
6374 }
6375
6376 if (SUCCEEDED(rc))
6377 {
6378 ctrl->i_setBootable(aBootable);
6379 i_setModified(IsModified_Storage);
6380 }
6381 }
6382
6383 if (SUCCEEDED(rc))
6384 {
6385 /* inform the direct session if any */
6386 alock.release();
6387 i_onStorageControllerChange(i_getId(), aName);
6388 }
6389
6390 return rc;
6391}
6392
6393HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6394{
6395 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6396
6397 HRESULT rc = i_checkStateDependency(MutableStateDep);
6398 if (FAILED(rc)) return rc;
6399
6400 ComObjPtr<StorageController> ctrl;
6401 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6402 if (FAILED(rc)) return rc;
6403
6404 MediumAttachmentList llDetachedAttachments;
6405 {
6406 /* find all attached devices to the appropriate storage controller and detach them all */
6407 // make a temporary list because detachDevice invalidates iterators into
6408 // mMediumAttachments
6409 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6410
6411 for (MediumAttachmentList::const_iterator
6412 it = llAttachments2.begin();
6413 it != llAttachments2.end();
6414 ++it)
6415 {
6416 MediumAttachment *pAttachTemp = *it;
6417
6418 AutoCaller localAutoCaller(pAttachTemp);
6419 if (FAILED(localAutoCaller.hrc())) return localAutoCaller.hrc();
6420
6421 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6422
6423 if (pAttachTemp->i_getControllerName() == aName)
6424 {
6425 llDetachedAttachments.push_back(pAttachTemp);
6426 rc = i_detachDevice(pAttachTemp, alock, NULL);
6427 if (FAILED(rc)) return rc;
6428 }
6429 }
6430 }
6431
6432 /* send event about detached devices before removing parent controller */
6433 for (MediumAttachmentList::const_iterator
6434 it = llDetachedAttachments.begin();
6435 it != llDetachedAttachments.end();
6436 ++it)
6437 {
6438 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6439 }
6440
6441 /* We can remove it now. */
6442 i_setModified(IsModified_Storage);
6443 mStorageControllers.backup();
6444
6445 ctrl->i_unshare();
6446
6447 mStorageControllers->remove(ctrl);
6448
6449 /* inform the direct session if any */
6450 alock.release();
6451 i_onStorageControllerChange(i_getId(), aName);
6452
6453 return S_OK;
6454}
6455
6456HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6457 ComPtr<IUSBController> &aController)
6458{
6459 if ( (aType <= USBControllerType_Null)
6460 || (aType >= USBControllerType_Last))
6461 return setError(E_INVALIDARG,
6462 tr("Invalid USB controller type: %d"),
6463 aType);
6464
6465 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6466
6467 HRESULT rc = i_checkStateDependency(MutableStateDep);
6468 if (FAILED(rc)) return rc;
6469
6470 /* try to find one with the same type first. */
6471 ComObjPtr<USBController> ctrl;
6472
6473 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6474 if (SUCCEEDED(rc))
6475 return setError(VBOX_E_OBJECT_IN_USE,
6476 tr("USB controller named '%s' already exists"),
6477 aName.c_str());
6478
6479 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6480 ULONG maxInstances;
6481 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6482 if (FAILED(rc))
6483 return rc;
6484
6485 ULONG cInstances = i_getUSBControllerCountByType(aType);
6486 if (cInstances >= maxInstances)
6487 return setError(E_INVALIDARG,
6488 tr("Too many USB controllers of this type"));
6489
6490 ctrl.createObject();
6491
6492 rc = ctrl->init(this, aName, aType);
6493 if (FAILED(rc)) return rc;
6494
6495 i_setModified(IsModified_USB);
6496 mUSBControllers.backup();
6497 mUSBControllers->push_back(ctrl);
6498
6499 ctrl.queryInterfaceTo(aController.asOutParam());
6500
6501 /* inform the direct session if any */
6502 alock.release();
6503 i_onUSBControllerChange();
6504
6505 return S_OK;
6506}
6507
6508HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6509{
6510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6511
6512 ComObjPtr<USBController> ctrl;
6513
6514 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6515 if (SUCCEEDED(rc))
6516 ctrl.queryInterfaceTo(aController.asOutParam());
6517
6518 return rc;
6519}
6520
6521HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6522 ULONG *aControllers)
6523{
6524 if ( (aType <= USBControllerType_Null)
6525 || (aType >= USBControllerType_Last))
6526 return setError(E_INVALIDARG,
6527 tr("Invalid USB controller type: %d"),
6528 aType);
6529
6530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6531
6532 ComObjPtr<USBController> ctrl;
6533
6534 *aControllers = i_getUSBControllerCountByType(aType);
6535
6536 return S_OK;
6537}
6538
6539HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6540{
6541
6542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6543
6544 HRESULT rc = i_checkStateDependency(MutableStateDep);
6545 if (FAILED(rc)) return rc;
6546
6547 ComObjPtr<USBController> ctrl;
6548 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6549 if (FAILED(rc)) return rc;
6550
6551 i_setModified(IsModified_USB);
6552 mUSBControllers.backup();
6553
6554 ctrl->i_unshare();
6555
6556 mUSBControllers->remove(ctrl);
6557
6558 /* inform the direct session if any */
6559 alock.release();
6560 i_onUSBControllerChange();
6561
6562 return S_OK;
6563}
6564
6565HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6566 ULONG *aOriginX,
6567 ULONG *aOriginY,
6568 ULONG *aWidth,
6569 ULONG *aHeight,
6570 BOOL *aEnabled)
6571{
6572 uint32_t u32OriginX= 0;
6573 uint32_t u32OriginY= 0;
6574 uint32_t u32Width = 0;
6575 uint32_t u32Height = 0;
6576 uint16_t u16Flags = 0;
6577
6578#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6579 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6580#else
6581 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6582#endif
6583 int vrc = readSavedGuestScreenInfo(SavedStateStream, mSSData->strStateFilePath, aScreenId,
6584 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6585 if (RT_FAILURE(vrc))
6586 {
6587#ifdef RT_OS_WINDOWS
6588 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6589 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6590 * So just assign fEnable to TRUE again.
6591 * The right fix would be to change GUI API wrappers to make sure that parameters
6592 * are changed only if API succeeds.
6593 */
6594 *aEnabled = TRUE;
6595#endif
6596 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6597 tr("Saved guest size is not available (%Rrc)"),
6598 vrc);
6599 }
6600
6601 *aOriginX = u32OriginX;
6602 *aOriginY = u32OriginY;
6603 *aWidth = u32Width;
6604 *aHeight = u32Height;
6605 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6606
6607 return S_OK;
6608}
6609
6610HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6611 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6612{
6613 if (aScreenId != 0)
6614 return E_NOTIMPL;
6615
6616 if ( aBitmapFormat != BitmapFormat_BGR0
6617 && aBitmapFormat != BitmapFormat_BGRA
6618 && aBitmapFormat != BitmapFormat_RGBA
6619 && aBitmapFormat != BitmapFormat_PNG)
6620 return setError(E_NOTIMPL,
6621 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6622
6623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6624
6625 uint8_t *pu8Data = NULL;
6626 uint32_t cbData = 0;
6627 uint32_t u32Width = 0;
6628 uint32_t u32Height = 0;
6629
6630#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6631 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6632#else
6633 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6634#endif
6635 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 0 /* u32Type */,
6636 &pu8Data, &cbData, &u32Width, &u32Height);
6637 if (RT_FAILURE(vrc))
6638 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6639 tr("Saved thumbnail data is not available (%Rrc)"),
6640 vrc);
6641
6642 HRESULT hr = S_OK;
6643
6644 *aWidth = u32Width;
6645 *aHeight = u32Height;
6646
6647 if (cbData > 0)
6648 {
6649 /* Convert pixels to the format expected by the API caller. */
6650 if (aBitmapFormat == BitmapFormat_BGR0)
6651 {
6652 /* [0] B, [1] G, [2] R, [3] 0. */
6653 aData.resize(cbData);
6654 memcpy(&aData.front(), pu8Data, cbData);
6655 }
6656 else if (aBitmapFormat == BitmapFormat_BGRA)
6657 {
6658 /* [0] B, [1] G, [2] R, [3] A. */
6659 aData.resize(cbData);
6660 for (uint32_t i = 0; i < cbData; i += 4)
6661 {
6662 aData[i] = pu8Data[i];
6663 aData[i + 1] = pu8Data[i + 1];
6664 aData[i + 2] = pu8Data[i + 2];
6665 aData[i + 3] = 0xff;
6666 }
6667 }
6668 else if (aBitmapFormat == BitmapFormat_RGBA)
6669 {
6670 /* [0] R, [1] G, [2] B, [3] A. */
6671 aData.resize(cbData);
6672 for (uint32_t i = 0; i < cbData; i += 4)
6673 {
6674 aData[i] = pu8Data[i + 2];
6675 aData[i + 1] = pu8Data[i + 1];
6676 aData[i + 2] = pu8Data[i];
6677 aData[i + 3] = 0xff;
6678 }
6679 }
6680 else if (aBitmapFormat == BitmapFormat_PNG)
6681 {
6682 uint8_t *pu8PNG = NULL;
6683 uint32_t cbPNG = 0;
6684 uint32_t cxPNG = 0;
6685 uint32_t cyPNG = 0;
6686
6687 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6688
6689 if (RT_SUCCESS(vrc))
6690 {
6691 aData.resize(cbPNG);
6692 if (cbPNG)
6693 memcpy(&aData.front(), pu8PNG, cbPNG);
6694 }
6695 else
6696 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6697 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6698 vrc);
6699
6700 RTMemFree(pu8PNG);
6701 }
6702 }
6703
6704 freeSavedDisplayScreenshot(pu8Data);
6705
6706 return hr;
6707}
6708
6709HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6710 ULONG *aWidth,
6711 ULONG *aHeight,
6712 std::vector<BitmapFormat_T> &aBitmapFormats)
6713{
6714 if (aScreenId != 0)
6715 return E_NOTIMPL;
6716
6717 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6718
6719 uint8_t *pu8Data = NULL;
6720 uint32_t cbData = 0;
6721 uint32_t u32Width = 0;
6722 uint32_t u32Height = 0;
6723
6724#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6725 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6726#else
6727 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6728#endif
6729 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6730 &pu8Data, &cbData, &u32Width, &u32Height);
6731
6732 if (RT_FAILURE(vrc))
6733 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6734 tr("Saved screenshot data is not available (%Rrc)"),
6735 vrc);
6736
6737 *aWidth = u32Width;
6738 *aHeight = u32Height;
6739 aBitmapFormats.resize(1);
6740 aBitmapFormats[0] = BitmapFormat_PNG;
6741
6742 freeSavedDisplayScreenshot(pu8Data);
6743
6744 return S_OK;
6745}
6746
6747HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6748 BitmapFormat_T aBitmapFormat,
6749 ULONG *aWidth,
6750 ULONG *aHeight,
6751 std::vector<BYTE> &aData)
6752{
6753 if (aScreenId != 0)
6754 return E_NOTIMPL;
6755
6756 if (aBitmapFormat != BitmapFormat_PNG)
6757 return E_NOTIMPL;
6758
6759 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6760
6761 uint8_t *pu8Data = NULL;
6762 uint32_t cbData = 0;
6763 uint32_t u32Width = 0;
6764 uint32_t u32Height = 0;
6765
6766#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6767 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6768#else
6769 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6770#endif
6771 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6772 &pu8Data, &cbData, &u32Width, &u32Height);
6773
6774 if (RT_FAILURE(vrc))
6775 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6776 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6777 vrc);
6778
6779 *aWidth = u32Width;
6780 *aHeight = u32Height;
6781
6782 aData.resize(cbData);
6783 if (cbData)
6784 memcpy(&aData.front(), pu8Data, cbData);
6785
6786 freeSavedDisplayScreenshot(pu8Data);
6787
6788 return S_OK;
6789}
6790
6791HRESULT Machine::hotPlugCPU(ULONG aCpu)
6792{
6793 HRESULT rc = S_OK;
6794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6795
6796 if (!mHWData->mCPUHotPlugEnabled)
6797 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6798
6799 if (aCpu >= mHWData->mCPUCount)
6800 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6801
6802 if (mHWData->mCPUAttached[aCpu])
6803 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6804
6805 rc = i_checkStateDependency(MutableOrRunningStateDep);
6806 if (FAILED(rc)) return rc;
6807
6808 alock.release();
6809 rc = i_onCPUChange(aCpu, false);
6810 alock.acquire();
6811 if (FAILED(rc)) return rc;
6812
6813 i_setModified(IsModified_MachineData);
6814 mHWData.backup();
6815 mHWData->mCPUAttached[aCpu] = true;
6816
6817 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6818 if (Global::IsOnline(mData->mMachineState))
6819 i_saveSettings(NULL, alock);
6820
6821 return S_OK;
6822}
6823
6824HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6825{
6826 HRESULT rc = S_OK;
6827
6828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6829
6830 if (!mHWData->mCPUHotPlugEnabled)
6831 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6832
6833 if (aCpu >= SchemaDefs::MaxCPUCount)
6834 return setError(E_INVALIDARG,
6835 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6836 SchemaDefs::MaxCPUCount);
6837
6838 if (!mHWData->mCPUAttached[aCpu])
6839 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6840
6841 /* CPU 0 can't be detached */
6842 if (aCpu == 0)
6843 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6844
6845 rc = i_checkStateDependency(MutableOrRunningStateDep);
6846 if (FAILED(rc)) return rc;
6847
6848 alock.release();
6849 rc = i_onCPUChange(aCpu, true);
6850 alock.acquire();
6851 if (FAILED(rc)) return rc;
6852
6853 i_setModified(IsModified_MachineData);
6854 mHWData.backup();
6855 mHWData->mCPUAttached[aCpu] = false;
6856
6857 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6858 if (Global::IsOnline(mData->mMachineState))
6859 i_saveSettings(NULL, alock);
6860
6861 return S_OK;
6862}
6863
6864HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6865{
6866 *aAttached = false;
6867
6868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6869
6870 /* If hotplug is enabled the CPU is always enabled. */
6871 if (!mHWData->mCPUHotPlugEnabled)
6872 {
6873 if (aCpu < mHWData->mCPUCount)
6874 *aAttached = true;
6875 }
6876 else
6877 {
6878 if (aCpu < SchemaDefs::MaxCPUCount)
6879 *aAttached = mHWData->mCPUAttached[aCpu];
6880 }
6881
6882 return S_OK;
6883}
6884
6885HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6886{
6887 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6888
6889 Utf8Str log = i_getLogFilename(aIdx);
6890 if (!RTFileExists(log.c_str()))
6891 log.setNull();
6892 aFilename = log;
6893
6894 return S_OK;
6895}
6896
6897HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6898{
6899 if (aSize < 0)
6900 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6901
6902 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6903
6904 HRESULT rc = S_OK;
6905 Utf8Str log = i_getLogFilename(aIdx);
6906
6907 /* do not unnecessarily hold the lock while doing something which does
6908 * not need the lock and potentially takes a long time. */
6909 alock.release();
6910
6911 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6912 * keeps the SOAP reply size under 1M for the webservice (we're using
6913 * base64 encoded strings for binary data for years now, avoiding the
6914 * expansion of each byte array element to approx. 25 bytes of XML. */
6915 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6916 aData.resize(cbData);
6917
6918 int vrc = VINF_SUCCESS;
6919 RTVFSIOSTREAM hVfsIosLog = NIL_RTVFSIOSTREAM;
6920
6921#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6922 if (mData->mstrLogKeyId.isNotEmpty() && mData->mstrLogKeyStore.isNotEmpty())
6923 {
6924 PCVBOXCRYPTOIF pCryptoIf = NULL;
6925 rc = i_getVirtualBox()->i_retainCryptoIf(&pCryptoIf);
6926 if (SUCCEEDED(rc))
6927 {
6928 alock.acquire();
6929
6930 SecretKey *pKey = NULL;
6931 vrc = mData->mpKeyStore->retainSecretKey(mData->mstrLogKeyId, &pKey);
6932 alock.release();
6933
6934 if (RT_SUCCESS(vrc))
6935 {
6936 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6937 if (RT_SUCCESS(vrc))
6938 {
6939 RTVFSIOSTREAM hVfsIosLogDec = NIL_RTVFSIOSTREAM;
6940 vrc = pCryptoIf->pfnCryptoIoStrmFromVfsIoStrmDecrypt(hVfsIosLog, mData->mstrLogKeyStore.c_str(),
6941 (const char *)pKey->getKeyBuffer(), &hVfsIosLogDec);
6942 if (RT_SUCCESS(vrc))
6943 {
6944 RTVfsIoStrmRelease(hVfsIosLog);
6945 hVfsIosLog = hVfsIosLogDec;
6946 }
6947 }
6948
6949 pKey->release();
6950 }
6951
6952 i_getVirtualBox()->i_releaseCryptoIf(pCryptoIf);
6953 }
6954 }
6955 else
6956 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6957#else
6958 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6959#endif
6960 if (RT_SUCCESS(vrc))
6961 {
6962 vrc = RTVfsIoStrmReadAt(hVfsIosLog, aOffset,
6963 cbData ? &aData.front() : NULL, cbData,
6964 true /*fBlocking*/, &cbData);
6965 if (RT_SUCCESS(vrc))
6966 aData.resize(cbData);
6967 else
6968 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6969 tr("Could not read log file '%s' (%Rrc)"),
6970 log.c_str(), vrc);
6971
6972 RTVfsIoStrmRelease(hVfsIosLog);
6973 }
6974 else
6975 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6976 tr("Could not open log file '%s' (%Rrc)"),
6977 log.c_str(), vrc);
6978
6979 if (FAILED(rc))
6980 aData.resize(0);
6981
6982 return rc;
6983}
6984
6985
6986/**
6987 * Currently this method doesn't attach device to the running VM,
6988 * just makes sure it's plugged on next VM start.
6989 */
6990HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6991{
6992 // lock scope
6993 {
6994 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6995
6996 HRESULT rc = i_checkStateDependency(MutableStateDep);
6997 if (FAILED(rc)) return rc;
6998
6999 ChipsetType_T aChipset = ChipsetType_PIIX3;
7000 COMGETTER(ChipsetType)(&aChipset);
7001
7002 if (aChipset != ChipsetType_ICH9)
7003 {
7004 return setError(E_INVALIDARG,
7005 tr("Host PCI attachment only supported with ICH9 chipset"));
7006 }
7007
7008 // check if device with this host PCI address already attached
7009 for (HWData::PCIDeviceAssignmentList::const_iterator
7010 it = mHWData->mPCIDeviceAssignments.begin();
7011 it != mHWData->mPCIDeviceAssignments.end();
7012 ++it)
7013 {
7014 LONG iHostAddress = -1;
7015 ComPtr<PCIDeviceAttachment> pAttach;
7016 pAttach = *it;
7017 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7018 if (iHostAddress == aHostAddress)
7019 return setError(E_INVALIDARG,
7020 tr("Device with host PCI address already attached to this VM"));
7021 }
7022
7023 ComObjPtr<PCIDeviceAttachment> pda;
7024 char name[32];
7025
7026 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
7027 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
7028 pda.createObject();
7029 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
7030 i_setModified(IsModified_MachineData);
7031 mHWData.backup();
7032 mHWData->mPCIDeviceAssignments.push_back(pda);
7033 }
7034
7035 return S_OK;
7036}
7037
7038/**
7039 * Currently this method doesn't detach device from the running VM,
7040 * just makes sure it's not plugged on next VM start.
7041 */
7042HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
7043{
7044 ComObjPtr<PCIDeviceAttachment> pAttach;
7045 bool fRemoved = false;
7046 HRESULT rc;
7047
7048 // lock scope
7049 {
7050 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7051
7052 rc = i_checkStateDependency(MutableStateDep);
7053 if (FAILED(rc)) return rc;
7054
7055 for (HWData::PCIDeviceAssignmentList::const_iterator
7056 it = mHWData->mPCIDeviceAssignments.begin();
7057 it != mHWData->mPCIDeviceAssignments.end();
7058 ++it)
7059 {
7060 LONG iHostAddress = -1;
7061 pAttach = *it;
7062 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7063 if (iHostAddress != -1 && iHostAddress == aHostAddress)
7064 {
7065 i_setModified(IsModified_MachineData);
7066 mHWData.backup();
7067 mHWData->mPCIDeviceAssignments.remove(pAttach);
7068 fRemoved = true;
7069 break;
7070 }
7071 }
7072 }
7073
7074
7075 /* Fire event outside of the lock */
7076 if (fRemoved)
7077 {
7078 Assert(!pAttach.isNull());
7079 ComPtr<IEventSource> es;
7080 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7081 Assert(SUCCEEDED(rc));
7082 Bstr mid;
7083 rc = this->COMGETTER(Id)(mid.asOutParam());
7084 Assert(SUCCEEDED(rc));
7085 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7086 }
7087
7088 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7089 tr("No host PCI device %08x attached"),
7090 aHostAddress
7091 );
7092}
7093
7094HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
7095{
7096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7097
7098 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
7099 size_t i = 0;
7100 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
7101 it = mHWData->mPCIDeviceAssignments.begin();
7102 it != mHWData->mPCIDeviceAssignments.end();
7103 ++it, ++i)
7104 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
7105
7106 return S_OK;
7107}
7108
7109HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
7110{
7111 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
7112
7113 return S_OK;
7114}
7115
7116HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
7117{
7118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7119
7120 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
7121
7122 return S_OK;
7123}
7124
7125HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
7126{
7127 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7128 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7129 if (SUCCEEDED(hrc))
7130 {
7131 hrc = mHWData.backupEx();
7132 if (SUCCEEDED(hrc))
7133 {
7134 i_setModified(IsModified_MachineData);
7135 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
7136 }
7137 }
7138 return hrc;
7139}
7140
7141HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
7142{
7143 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7144 aTracingConfig = mHWData->mDebugging.strTracingConfig;
7145 return S_OK;
7146}
7147
7148HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
7149{
7150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7151 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7152 if (SUCCEEDED(hrc))
7153 {
7154 hrc = mHWData.backupEx();
7155 if (SUCCEEDED(hrc))
7156 {
7157 mHWData->mDebugging.strTracingConfig = aTracingConfig;
7158 if (SUCCEEDED(hrc))
7159 i_setModified(IsModified_MachineData);
7160 }
7161 }
7162 return hrc;
7163}
7164
7165HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
7166{
7167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7168
7169 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
7170
7171 return S_OK;
7172}
7173
7174HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
7175{
7176 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7177 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7178 if (SUCCEEDED(hrc))
7179 {
7180 hrc = mHWData.backupEx();
7181 if (SUCCEEDED(hrc))
7182 {
7183 i_setModified(IsModified_MachineData);
7184 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
7185 }
7186 }
7187 return hrc;
7188}
7189
7190HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7191{
7192 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7193
7194 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7195
7196 return S_OK;
7197}
7198
7199HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7200{
7201 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7202
7203 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7204 if ( SUCCEEDED(hrc)
7205 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7206 {
7207 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7208 int vrc;
7209
7210 if (aAutostartEnabled)
7211 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7212 else
7213 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7214
7215 if (RT_SUCCESS(vrc))
7216 {
7217 hrc = mHWData.backupEx();
7218 if (SUCCEEDED(hrc))
7219 {
7220 i_setModified(IsModified_MachineData);
7221 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7222 }
7223 }
7224 else if (vrc == VERR_NOT_SUPPORTED)
7225 hrc = setError(VBOX_E_NOT_SUPPORTED,
7226 tr("The VM autostart feature is not supported on this platform"));
7227 else if (vrc == VERR_PATH_NOT_FOUND)
7228 hrc = setError(E_FAIL,
7229 tr("The path to the autostart database is not set"));
7230 else
7231 hrc = setError(E_UNEXPECTED,
7232 aAutostartEnabled ?
7233 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
7234 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
7235 mUserData->s.strName.c_str(), vrc);
7236 }
7237 return hrc;
7238}
7239
7240HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7241{
7242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7243
7244 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7245
7246 return S_OK;
7247}
7248
7249HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7250{
7251 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7252 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7253 if (SUCCEEDED(hrc))
7254 {
7255 hrc = mHWData.backupEx();
7256 if (SUCCEEDED(hrc))
7257 {
7258 i_setModified(IsModified_MachineData);
7259 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7260 }
7261 }
7262 return hrc;
7263}
7264
7265HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7266{
7267 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7268
7269 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7270
7271 return S_OK;
7272}
7273
7274HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7275{
7276 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7277 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7278 if ( SUCCEEDED(hrc)
7279 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7280 {
7281 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7282 int vrc;
7283
7284 if (aAutostopType != AutostopType_Disabled)
7285 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7286 else
7287 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7288
7289 if (RT_SUCCESS(vrc))
7290 {
7291 hrc = mHWData.backupEx();
7292 if (SUCCEEDED(hrc))
7293 {
7294 i_setModified(IsModified_MachineData);
7295 mHWData->mAutostart.enmAutostopType = aAutostopType;
7296 }
7297 }
7298 else if (vrc == VERR_NOT_SUPPORTED)
7299 hrc = setError(VBOX_E_NOT_SUPPORTED,
7300 tr("The VM autostop feature is not supported on this platform"));
7301 else if (vrc == VERR_PATH_NOT_FOUND)
7302 hrc = setError(E_FAIL,
7303 tr("The path to the autostart database is not set"));
7304 else
7305 hrc = setError(E_UNEXPECTED,
7306 aAutostopType != AutostopType_Disabled ?
7307 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
7308 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
7309 mUserData->s.strName.c_str(), vrc);
7310 }
7311 return hrc;
7312}
7313
7314HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7315{
7316 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7317
7318 aDefaultFrontend = mHWData->mDefaultFrontend;
7319
7320 return S_OK;
7321}
7322
7323HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7324{
7325 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7326 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7327 if (SUCCEEDED(hrc))
7328 {
7329 hrc = mHWData.backupEx();
7330 if (SUCCEEDED(hrc))
7331 {
7332 i_setModified(IsModified_MachineData);
7333 mHWData->mDefaultFrontend = aDefaultFrontend;
7334 }
7335 }
7336 return hrc;
7337}
7338
7339HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7340{
7341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7342 size_t cbIcon = mUserData->s.ovIcon.size();
7343 aIcon.resize(cbIcon);
7344 if (cbIcon)
7345 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7346 return S_OK;
7347}
7348
7349HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7350{
7351 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7352 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7353 if (SUCCEEDED(hrc))
7354 {
7355 i_setModified(IsModified_MachineData);
7356 mUserData.backup();
7357 size_t cbIcon = aIcon.size();
7358 mUserData->s.ovIcon.resize(cbIcon);
7359 if (cbIcon)
7360 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7361 }
7362 return hrc;
7363}
7364
7365HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7366{
7367#ifdef VBOX_WITH_USB
7368 *aUSBProxyAvailable = true;
7369#else
7370 *aUSBProxyAvailable = false;
7371#endif
7372 return S_OK;
7373}
7374
7375HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
7376{
7377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7378
7379 *aVMProcessPriority = mUserData->s.enmVMPriority;
7380
7381 return S_OK;
7382}
7383
7384HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
7385{
7386 RT_NOREF(aVMProcessPriority);
7387 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7388 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7389 if (SUCCEEDED(hrc))
7390 {
7391 hrc = mUserData.backupEx();
7392 if (SUCCEEDED(hrc))
7393 {
7394 i_setModified(IsModified_MachineData);
7395 mUserData->s.enmVMPriority = aVMProcessPriority;
7396 }
7397 }
7398 alock.release();
7399 if (SUCCEEDED(hrc))
7400 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
7401 return hrc;
7402}
7403
7404HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7405 ComPtr<IProgress> &aProgress)
7406{
7407 ComObjPtr<Progress> pP;
7408 Progress *ppP = pP;
7409 IProgress *iP = static_cast<IProgress *>(ppP);
7410 IProgress **pProgress = &iP;
7411
7412 IMachine *pTarget = aTarget;
7413
7414 /* Convert the options. */
7415 RTCList<CloneOptions_T> optList;
7416 if (aOptions.size())
7417 for (size_t i = 0; i < aOptions.size(); ++i)
7418 optList.append(aOptions[i]);
7419
7420 if (optList.contains(CloneOptions_Link))
7421 {
7422 if (!i_isSnapshotMachine())
7423 return setError(E_INVALIDARG,
7424 tr("Linked clone can only be created from a snapshot"));
7425 if (aMode != CloneMode_MachineState)
7426 return setError(E_INVALIDARG,
7427 tr("Linked clone can only be created for a single machine state"));
7428 }
7429 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7430
7431 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7432
7433 HRESULT rc = pWorker->start(pProgress);
7434
7435 pP = static_cast<Progress *>(*pProgress);
7436 pP.queryInterfaceTo(aProgress.asOutParam());
7437
7438 return rc;
7439
7440}
7441
7442HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7443 const com::Utf8Str &aType,
7444 ComPtr<IProgress> &aProgress)
7445{
7446 LogFlowThisFuncEnter();
7447
7448 ComObjPtr<Progress> ptrProgress;
7449 HRESULT hrc = ptrProgress.createObject();
7450 if (SUCCEEDED(hrc))
7451 {
7452 com::Utf8Str strDefaultPath;
7453 if (aTargetPath.isEmpty())
7454 i_calculateFullPath(".", strDefaultPath);
7455
7456 /* Initialize our worker task */
7457 MachineMoveVM *pTask = NULL;
7458 try
7459 {
7460 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7461 }
7462 catch (std::bad_alloc &)
7463 {
7464 return E_OUTOFMEMORY;
7465 }
7466
7467 hrc = pTask->init();//no exceptions are thrown
7468
7469 if (SUCCEEDED(hrc))
7470 {
7471 hrc = pTask->createThread();
7472 pTask = NULL; /* Consumed by createThread(). */
7473 if (SUCCEEDED(hrc))
7474 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7475 else
7476 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7477 }
7478 else
7479 delete pTask;
7480 }
7481
7482 LogFlowThisFuncLeave();
7483 return hrc;
7484
7485}
7486
7487HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7488{
7489 NOREF(aProgress);
7490 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7491
7492 // This check should always fail.
7493 HRESULT rc = i_checkStateDependency(MutableStateDep);
7494 if (FAILED(rc)) return rc;
7495
7496 AssertFailedReturn(E_NOTIMPL);
7497}
7498
7499HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7500{
7501 NOREF(aSavedStateFile);
7502 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7503
7504 // This check should always fail.
7505 HRESULT rc = i_checkStateDependency(MutableStateDep);
7506 if (FAILED(rc)) return rc;
7507
7508 AssertFailedReturn(E_NOTIMPL);
7509}
7510
7511HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7512{
7513 NOREF(aFRemoveFile);
7514 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7515
7516 // This check should always fail.
7517 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7518 if (FAILED(rc)) return rc;
7519
7520 AssertFailedReturn(E_NOTIMPL);
7521}
7522
7523// public methods for internal purposes
7524/////////////////////////////////////////////////////////////////////////////
7525
7526/**
7527 * Adds the given IsModified_* flag to the dirty flags of the machine.
7528 * This must be called either during i_loadSettings or under the machine write lock.
7529 * @param fl Flag
7530 * @param fAllowStateModification If state modifications are allowed.
7531 */
7532void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7533{
7534 mData->flModifications |= fl;
7535 if (fAllowStateModification && i_isStateModificationAllowed())
7536 mData->mCurrentStateModified = true;
7537}
7538
7539/**
7540 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7541 * care of the write locking.
7542 *
7543 * @param fModification The flag to add.
7544 * @param fAllowStateModification If state modifications are allowed.
7545 */
7546void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7547{
7548 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7549 i_setModified(fModification, fAllowStateModification);
7550}
7551
7552/**
7553 * Saves the registry entry of this machine to the given configuration node.
7554 *
7555 * @param data Machine registry data.
7556 *
7557 * @note locks this object for reading.
7558 */
7559HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7560{
7561 AutoLimitedCaller autoCaller(this);
7562 AssertComRCReturnRC(autoCaller.hrc());
7563
7564 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7565
7566 data.uuid = mData->mUuid;
7567 data.strSettingsFile = mData->m_strConfigFile;
7568
7569 return S_OK;
7570}
7571
7572/**
7573 * Calculates the absolute path of the given path taking the directory of the
7574 * machine settings file as the current directory.
7575 *
7576 * @param strPath Path to calculate the absolute path for.
7577 * @param aResult Where to put the result (used only on success, can be the
7578 * same Utf8Str instance as passed in @a aPath).
7579 * @return IPRT result.
7580 *
7581 * @note Locks this object for reading.
7582 */
7583int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7584{
7585 AutoCaller autoCaller(this);
7586 AssertComRCReturn(autoCaller.hrc(), Global::vboxStatusCodeFromCOM(autoCaller.hrc()));
7587
7588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7589
7590 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7591
7592 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7593
7594 strSettingsDir.stripFilename();
7595 char szFolder[RTPATH_MAX];
7596 size_t cbFolder = sizeof(szFolder);
7597 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7598 if (RT_SUCCESS(vrc))
7599 aResult = szFolder;
7600
7601 return vrc;
7602}
7603
7604/**
7605 * Copies strSource to strTarget, making it relative to the machine folder
7606 * if it is a subdirectory thereof, or simply copying it otherwise.
7607 *
7608 * @param strSource Path to evaluate and copy.
7609 * @param strTarget Buffer to receive target path.
7610 *
7611 * @note Locks this object for reading.
7612 */
7613void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7614 Utf8Str &strTarget)
7615{
7616 AutoCaller autoCaller(this);
7617 AssertComRCReturn(autoCaller.hrc(), (void)0);
7618
7619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7620
7621 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7622 // use strTarget as a temporary buffer to hold the machine settings dir
7623 strTarget = mData->m_strConfigFileFull;
7624 strTarget.stripFilename();
7625 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7626 {
7627 // is relative: then append what's left
7628 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7629 // for empty paths (only possible for subdirs) use "." to avoid
7630 // triggering default settings for not present config attributes.
7631 if (strTarget.isEmpty())
7632 strTarget = ".";
7633 }
7634 else
7635 // is not relative: then overwrite
7636 strTarget = strSource;
7637}
7638
7639/**
7640 * Returns the full path to the machine's log folder in the
7641 * \a aLogFolder argument.
7642 */
7643void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7644{
7645 AutoCaller autoCaller(this);
7646 AssertComRCReturnVoid(autoCaller.hrc());
7647
7648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7649
7650 char szTmp[RTPATH_MAX];
7651 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7652 if (RT_SUCCESS(vrc))
7653 {
7654 if (szTmp[0] && !mUserData.isNull())
7655 {
7656 char szTmp2[RTPATH_MAX];
7657 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7658 if (RT_SUCCESS(vrc))
7659 aLogFolder.printf("%s%c%s",
7660 szTmp2,
7661 RTPATH_DELIMITER,
7662 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7663 }
7664 else
7665 vrc = VERR_PATH_IS_RELATIVE;
7666 }
7667
7668 if (RT_FAILURE(vrc))
7669 {
7670 // fallback if VBOX_USER_LOGHOME is not set or invalid
7671 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7672 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7673 aLogFolder.append(RTPATH_DELIMITER);
7674 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7675 }
7676}
7677
7678/**
7679 * Returns the full path to the machine's log file for an given index.
7680 */
7681Utf8Str Machine::i_getLogFilename(ULONG idx)
7682{
7683 Utf8Str logFolder;
7684 getLogFolder(logFolder);
7685 Assert(logFolder.length());
7686
7687 Utf8Str log;
7688 if (idx == 0)
7689 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7690#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7691 else if (idx == 1)
7692 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7693 else
7694 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7695#else
7696 else
7697 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7698#endif
7699 return log;
7700}
7701
7702/**
7703 * Returns the full path to the machine's hardened log file.
7704 */
7705Utf8Str Machine::i_getHardeningLogFilename(void)
7706{
7707 Utf8Str strFilename;
7708 getLogFolder(strFilename);
7709 Assert(strFilename.length());
7710 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7711 return strFilename;
7712}
7713
7714/**
7715 * Returns the default NVRAM filename based on the location of the VM config.
7716 * Note that this is a relative path.
7717 */
7718Utf8Str Machine::i_getDefaultNVRAMFilename()
7719{
7720 AutoCaller autoCaller(this);
7721 AssertComRCReturn(autoCaller.hrc(), Utf8Str::Empty);
7722
7723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7724
7725 if (i_isSnapshotMachine())
7726 return Utf8Str::Empty;
7727
7728 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7729 strNVRAMFilePath.stripPath();
7730 strNVRAMFilePath.stripSuffix();
7731 strNVRAMFilePath += ".nvram";
7732
7733 return strNVRAMFilePath;
7734}
7735
7736/**
7737 * Returns the NVRAM filename for a new snapshot. This intentionally works
7738 * similarly to the saved state file naming. Note that this is usually
7739 * a relative path, unless the snapshot folder is absolute.
7740 */
7741Utf8Str Machine::i_getSnapshotNVRAMFilename()
7742{
7743 AutoCaller autoCaller(this);
7744 AssertComRCReturn(autoCaller.hrc(), Utf8Str::Empty);
7745
7746 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7747
7748 RTTIMESPEC ts;
7749 RTTimeNow(&ts);
7750 RTTIME time;
7751 RTTimeExplode(&time, &ts);
7752
7753 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7754 strNVRAMFilePath += RTPATH_DELIMITER;
7755 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7756 time.i32Year, time.u8Month, time.u8MonthDay,
7757 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7758
7759 return strNVRAMFilePath;
7760}
7761
7762/**
7763 * Returns the version of the settings file.
7764 */
7765SettingsVersion_T Machine::i_getSettingsVersion(void)
7766{
7767 AutoCaller autoCaller(this);
7768 AssertComRCReturn(autoCaller.hrc(), SettingsVersion_Null);
7769
7770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7771
7772 return mData->pMachineConfigFile->getSettingsVersion();
7773}
7774
7775/**
7776 * Composes a unique saved state filename based on the current system time. The filename is
7777 * granular to the second so this will work so long as no more than one snapshot is taken on
7778 * a machine per second.
7779 *
7780 * Before version 4.1, we used this formula for saved state files:
7781 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7782 * which no longer works because saved state files can now be shared between the saved state of the
7783 * "saved" machine and an online snapshot, and the following would cause problems:
7784 * 1) save machine
7785 * 2) create online snapshot from that machine state --> reusing saved state file
7786 * 3) save machine again --> filename would be reused, breaking the online snapshot
7787 *
7788 * So instead we now use a timestamp.
7789 *
7790 * @param strStateFilePath
7791 */
7792
7793void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7794{
7795 AutoCaller autoCaller(this);
7796 AssertComRCReturnVoid(autoCaller.hrc());
7797
7798 {
7799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7800 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7801 }
7802
7803 RTTIMESPEC ts;
7804 RTTimeNow(&ts);
7805 RTTIME time;
7806 RTTimeExplode(&time, &ts);
7807
7808 strStateFilePath += RTPATH_DELIMITER;
7809 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7810 time.i32Year, time.u8Month, time.u8MonthDay,
7811 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7812}
7813
7814/**
7815 * Returns whether at least one USB controller is present for the VM.
7816 */
7817bool Machine::i_isUSBControllerPresent()
7818{
7819 AutoCaller autoCaller(this);
7820 AssertComRCReturn(autoCaller.hrc(), false);
7821
7822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7823
7824 return (mUSBControllers->size() > 0);
7825}
7826
7827
7828/**
7829 * @note Locks this object for writing, calls the client process
7830 * (inside the lock).
7831 */
7832HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7833 const Utf8Str &strFrontend,
7834 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7835 ProgressProxy *aProgress)
7836{
7837 LogFlowThisFuncEnter();
7838
7839 AssertReturn(aControl, E_FAIL);
7840 AssertReturn(aProgress, E_FAIL);
7841 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7842
7843 AutoCaller autoCaller(this);
7844 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
7845
7846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7847
7848 if (!mData->mRegistered)
7849 return setError(E_UNEXPECTED,
7850 tr("The machine '%s' is not registered"),
7851 mUserData->s.strName.c_str());
7852
7853 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7854
7855 /* The process started when launching a VM with separate UI/VM processes is always
7856 * the UI process, i.e. needs special handling as it won't claim the session. */
7857 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7858
7859 if (fSeparate)
7860 {
7861 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7862 return setError(VBOX_E_INVALID_OBJECT_STATE,
7863 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7864 mUserData->s.strName.c_str());
7865 }
7866 else
7867 {
7868 if ( mData->mSession.mState == SessionState_Locked
7869 || mData->mSession.mState == SessionState_Spawning
7870 || mData->mSession.mState == SessionState_Unlocking)
7871 return setError(VBOX_E_INVALID_OBJECT_STATE,
7872 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7873 mUserData->s.strName.c_str());
7874
7875 /* may not be busy */
7876 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7877 }
7878
7879 /* Hardening logging */
7880#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7881 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7882 {
7883 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7884 int vrc2;
7885 /* ignore rc */ i_deleteFile(strHardeningLogFile, false /* fIgnoreFailures */, tr("hardening log file"), &vrc2);
7886 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7887 {
7888 Utf8Str strStartupLogDir = strHardeningLogFile;
7889 strStartupLogDir.stripFilename();
7890 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7891 file without stripping the file. */
7892 }
7893 strSupHardeningLogArg.append(strHardeningLogFile);
7894
7895 /* Remove legacy log filename to avoid confusion. */
7896 Utf8Str strOldStartupLogFile;
7897 getLogFolder(strOldStartupLogFile);
7898 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7899 i_deleteFile(strOldStartupLogFile, true /* fIgnoreFailures */);
7900 }
7901#else
7902 Utf8Str strSupHardeningLogArg;
7903#endif
7904
7905 Utf8Str strAppOverride;
7906#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7907 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7908#endif
7909
7910 bool fUseVBoxSDS = false;
7911 Utf8Str strCanonicalName;
7912 if (false)
7913 { }
7914#ifdef VBOX_WITH_QTGUI
7915 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7916 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7917 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7918 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7919 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7920 {
7921 strCanonicalName = "GUI/Qt";
7922 fUseVBoxSDS = true;
7923 }
7924#endif
7925#ifdef VBOX_WITH_VBOXSDL
7926 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7927 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7928 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7929 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7930 {
7931 strCanonicalName = "GUI/SDL";
7932 fUseVBoxSDS = true;
7933 }
7934#endif
7935#ifdef VBOX_WITH_HEADLESS
7936 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7937 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7938 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7939 {
7940 strCanonicalName = "headless";
7941 }
7942#endif
7943 else
7944 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7945
7946 Utf8Str idStr = mData->mUuid.toString();
7947 Utf8Str const &strMachineName = mUserData->s.strName;
7948 RTPROCESS pid = NIL_RTPROCESS;
7949
7950#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7951 RT_NOREF(fUseVBoxSDS);
7952#else
7953 DWORD idCallerSession = ~(DWORD)0;
7954 if (fUseVBoxSDS)
7955 {
7956 /*
7957 * The VBoxSDS should be used for process launching the VM with
7958 * GUI only if the caller and the VBoxSDS are in different Windows
7959 * sessions and the caller in the interactive one.
7960 */
7961 fUseVBoxSDS = false;
7962
7963 /* Get windows session of the current process. The process token used
7964 due to several reasons:
7965 1. The token is absent for the current thread except someone set it
7966 for us.
7967 2. Needs to get the id of the session where the process is started.
7968 We only need to do this once, though. */
7969 static DWORD s_idCurrentSession = ~(DWORD)0;
7970 DWORD idCurrentSession = s_idCurrentSession;
7971 if (idCurrentSession == ~(DWORD)0)
7972 {
7973 HANDLE hCurrentProcessToken = NULL;
7974 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7975 {
7976 DWORD cbIgn = 0;
7977 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7978 s_idCurrentSession = idCurrentSession;
7979 else
7980 {
7981 idCurrentSession = ~(DWORD)0;
7982 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7983 }
7984 CloseHandle(hCurrentProcessToken);
7985 }
7986 else
7987 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7988 }
7989
7990 /* get the caller's session */
7991 HRESULT hrc = CoImpersonateClient();
7992 if (SUCCEEDED(hrc))
7993 {
7994 HANDLE hCallerThreadToken;
7995 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7996 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7997 &hCallerThreadToken))
7998 {
7999 SetLastError(NO_ERROR);
8000 DWORD cbIgn = 0;
8001 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
8002 {
8003 /* Only need to use SDS if the session ID differs: */
8004 if (idCurrentSession != idCallerSession)
8005 {
8006 fUseVBoxSDS = false;
8007
8008 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
8009 DWORD cbTokenGroups = 0;
8010 PTOKEN_GROUPS pTokenGroups = NULL;
8011 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
8012 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
8013 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
8014 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
8015 {
8016 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
8017 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
8018 PSID pInteractiveSid = NULL;
8019 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
8020 {
8021 /* Iterate over the groups looking for the interactive SID: */
8022 fUseVBoxSDS = false;
8023 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
8024 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
8025 {
8026 fUseVBoxSDS = true;
8027 break;
8028 }
8029 FreeSid(pInteractiveSid);
8030 }
8031 }
8032 else
8033 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
8034 RTMemTmpFree(pTokenGroups);
8035 }
8036 }
8037 else
8038 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
8039 CloseHandle(hCallerThreadToken);
8040 }
8041 else
8042 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
8043 CoRevertToSelf();
8044 }
8045 else
8046 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
8047 }
8048 if (fUseVBoxSDS)
8049 {
8050 /* connect to VBoxSDS */
8051 ComPtr<IVirtualBoxSDS> pVBoxSDS;
8052 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
8053 if (FAILED(rc))
8054 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
8055 strMachineName.c_str());
8056
8057 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
8058 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
8059 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
8060 service to access the files. */
8061 rc = CoSetProxyBlanket(pVBoxSDS,
8062 RPC_C_AUTHN_DEFAULT,
8063 RPC_C_AUTHZ_DEFAULT,
8064 COLE_DEFAULT_PRINCIPAL,
8065 RPC_C_AUTHN_LEVEL_DEFAULT,
8066 RPC_C_IMP_LEVEL_IMPERSONATE,
8067 NULL,
8068 EOAC_DEFAULT);
8069 if (FAILED(rc))
8070 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
8071
8072 size_t const cEnvVars = aEnvironmentChanges.size();
8073 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
8074 for (size_t i = 0; i < cEnvVars; i++)
8075 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
8076
8077 ULONG uPid = 0;
8078 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
8079 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
8080 idCallerSession, &uPid);
8081 if (FAILED(rc))
8082 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
8083 pid = (RTPROCESS)uPid;
8084 }
8085 else
8086#endif /* VBOX_WITH_SDS && RT_OS_WINDOWS */
8087 {
8088 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
8089 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
8090 if (RT_FAILURE(vrc))
8091 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
8092 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
8093 }
8094
8095 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
8096 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
8097
8098 if (!fSeparate)
8099 {
8100 /*
8101 * Note that we don't release the lock here before calling the client,
8102 * because it doesn't need to call us back if called with a NULL argument.
8103 * Releasing the lock here is dangerous because we didn't prepare the
8104 * launch data yet, but the client we've just started may happen to be
8105 * too fast and call LockMachine() that will fail (because of PID, etc.),
8106 * so that the Machine will never get out of the Spawning session state.
8107 */
8108
8109 /* inform the session that it will be a remote one */
8110 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8111#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8112 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8113#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8114 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8115#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8116 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8117
8118 if (FAILED(rc))
8119 {
8120 /* restore the session state */
8121 mData->mSession.mState = SessionState_Unlocked;
8122 alock.release();
8123 mParent->i_addProcessToReap(pid);
8124 /* The failure may occur w/o any error info (from RPC), so provide one */
8125 return setError(VBOX_E_VM_ERROR,
8126 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8127 }
8128
8129 /* attach launch data to the machine */
8130 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8131 mData->mSession.mRemoteControls.push_back(aControl);
8132 mData->mSession.mProgress = aProgress;
8133 mData->mSession.mPID = pid;
8134 mData->mSession.mState = SessionState_Spawning;
8135 Assert(strCanonicalName.isNotEmpty());
8136 mData->mSession.mName = strCanonicalName;
8137 }
8138 else
8139 {
8140 /* For separate UI process we declare the launch as completed instantly, as the
8141 * actual headless VM start may or may not come. No point in remembering anything
8142 * yet, as what matters for us is when the headless VM gets started. */
8143 aProgress->i_notifyComplete(S_OK);
8144 }
8145
8146 alock.release();
8147 mParent->i_addProcessToReap(pid);
8148
8149 LogFlowThisFuncLeave();
8150 return S_OK;
8151}
8152
8153/**
8154 * Returns @c true if the given session machine instance has an open direct
8155 * session (and optionally also for direct sessions which are closing) and
8156 * returns the session control machine instance if so.
8157 *
8158 * Note that when the method returns @c false, the arguments remain unchanged.
8159 *
8160 * @param aMachine Session machine object.
8161 * @param aControl Direct session control object (optional).
8162 * @param aRequireVM If true then only allow VM sessions.
8163 * @param aAllowClosing If true then additionally a session which is currently
8164 * being closed will also be allowed.
8165 *
8166 * @note locks this object for reading.
8167 */
8168bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8169 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8170 bool aRequireVM /*= false*/,
8171 bool aAllowClosing /*= false*/)
8172{
8173 AutoLimitedCaller autoCaller(this);
8174 AssertComRCReturn(autoCaller.hrc(), false);
8175
8176 /* just return false for inaccessible machines */
8177 if (getObjectState().getState() != ObjectState::Ready)
8178 return false;
8179
8180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8181
8182 if ( ( mData->mSession.mState == SessionState_Locked
8183 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
8184 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8185 )
8186 {
8187 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8188
8189 aMachine = mData->mSession.mMachine;
8190
8191 if (aControl != NULL)
8192 *aControl = mData->mSession.mDirectControl;
8193
8194 return true;
8195 }
8196
8197 return false;
8198}
8199
8200/**
8201 * Returns @c true if the given machine has an spawning direct session.
8202 *
8203 * @note locks this object for reading.
8204 */
8205bool Machine::i_isSessionSpawning()
8206{
8207 AutoLimitedCaller autoCaller(this);
8208 AssertComRCReturn(autoCaller.hrc(), false);
8209
8210 /* just return false for inaccessible machines */
8211 if (getObjectState().getState() != ObjectState::Ready)
8212 return false;
8213
8214 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8215
8216 if (mData->mSession.mState == SessionState_Spawning)
8217 return true;
8218
8219 return false;
8220}
8221
8222/**
8223 * Called from the client watcher thread to check for unexpected client process
8224 * death during Session_Spawning state (e.g. before it successfully opened a
8225 * direct session).
8226 *
8227 * On Win32 and on OS/2, this method is called only when we've got the
8228 * direct client's process termination notification, so it always returns @c
8229 * true.
8230 *
8231 * On other platforms, this method returns @c true if the client process is
8232 * terminated and @c false if it's still alive.
8233 *
8234 * @note Locks this object for writing.
8235 */
8236bool Machine::i_checkForSpawnFailure()
8237{
8238 AutoCaller autoCaller(this);
8239 if (!autoCaller.isOk())
8240 {
8241 /* nothing to do */
8242 LogFlowThisFunc(("Already uninitialized!\n"));
8243 return true;
8244 }
8245
8246 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8247
8248 if (mData->mSession.mState != SessionState_Spawning)
8249 {
8250 /* nothing to do */
8251 LogFlowThisFunc(("Not spawning any more!\n"));
8252 return true;
8253 }
8254
8255 HRESULT rc = S_OK;
8256
8257 /* PID not yet initialized, skip check. */
8258 if (mData->mSession.mPID == NIL_RTPROCESS)
8259 return false;
8260
8261 RTPROCSTATUS status;
8262 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8263
8264 if (vrc != VERR_PROCESS_RUNNING)
8265 {
8266 Utf8Str strExtraInfo;
8267
8268#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8269 /* If the startup logfile exists and is of non-zero length, tell the
8270 user to look there for more details to encourage them to attach it
8271 when reporting startup issues. */
8272 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8273 uint64_t cbStartupLogFile = 0;
8274 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
8275 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8276 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
8277#endif
8278
8279 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8280 rc = setError(E_FAIL,
8281 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8282 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8283 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8284 rc = setError(E_FAIL,
8285 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8286 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8287 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8288 rc = setError(E_FAIL,
8289 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8290 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8291 else
8292 rc = setErrorBoth(E_FAIL, vrc,
8293 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8294 i_getName().c_str(), vrc, strExtraInfo.c_str());
8295 }
8296
8297 if (FAILED(rc))
8298 {
8299 /* Close the remote session, remove the remote control from the list
8300 * and reset session state to Closed (@note keep the code in sync with
8301 * the relevant part in LockMachine()). */
8302
8303 Assert(mData->mSession.mRemoteControls.size() == 1);
8304 if (mData->mSession.mRemoteControls.size() == 1)
8305 {
8306 ErrorInfoKeeper eik;
8307 mData->mSession.mRemoteControls.front()->Uninitialize();
8308 }
8309
8310 mData->mSession.mRemoteControls.clear();
8311 mData->mSession.mState = SessionState_Unlocked;
8312
8313 /* finalize the progress after setting the state */
8314 if (!mData->mSession.mProgress.isNull())
8315 {
8316 mData->mSession.mProgress->notifyComplete(rc);
8317 mData->mSession.mProgress.setNull();
8318 }
8319
8320 mData->mSession.mPID = NIL_RTPROCESS;
8321
8322 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
8323 return true;
8324 }
8325
8326 return false;
8327}
8328
8329/**
8330 * Checks whether the machine can be registered. If so, commits and saves
8331 * all settings.
8332 *
8333 * @note Must be called from mParent's write lock. Locks this object and
8334 * children for writing.
8335 */
8336HRESULT Machine::i_prepareRegister()
8337{
8338 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8339
8340 AutoLimitedCaller autoCaller(this);
8341 AssertComRCReturnRC(autoCaller.hrc());
8342
8343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8344
8345 /* wait for state dependents to drop to zero */
8346 i_ensureNoStateDependencies(alock);
8347
8348 if (!mData->mAccessible)
8349 return setError(VBOX_E_INVALID_OBJECT_STATE,
8350 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8351 mUserData->s.strName.c_str(),
8352 mData->mUuid.toString().c_str());
8353
8354 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8355
8356 if (mData->mRegistered)
8357 return setError(VBOX_E_INVALID_OBJECT_STATE,
8358 tr("The machine '%s' with UUID {%s} is already registered"),
8359 mUserData->s.strName.c_str(),
8360 mData->mUuid.toString().c_str());
8361
8362 HRESULT rc = S_OK;
8363
8364 // Ensure the settings are saved. If we are going to be registered and
8365 // no config file exists yet, create it by calling i_saveSettings() too.
8366 if ( (mData->flModifications)
8367 || (!mData->pMachineConfigFile->fileExists())
8368 )
8369 {
8370 rc = i_saveSettings(NULL, alock);
8371 // no need to check whether VirtualBox.xml needs saving too since
8372 // we can't have a machine XML file rename pending
8373 if (FAILED(rc)) return rc;
8374 }
8375
8376 /* more config checking goes here */
8377
8378 if (SUCCEEDED(rc))
8379 {
8380 /* we may have had implicit modifications we want to fix on success */
8381 i_commit();
8382
8383 mData->mRegistered = true;
8384 }
8385 else
8386 {
8387 /* we may have had implicit modifications we want to cancel on failure*/
8388 i_rollback(false /* aNotify */);
8389 }
8390
8391 return rc;
8392}
8393
8394/**
8395 * Increases the number of objects dependent on the machine state or on the
8396 * registered state. Guarantees that these two states will not change at least
8397 * until #i_releaseStateDependency() is called.
8398 *
8399 * Depending on the @a aDepType value, additional state checks may be made.
8400 * These checks will set extended error info on failure. See
8401 * #i_checkStateDependency() for more info.
8402 *
8403 * If this method returns a failure, the dependency is not added and the caller
8404 * is not allowed to rely on any particular machine state or registration state
8405 * value and may return the failed result code to the upper level.
8406 *
8407 * @param aDepType Dependency type to add.
8408 * @param aState Current machine state (NULL if not interested).
8409 * @param aRegistered Current registered state (NULL if not interested).
8410 *
8411 * @note Locks this object for writing.
8412 */
8413HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8414 MachineState_T *aState /* = NULL */,
8415 BOOL *aRegistered /* = NULL */)
8416{
8417 AutoCaller autoCaller(this);
8418 AssertComRCReturnRC(autoCaller.hrc());
8419
8420 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8421
8422 HRESULT rc = i_checkStateDependency(aDepType);
8423 if (FAILED(rc)) return rc;
8424
8425 {
8426 if (mData->mMachineStateChangePending != 0)
8427 {
8428 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8429 * drop to zero so don't add more. It may make sense to wait a bit
8430 * and retry before reporting an error (since the pending state
8431 * transition should be really quick) but let's just assert for
8432 * now to see if it ever happens on practice. */
8433
8434 AssertFailed();
8435
8436 return setError(E_ACCESSDENIED,
8437 tr("Machine state change is in progress. Please retry the operation later."));
8438 }
8439
8440 ++mData->mMachineStateDeps;
8441 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8442 }
8443
8444 if (aState)
8445 *aState = mData->mMachineState;
8446 if (aRegistered)
8447 *aRegistered = mData->mRegistered;
8448
8449 return S_OK;
8450}
8451
8452/**
8453 * Decreases the number of objects dependent on the machine state.
8454 * Must always complete the #i_addStateDependency() call after the state
8455 * dependency is no more necessary.
8456 */
8457void Machine::i_releaseStateDependency()
8458{
8459 AutoCaller autoCaller(this);
8460 AssertComRCReturnVoid(autoCaller.hrc());
8461
8462 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8463
8464 /* releaseStateDependency() w/o addStateDependency()? */
8465 AssertReturnVoid(mData->mMachineStateDeps != 0);
8466 -- mData->mMachineStateDeps;
8467
8468 if (mData->mMachineStateDeps == 0)
8469 {
8470 /* inform i_ensureNoStateDependencies() that there are no more deps */
8471 if (mData->mMachineStateChangePending != 0)
8472 {
8473 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8474 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8475 }
8476 }
8477}
8478
8479Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8480{
8481 /* start with nothing found */
8482 Utf8Str strResult("");
8483
8484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8485
8486 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8487 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8488 // found:
8489 strResult = it->second; // source is a Utf8Str
8490
8491 return strResult;
8492}
8493
8494// protected methods
8495/////////////////////////////////////////////////////////////////////////////
8496
8497/**
8498 * Performs machine state checks based on the @a aDepType value. If a check
8499 * fails, this method will set extended error info, otherwise it will return
8500 * S_OK. It is supposed, that on failure, the caller will immediately return
8501 * the return value of this method to the upper level.
8502 *
8503 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8504 *
8505 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8506 * current state of this machine object allows to change settings of the
8507 * machine (i.e. the machine is not registered, or registered but not running
8508 * and not saved). It is useful to call this method from Machine setters
8509 * before performing any change.
8510 *
8511 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8512 * as for MutableStateDep except that if the machine is saved, S_OK is also
8513 * returned. This is useful in setters which allow changing machine
8514 * properties when it is in the saved state.
8515 *
8516 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8517 * if the current state of this machine object allows to change runtime
8518 * changeable settings of the machine (i.e. the machine is not registered, or
8519 * registered but either running or not running and not saved). It is useful
8520 * to call this method from Machine setters before performing any changes to
8521 * runtime changeable settings.
8522 *
8523 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8524 * the same as for MutableOrRunningStateDep except that if the machine is
8525 * saved, S_OK is also returned. This is useful in setters which allow
8526 * changing runtime and saved state changeable machine properties.
8527 *
8528 * @param aDepType Dependency type to check.
8529 *
8530 * @note Non Machine based classes should use #i_addStateDependency() and
8531 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8532 * template.
8533 *
8534 * @note This method must be called from under this object's read or write
8535 * lock.
8536 */
8537HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8538{
8539 switch (aDepType)
8540 {
8541 case AnyStateDep:
8542 {
8543 break;
8544 }
8545 case MutableStateDep:
8546 {
8547 if ( mData->mRegistered
8548 && ( !i_isSessionMachine()
8549 || ( mData->mMachineState != MachineState_Aborted
8550 && mData->mMachineState != MachineState_Teleported
8551 && mData->mMachineState != MachineState_PoweredOff
8552 )
8553 )
8554 )
8555 return setError(VBOX_E_INVALID_VM_STATE,
8556 tr("The machine is not mutable (state is %s)"),
8557 Global::stringifyMachineState(mData->mMachineState));
8558 break;
8559 }
8560 case MutableOrSavedStateDep:
8561 {
8562 if ( mData->mRegistered
8563 && ( !i_isSessionMachine()
8564 || ( mData->mMachineState != MachineState_Aborted
8565 && mData->mMachineState != MachineState_Teleported
8566 && mData->mMachineState != MachineState_Saved
8567 && mData->mMachineState != MachineState_AbortedSaved
8568 && mData->mMachineState != MachineState_PoweredOff
8569 )
8570 )
8571 )
8572 return setError(VBOX_E_INVALID_VM_STATE,
8573 tr("The machine is not mutable or saved (state is %s)"),
8574 Global::stringifyMachineState(mData->mMachineState));
8575 break;
8576 }
8577 case MutableOrRunningStateDep:
8578 {
8579 if ( mData->mRegistered
8580 && ( !i_isSessionMachine()
8581 || ( mData->mMachineState != MachineState_Aborted
8582 && mData->mMachineState != MachineState_Teleported
8583 && mData->mMachineState != MachineState_PoweredOff
8584 && !Global::IsOnline(mData->mMachineState)
8585 )
8586 )
8587 )
8588 return setError(VBOX_E_INVALID_VM_STATE,
8589 tr("The machine is not mutable or running (state is %s)"),
8590 Global::stringifyMachineState(mData->mMachineState));
8591 break;
8592 }
8593 case MutableOrSavedOrRunningStateDep:
8594 {
8595 if ( mData->mRegistered
8596 && ( !i_isSessionMachine()
8597 || ( mData->mMachineState != MachineState_Aborted
8598 && mData->mMachineState != MachineState_Teleported
8599 && mData->mMachineState != MachineState_Saved
8600 && mData->mMachineState != MachineState_AbortedSaved
8601 && mData->mMachineState != MachineState_PoweredOff
8602 && !Global::IsOnline(mData->mMachineState)
8603 )
8604 )
8605 )
8606 return setError(VBOX_E_INVALID_VM_STATE,
8607 tr("The machine is not mutable, saved or running (state is %s)"),
8608 Global::stringifyMachineState(mData->mMachineState));
8609 break;
8610 }
8611 }
8612
8613 return S_OK;
8614}
8615
8616/**
8617 * Helper to initialize all associated child objects and allocate data
8618 * structures.
8619 *
8620 * This method must be called as a part of the object's initialization procedure
8621 * (usually done in the #init() method).
8622 *
8623 * @note Must be called only from #init() or from #i_registeredInit().
8624 */
8625HRESULT Machine::initDataAndChildObjects()
8626{
8627 AutoCaller autoCaller(this);
8628 AssertComRCReturnRC(autoCaller.hrc());
8629 AssertReturn( getObjectState().getState() == ObjectState::InInit
8630 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8631
8632 AssertReturn(!mData->mAccessible, E_FAIL);
8633
8634 /* allocate data structures */
8635 mSSData.allocate();
8636 mUserData.allocate();
8637 mHWData.allocate();
8638 mMediumAttachments.allocate();
8639 mStorageControllers.allocate();
8640 mUSBControllers.allocate();
8641
8642 /* initialize mOSTypeId */
8643 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8644
8645/** @todo r=bird: init() methods never fails, right? Why don't we make them
8646 * return void then! */
8647
8648 /* create associated BIOS settings object */
8649 unconst(mBIOSSettings).createObject();
8650 mBIOSSettings->init(this);
8651
8652 /* create associated recording settings object */
8653 unconst(mRecordingSettings).createObject();
8654 mRecordingSettings->init(this);
8655
8656 /* create associated trusted platform module object */
8657 unconst(mTrustedPlatformModule).createObject();
8658 mTrustedPlatformModule->init(this);
8659
8660 /* create associated NVRAM store object */
8661 unconst(mNvramStore).createObject();
8662 mNvramStore->init(this);
8663
8664 /* create the graphics adapter object (always present) */
8665 unconst(mGraphicsAdapter).createObject();
8666 mGraphicsAdapter->init(this);
8667
8668 /* create an associated VRDE object (default is disabled) */
8669 unconst(mVRDEServer).createObject();
8670 mVRDEServer->init(this);
8671
8672 /* create associated serial port objects */
8673 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8674 {
8675 unconst(mSerialPorts[slot]).createObject();
8676 mSerialPorts[slot]->init(this, slot);
8677 }
8678
8679 /* create associated parallel port objects */
8680 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8681 {
8682 unconst(mParallelPorts[slot]).createObject();
8683 mParallelPorts[slot]->init(this, slot);
8684 }
8685
8686 /* create the audio settings object */
8687 unconst(mAudioSettings).createObject();
8688 mAudioSettings->init(this);
8689
8690 /* create the USB device filters object (always present) */
8691 unconst(mUSBDeviceFilters).createObject();
8692 mUSBDeviceFilters->init(this);
8693
8694 /* create associated network adapter objects */
8695 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8696 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8697 {
8698 unconst(mNetworkAdapters[slot]).createObject();
8699 mNetworkAdapters[slot]->init(this, slot);
8700 }
8701
8702 /* create the bandwidth control */
8703 unconst(mBandwidthControl).createObject();
8704 mBandwidthControl->init(this);
8705
8706 /* create the guest debug control object */
8707 unconst(mGuestDebugControl).createObject();
8708 mGuestDebugControl->init(this);
8709
8710 return S_OK;
8711}
8712
8713/**
8714 * Helper to uninitialize all associated child objects and to free all data
8715 * structures.
8716 *
8717 * This method must be called as a part of the object's uninitialization
8718 * procedure (usually done in the #uninit() method).
8719 *
8720 * @note Must be called only from #uninit() or from #i_registeredInit().
8721 */
8722void Machine::uninitDataAndChildObjects()
8723{
8724 AutoCaller autoCaller(this);
8725 AssertComRCReturnVoid(autoCaller.hrc());
8726 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8727 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8728 || getObjectState().getState() == ObjectState::InUninit
8729 || getObjectState().getState() == ObjectState::Limited);
8730
8731 /* tell all our other child objects we've been uninitialized */
8732 if (mGuestDebugControl)
8733 {
8734 mGuestDebugControl->uninit();
8735 unconst(mGuestDebugControl).setNull();
8736 }
8737
8738 if (mBandwidthControl)
8739 {
8740 mBandwidthControl->uninit();
8741 unconst(mBandwidthControl).setNull();
8742 }
8743
8744 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8745 {
8746 if (mNetworkAdapters[slot])
8747 {
8748 mNetworkAdapters[slot]->uninit();
8749 unconst(mNetworkAdapters[slot]).setNull();
8750 }
8751 }
8752
8753 if (mUSBDeviceFilters)
8754 {
8755 mUSBDeviceFilters->uninit();
8756 unconst(mUSBDeviceFilters).setNull();
8757 }
8758
8759 if (mAudioSettings)
8760 {
8761 mAudioSettings->uninit();
8762 unconst(mAudioSettings).setNull();
8763 }
8764
8765 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8766 {
8767 if (mParallelPorts[slot])
8768 {
8769 mParallelPorts[slot]->uninit();
8770 unconst(mParallelPorts[slot]).setNull();
8771 }
8772 }
8773
8774 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8775 {
8776 if (mSerialPorts[slot])
8777 {
8778 mSerialPorts[slot]->uninit();
8779 unconst(mSerialPorts[slot]).setNull();
8780 }
8781 }
8782
8783 if (mVRDEServer)
8784 {
8785 mVRDEServer->uninit();
8786 unconst(mVRDEServer).setNull();
8787 }
8788
8789 if (mGraphicsAdapter)
8790 {
8791 mGraphicsAdapter->uninit();
8792 unconst(mGraphicsAdapter).setNull();
8793 }
8794
8795 if (mBIOSSettings)
8796 {
8797 mBIOSSettings->uninit();
8798 unconst(mBIOSSettings).setNull();
8799 }
8800
8801 if (mRecordingSettings)
8802 {
8803 mRecordingSettings->uninit();
8804 unconst(mRecordingSettings).setNull();
8805 }
8806
8807 if (mTrustedPlatformModule)
8808 {
8809 mTrustedPlatformModule->uninit();
8810 unconst(mTrustedPlatformModule).setNull();
8811 }
8812
8813 if (mNvramStore)
8814 {
8815 mNvramStore->uninit();
8816 unconst(mNvramStore).setNull();
8817 }
8818
8819 /* Deassociate media (only when a real Machine or a SnapshotMachine
8820 * instance is uninitialized; SessionMachine instances refer to real
8821 * Machine media). This is necessary for a clean re-initialization of
8822 * the VM after successfully re-checking the accessibility state. Note
8823 * that in case of normal Machine or SnapshotMachine uninitialization (as
8824 * a result of unregistering or deleting the snapshot), outdated media
8825 * attachments will already be uninitialized and deleted, so this
8826 * code will not affect them. */
8827 if ( !mMediumAttachments.isNull()
8828 && !i_isSessionMachine()
8829 )
8830 {
8831 for (MediumAttachmentList::const_iterator
8832 it = mMediumAttachments->begin();
8833 it != mMediumAttachments->end();
8834 ++it)
8835 {
8836 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8837 if (pMedium.isNull())
8838 continue;
8839 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8840 AssertComRC(rc);
8841 }
8842 }
8843
8844 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8845 {
8846 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children)
8847 if (mData->mFirstSnapshot)
8848 {
8849 // Snapshots tree is protected by machine write lock.
8850 // Otherwise we assert in Snapshot::uninit()
8851 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8852 mData->mFirstSnapshot->uninit();
8853 mData->mFirstSnapshot.setNull();
8854 }
8855
8856 mData->mCurrentSnapshot.setNull();
8857 }
8858
8859 /* free data structures (the essential mData structure is not freed here
8860 * since it may be still in use) */
8861 mMediumAttachments.free();
8862 mStorageControllers.free();
8863 mUSBControllers.free();
8864 mHWData.free();
8865 mUserData.free();
8866 mSSData.free();
8867}
8868
8869/**
8870 * Returns a pointer to the Machine object for this machine that acts like a
8871 * parent for complex machine data objects such as shared folders, etc.
8872 *
8873 * For primary Machine objects and for SnapshotMachine objects, returns this
8874 * object's pointer itself. For SessionMachine objects, returns the peer
8875 * (primary) machine pointer.
8876 */
8877Machine *Machine::i_getMachine()
8878{
8879 if (i_isSessionMachine())
8880 return (Machine*)mPeer;
8881 return this;
8882}
8883
8884/**
8885 * Makes sure that there are no machine state dependents. If necessary, waits
8886 * for the number of dependents to drop to zero.
8887 *
8888 * Make sure this method is called from under this object's write lock to
8889 * guarantee that no new dependents may be added when this method returns
8890 * control to the caller.
8891 *
8892 * @note Receives a lock to this object for writing. The lock will be released
8893 * while waiting (if necessary).
8894 *
8895 * @warning To be used only in methods that change the machine state!
8896 */
8897void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8898{
8899 AssertReturnVoid(isWriteLockOnCurrentThread());
8900
8901 /* Wait for all state dependents if necessary */
8902 if (mData->mMachineStateDeps != 0)
8903 {
8904 /* lazy semaphore creation */
8905 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8906 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8907
8908 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8909 mData->mMachineStateDeps));
8910
8911 ++mData->mMachineStateChangePending;
8912
8913 /* reset the semaphore before waiting, the last dependent will signal
8914 * it */
8915 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8916
8917 alock.release();
8918
8919 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8920
8921 alock.acquire();
8922
8923 -- mData->mMachineStateChangePending;
8924 }
8925}
8926
8927/**
8928 * Changes the machine state and informs callbacks.
8929 *
8930 * This method is not intended to fail so it either returns S_OK or asserts (and
8931 * returns a failure).
8932 *
8933 * @note Locks this object for writing.
8934 */
8935HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8936{
8937 LogFlowThisFuncEnter();
8938 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8939 Assert(aMachineState != MachineState_Null);
8940
8941 AutoCaller autoCaller(this);
8942 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
8943
8944 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8945
8946 /* wait for state dependents to drop to zero */
8947 i_ensureNoStateDependencies(alock);
8948
8949 MachineState_T const enmOldState = mData->mMachineState;
8950 if (enmOldState != aMachineState)
8951 {
8952 mData->mMachineState = aMachineState;
8953 RTTimeNow(&mData->mLastStateChange);
8954
8955#ifdef VBOX_WITH_DTRACE_R3_MAIN
8956 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8957#endif
8958 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8959 }
8960
8961 LogFlowThisFuncLeave();
8962 return S_OK;
8963}
8964
8965/**
8966 * Searches for a shared folder with the given logical name
8967 * in the collection of shared folders.
8968 *
8969 * @param aName logical name of the shared folder
8970 * @param aSharedFolder where to return the found object
8971 * @param aSetError whether to set the error info if the folder is
8972 * not found
8973 * @return
8974 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8975 *
8976 * @note
8977 * must be called from under the object's lock!
8978 */
8979HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8980 ComObjPtr<SharedFolder> &aSharedFolder,
8981 bool aSetError /* = false */)
8982{
8983 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8984 for (HWData::SharedFolderList::const_iterator
8985 it = mHWData->mSharedFolders.begin();
8986 it != mHWData->mSharedFolders.end();
8987 ++it)
8988 {
8989 SharedFolder *pSF = *it;
8990 AutoCaller autoCaller(pSF);
8991 if (pSF->i_getName() == aName)
8992 {
8993 aSharedFolder = pSF;
8994 rc = S_OK;
8995 break;
8996 }
8997 }
8998
8999 if (aSetError && FAILED(rc))
9000 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
9001
9002 return rc;
9003}
9004
9005/**
9006 * Initializes all machine instance data from the given settings structures
9007 * from XML. The exception is the machine UUID which needs special handling
9008 * depending on the caller's use case, so the caller needs to set that herself.
9009 *
9010 * This gets called in several contexts during machine initialization:
9011 *
9012 * -- When machine XML exists on disk already and needs to be loaded into memory,
9013 * for example, from #i_registeredInit() to load all registered machines on
9014 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
9015 * attached to the machine should be part of some media registry already.
9016 *
9017 * -- During OVF import, when a machine config has been constructed from an
9018 * OVF file. In this case, puuidRegistry is set to the machine UUID to
9019 * ensure that the media listed as attachments in the config (which have
9020 * been imported from the OVF) receive the correct registry ID.
9021 *
9022 * -- During VM cloning.
9023 *
9024 * @param config Machine settings from XML.
9025 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
9026 * for each attached medium in the config.
9027 * @return
9028 */
9029HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
9030 const Guid *puuidRegistry)
9031{
9032 // copy name, description, OS type, teleporter, UTC etc.
9033 mUserData->s = config.machineUserData;
9034
9035 // look up the object by Id to check it is valid
9036 ComObjPtr<GuestOSType> pGuestOSType;
9037 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9038 if (!pGuestOSType.isNull())
9039 mUserData->s.strOsType = pGuestOSType->i_id();
9040
9041#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9042 // stateFile encryption (optional)
9043 mSSData->strStateKeyId = config.strStateKeyId;
9044 mSSData->strStateKeyStore = config.strStateKeyStore;
9045 mData->mstrLogKeyId = config.strLogKeyId;
9046 mData->mstrLogKeyStore = config.strLogKeyStore;
9047#endif
9048
9049 // stateFile (optional)
9050 if (config.strStateFile.isEmpty())
9051 mSSData->strStateFilePath.setNull();
9052 else
9053 {
9054 Utf8Str stateFilePathFull(config.strStateFile);
9055 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
9056 if (RT_FAILURE(vrc))
9057 return setErrorBoth(E_FAIL, vrc,
9058 tr("Invalid saved state file path '%s' (%Rrc)"),
9059 config.strStateFile.c_str(),
9060 vrc);
9061 mSSData->strStateFilePath = stateFilePathFull;
9062 }
9063
9064 // snapshot folder needs special processing so set it again
9065 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
9066 if (FAILED(rc)) return rc;
9067
9068 /* Copy the extra data items (config may or may not be the same as
9069 * mData->pMachineConfigFile) if necessary. When loading the XML files
9070 * from disk they are the same, but not for OVF import. */
9071 if (mData->pMachineConfigFile != &config)
9072 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
9073
9074 /* currentStateModified (optional, default is true) */
9075 mData->mCurrentStateModified = config.fCurrentStateModified;
9076
9077 mData->mLastStateChange = config.timeLastStateChange;
9078
9079 /*
9080 * note: all mUserData members must be assigned prior this point because
9081 * we need to commit changes in order to let mUserData be shared by all
9082 * snapshot machine instances.
9083 */
9084 mUserData.commitCopy();
9085
9086 // machine registry, if present (must be loaded before snapshots)
9087 if (config.canHaveOwnMediaRegistry())
9088 {
9089 // determine machine folder
9090 Utf8Str strMachineFolder = i_getSettingsFileFull();
9091 strMachineFolder.stripFilename();
9092 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
9093 config.mediaRegistry,
9094 strMachineFolder);
9095 if (FAILED(rc)) return rc;
9096 }
9097
9098 /* Snapshot node (optional) */
9099 size_t cRootSnapshots;
9100 if ((cRootSnapshots = config.llFirstSnapshot.size()))
9101 {
9102 // there must be only one root snapshot
9103 Assert(cRootSnapshots == 1);
9104 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9105
9106 rc = i_loadSnapshot(snap,
9107 config.uuidCurrentSnapshot);
9108 if (FAILED(rc)) return rc;
9109 }
9110
9111 // hardware data
9112 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart,
9113 config.recordingSettings);
9114 if (FAILED(rc)) return rc;
9115
9116 /*
9117 * NOTE: the assignment below must be the last thing to do,
9118 * otherwise it will be not possible to change the settings
9119 * somewhere in the code above because all setters will be
9120 * blocked by i_checkStateDependency(MutableStateDep).
9121 */
9122
9123 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
9124 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
9125 {
9126 /* no need to use i_setMachineState() during init() */
9127 mData->mMachineState = MachineState_AbortedSaved;
9128 }
9129 else if (config.fAborted)
9130 {
9131 mSSData->strStateFilePath.setNull();
9132
9133 /* no need to use i_setMachineState() during init() */
9134 mData->mMachineState = MachineState_Aborted;
9135 }
9136 else if (!mSSData->strStateFilePath.isEmpty())
9137 {
9138 /* no need to use i_setMachineState() during init() */
9139 mData->mMachineState = MachineState_Saved;
9140 }
9141
9142 // after loading settings, we are no longer different from the XML on disk
9143 mData->flModifications = 0;
9144
9145 return S_OK;
9146}
9147
9148/**
9149 * Loads all snapshots starting from the given settings.
9150 *
9151 * @param data snapshot settings.
9152 * @param aCurSnapshotId Current snapshot ID from the settings file.
9153 */
9154HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
9155 const Guid &aCurSnapshotId)
9156{
9157 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
9158 AssertReturn(!i_isSessionMachine(), E_FAIL);
9159
9160 HRESULT rc = S_OK;
9161
9162 std::list<const settings::Snapshot *> llSettingsTodo;
9163 llSettingsTodo.push_back(&data);
9164 std::list<Snapshot *> llParentsTodo;
9165 llParentsTodo.push_back(NULL);
9166
9167 while (llSettingsTodo.size() > 0)
9168 {
9169 const settings::Snapshot *current = llSettingsTodo.front();
9170 llSettingsTodo.pop_front();
9171 Snapshot *pParent = llParentsTodo.front();
9172 llParentsTodo.pop_front();
9173
9174 Utf8Str strStateFile;
9175 if (!current->strStateFile.isEmpty())
9176 {
9177 /* optional */
9178 strStateFile = current->strStateFile;
9179 int vrc = i_calculateFullPath(strStateFile, strStateFile);
9180 if (RT_FAILURE(vrc))
9181 {
9182 setErrorBoth(E_FAIL, vrc,
9183 tr("Invalid saved state file path '%s' (%Rrc)"),
9184 strStateFile.c_str(), vrc);
9185 }
9186 }
9187
9188 /* create a snapshot machine object */
9189 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9190 pSnapshotMachine.createObject();
9191 rc = pSnapshotMachine->initFromSettings(this,
9192 current->hardware,
9193 &current->debugging,
9194 &current->autostart,
9195 current->recordingSettings,
9196 current->uuid.ref(),
9197 strStateFile);
9198 if (FAILED(rc)) break;
9199
9200 /* create a snapshot object */
9201 ComObjPtr<Snapshot> pSnapshot;
9202 pSnapshot.createObject();
9203 /* initialize the snapshot */
9204 rc = pSnapshot->init(mParent, // VirtualBox object
9205 current->uuid,
9206 current->strName,
9207 current->strDescription,
9208 current->timestamp,
9209 pSnapshotMachine,
9210 pParent);
9211 if (FAILED(rc)) break;
9212
9213 /* memorize the first snapshot if necessary */
9214 if (!mData->mFirstSnapshot)
9215 {
9216 Assert(pParent == NULL);
9217 mData->mFirstSnapshot = pSnapshot;
9218 }
9219
9220 /* memorize the current snapshot when appropriate */
9221 if ( !mData->mCurrentSnapshot
9222 && pSnapshot->i_getId() == aCurSnapshotId
9223 )
9224 mData->mCurrentSnapshot = pSnapshot;
9225
9226 /* create all children */
9227 std::list<settings::Snapshot>::const_iterator itBegin = current->llChildSnapshots.begin();
9228 std::list<settings::Snapshot>::const_iterator itEnd = current->llChildSnapshots.end();
9229 for (std::list<settings::Snapshot>::const_iterator it = itBegin; it != itEnd; ++it)
9230 {
9231 llSettingsTodo.push_back(&*it);
9232 llParentsTodo.push_back(pSnapshot);
9233 }
9234 }
9235
9236 return rc;
9237}
9238
9239/**
9240 * Loads settings into mHWData.
9241 *
9242 * @param puuidRegistry Registry ID.
9243 * @param puuidSnapshot Snapshot ID
9244 * @param data Reference to the hardware settings.
9245 * @param pDbg Pointer to the debugging settings.
9246 * @param pAutostart Pointer to the autostart settings
9247 * @param recording Reference to recording settings.
9248 */
9249HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
9250 const Guid *puuidSnapshot,
9251 const settings::Hardware &data,
9252 const settings::Debugging *pDbg,
9253 const settings::Autostart *pAutostart,
9254 const settings::RecordingSettings &recording)
9255{
9256 AssertReturn(!i_isSessionMachine(), E_FAIL);
9257
9258 HRESULT rc = S_OK;
9259
9260 try
9261 {
9262 ComObjPtr<GuestOSType> pGuestOSType;
9263 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9264
9265 /* The hardware version attribute (optional). */
9266 mHWData->mHWVersion = data.strVersion;
9267 mHWData->mHardwareUUID = data.uuid;
9268
9269 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9270 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9271 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9272 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9273 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9274 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9275 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
9276 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
9277 mHWData->mPAEEnabled = data.fPAE;
9278 mHWData->mLongMode = data.enmLongMode;
9279 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9280 mHWData->mAPIC = data.fAPIC;
9281 mHWData->mX2APIC = data.fX2APIC;
9282 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9283 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9284 mHWData->mSpecCtrl = data.fSpecCtrl;
9285 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
9286 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
9287 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
9288 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
9289 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
9290 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9291 mHWData->mCPUCount = data.cCPUs;
9292 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9293 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9294 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9295 mHWData->mCpuProfile = data.strCpuProfile;
9296
9297 // cpu
9298 if (mHWData->mCPUHotPlugEnabled)
9299 {
9300 for (settings::CpuList::const_iterator
9301 it = data.llCpus.begin();
9302 it != data.llCpus.end();
9303 ++it)
9304 {
9305 const settings::Cpu &cpu = *it;
9306
9307 mHWData->mCPUAttached[cpu.ulId] = true;
9308 }
9309 }
9310
9311 // cpuid leafs
9312 for (settings::CpuIdLeafsList::const_iterator
9313 it = data.llCpuIdLeafs.begin();
9314 it != data.llCpuIdLeafs.end();
9315 ++it)
9316 {
9317 const settings::CpuIdLeaf &rLeaf= *it;
9318 if ( rLeaf.idx < UINT32_C(0x20)
9319 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9320 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9321 mHWData->mCpuIdLeafList.push_back(rLeaf);
9322 /* else: just ignore */
9323 }
9324
9325 mHWData->mMemorySize = data.ulMemorySizeMB;
9326 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9327
9328 // boot order
9329 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9330 {
9331 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9332 if (it == data.mapBootOrder.end())
9333 mHWData->mBootOrder[i] = DeviceType_Null;
9334 else
9335 mHWData->mBootOrder[i] = it->second;
9336 }
9337
9338 mHWData->mFirmwareType = data.firmwareType;
9339 mHWData->mPointingHIDType = data.pointingHIDType;
9340 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9341 mHWData->mChipsetType = data.chipsetType;
9342 mHWData->mIommuType = data.iommuType;
9343 mHWData->mParavirtProvider = data.paravirtProvider;
9344 mHWData->mParavirtDebug = data.strParavirtDebug;
9345 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9346 mHWData->mHPETEnabled = data.fHPETEnabled;
9347
9348 /* GraphicsAdapter */
9349 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
9350 if (FAILED(rc)) return rc;
9351
9352 /* VRDEServer */
9353 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9354 if (FAILED(rc)) return rc;
9355
9356 /* BIOS */
9357 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9358 if (FAILED(rc)) return rc;
9359
9360 /* Recording */
9361 rc = mRecordingSettings->i_loadSettings(recording);
9362 if (FAILED(rc)) return rc;
9363
9364 /* Trusted Platform Module */
9365 rc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
9366 if (FAILED(rc)) return rc;
9367
9368 rc = mNvramStore->i_loadSettings(data.nvramSettings);
9369 if (FAILED(rc)) return rc;
9370
9371 // Bandwidth control (must come before network adapters)
9372 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9373 if (FAILED(rc)) return rc;
9374
9375 /* USB controllers */
9376 for (settings::USBControllerList::const_iterator
9377 it = data.usbSettings.llUSBControllers.begin();
9378 it != data.usbSettings.llUSBControllers.end();
9379 ++it)
9380 {
9381 const settings::USBController &settingsCtrl = *it;
9382 ComObjPtr<USBController> newCtrl;
9383
9384 newCtrl.createObject();
9385 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9386 mUSBControllers->push_back(newCtrl);
9387 }
9388
9389 /* USB device filters */
9390 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9391 if (FAILED(rc)) return rc;
9392
9393 // network adapters (establish array size first and apply defaults, to
9394 // ensure reading the same settings as we saved, since the list skips
9395 // adapters having defaults)
9396 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9397 size_t oldCount = mNetworkAdapters.size();
9398 if (newCount > oldCount)
9399 {
9400 mNetworkAdapters.resize(newCount);
9401 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9402 {
9403 unconst(mNetworkAdapters[slot]).createObject();
9404 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9405 }
9406 }
9407 else if (newCount < oldCount)
9408 mNetworkAdapters.resize(newCount);
9409 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9410 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9411 for (settings::NetworkAdaptersList::const_iterator
9412 it = data.llNetworkAdapters.begin();
9413 it != data.llNetworkAdapters.end();
9414 ++it)
9415 {
9416 const settings::NetworkAdapter &nic = *it;
9417
9418 /* slot uniqueness is guaranteed by XML Schema */
9419 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9420 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9421 if (FAILED(rc)) return rc;
9422 }
9423
9424 // serial ports (establish defaults first, to ensure reading the same
9425 // settings as we saved, since the list skips ports having defaults)
9426 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9427 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9428 for (settings::SerialPortsList::const_iterator
9429 it = data.llSerialPorts.begin();
9430 it != data.llSerialPorts.end();
9431 ++it)
9432 {
9433 const settings::SerialPort &s = *it;
9434
9435 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9436 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9437 if (FAILED(rc)) return rc;
9438 }
9439
9440 // parallel ports (establish defaults first, to ensure reading the same
9441 // settings as we saved, since the list skips ports having defaults)
9442 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9443 mParallelPorts[i]->i_applyDefaults();
9444 for (settings::ParallelPortsList::const_iterator
9445 it = data.llParallelPorts.begin();
9446 it != data.llParallelPorts.end();
9447 ++it)
9448 {
9449 const settings::ParallelPort &p = *it;
9450
9451 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9452 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9453 if (FAILED(rc)) return rc;
9454 }
9455
9456 /* Audio settings */
9457 rc = mAudioSettings->i_loadSettings(data.audioAdapter);
9458 if (FAILED(rc)) return rc;
9459
9460 /* storage controllers */
9461 rc = i_loadStorageControllers(data.storage,
9462 puuidRegistry,
9463 puuidSnapshot);
9464 if (FAILED(rc)) return rc;
9465
9466 /* Shared folders */
9467 for (settings::SharedFoldersList::const_iterator
9468 it = data.llSharedFolders.begin();
9469 it != data.llSharedFolders.end();
9470 ++it)
9471 {
9472 const settings::SharedFolder &sf = *it;
9473
9474 ComObjPtr<SharedFolder> sharedFolder;
9475 /* Check for double entries. Not allowed! */
9476 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9477 if (SUCCEEDED(rc))
9478 return setError(VBOX_E_OBJECT_IN_USE,
9479 tr("Shared folder named '%s' already exists"),
9480 sf.strName.c_str());
9481
9482 /* Create the new shared folder. Don't break on error. This will be
9483 * reported when the machine starts. */
9484 sharedFolder.createObject();
9485 rc = sharedFolder->init(i_getMachine(),
9486 sf.strName,
9487 sf.strHostPath,
9488 RT_BOOL(sf.fWritable),
9489 RT_BOOL(sf.fAutoMount),
9490 sf.strAutoMountPoint,
9491 false /* fFailOnError */);
9492 if (FAILED(rc)) return rc;
9493 mHWData->mSharedFolders.push_back(sharedFolder);
9494 }
9495
9496 // Clipboard
9497 mHWData->mClipboardMode = data.clipboardMode;
9498 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9499
9500 // drag'n'drop
9501 mHWData->mDnDMode = data.dndMode;
9502
9503 // guest settings
9504 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9505
9506 // IO settings
9507 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9508 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9509
9510 // Host PCI devices
9511 for (settings::HostPCIDeviceAttachmentList::const_iterator
9512 it = data.pciAttachments.begin();
9513 it != data.pciAttachments.end();
9514 ++it)
9515 {
9516 const settings::HostPCIDeviceAttachment &hpda = *it;
9517 ComObjPtr<PCIDeviceAttachment> pda;
9518
9519 pda.createObject();
9520 pda->i_loadSettings(this, hpda);
9521 mHWData->mPCIDeviceAssignments.push_back(pda);
9522 }
9523
9524 /*
9525 * (The following isn't really real hardware, but it lives in HWData
9526 * for reasons of convenience.)
9527 */
9528
9529#ifdef VBOX_WITH_GUEST_PROPS
9530 /* Guest properties (optional) */
9531
9532 /* Only load transient guest properties for configs which have saved
9533 * state, because there shouldn't be any for powered off VMs. The same
9534 * logic applies for snapshots, as offline snapshots shouldn't have
9535 * any such properties. They confuse the code in various places.
9536 * Note: can't rely on the machine state, as it isn't set yet. */
9537 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9538 /* apologies for the hacky unconst() usage, but this needs hacking
9539 * actually inconsistent settings into consistency, otherwise there
9540 * will be some corner cases where the inconsistency survives
9541 * surprisingly long without getting fixed, especially for snapshots
9542 * as there are no config changes. */
9543 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9544 for (settings::GuestPropertiesList::iterator
9545 it = llGuestProperties.begin();
9546 it != llGuestProperties.end();
9547 /*nothing*/)
9548 {
9549 const settings::GuestProperty &prop = *it;
9550 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9551 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9552 if ( fSkipTransientGuestProperties
9553 && ( fFlags & GUEST_PROP_F_TRANSIENT
9554 || fFlags & GUEST_PROP_F_TRANSRESET))
9555 {
9556 it = llGuestProperties.erase(it);
9557 continue;
9558 }
9559 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9560 mHWData->mGuestProperties[prop.strName] = property;
9561 ++it;
9562 }
9563#endif /* VBOX_WITH_GUEST_PROPS defined */
9564
9565 rc = i_loadDebugging(pDbg);
9566 if (FAILED(rc))
9567 return rc;
9568
9569 mHWData->mAutostart = *pAutostart;
9570
9571 /* default frontend */
9572 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9573 }
9574 catch (std::bad_alloc &)
9575 {
9576 return E_OUTOFMEMORY;
9577 }
9578
9579 AssertComRC(rc);
9580 return rc;
9581}
9582
9583/**
9584 * Called from i_loadHardware() to load the debugging settings of the
9585 * machine.
9586 *
9587 * @param pDbg Pointer to the settings.
9588 */
9589HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9590{
9591 mHWData->mDebugging = *pDbg;
9592 /* no more processing currently required, this will probably change. */
9593
9594 HRESULT rc = mGuestDebugControl->i_loadSettings(*pDbg);
9595 if (FAILED(rc)) return rc;
9596
9597 return S_OK;
9598}
9599
9600/**
9601 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9602 *
9603 * @param data storage settings.
9604 * @param puuidRegistry media registry ID to set media to or NULL;
9605 * see Machine::i_loadMachineDataFromSettings()
9606 * @param puuidSnapshot snapshot ID
9607 * @return
9608 */
9609HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9610 const Guid *puuidRegistry,
9611 const Guid *puuidSnapshot)
9612{
9613 AssertReturn(!i_isSessionMachine(), E_FAIL);
9614
9615 HRESULT rc = S_OK;
9616
9617 for (settings::StorageControllersList::const_iterator
9618 it = data.llStorageControllers.begin();
9619 it != data.llStorageControllers.end();
9620 ++it)
9621 {
9622 const settings::StorageController &ctlData = *it;
9623
9624 ComObjPtr<StorageController> pCtl;
9625 /* Try to find one with the name first. */
9626 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9627 if (SUCCEEDED(rc))
9628 return setError(VBOX_E_OBJECT_IN_USE,
9629 tr("Storage controller named '%s' already exists"),
9630 ctlData.strName.c_str());
9631
9632 pCtl.createObject();
9633 rc = pCtl->init(this,
9634 ctlData.strName,
9635 ctlData.storageBus,
9636 ctlData.ulInstance,
9637 ctlData.fBootable);
9638 if (FAILED(rc)) return rc;
9639
9640 mStorageControllers->push_back(pCtl);
9641
9642 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9643 if (FAILED(rc)) return rc;
9644
9645 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9646 if (FAILED(rc)) return rc;
9647
9648 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9649 if (FAILED(rc)) return rc;
9650
9651 /* Load the attached devices now. */
9652 rc = i_loadStorageDevices(pCtl,
9653 ctlData,
9654 puuidRegistry,
9655 puuidSnapshot);
9656 if (FAILED(rc)) return rc;
9657 }
9658
9659 return S_OK;
9660}
9661
9662/**
9663 * Called from i_loadStorageControllers for a controller's devices.
9664 *
9665 * @param aStorageController
9666 * @param data
9667 * @param puuidRegistry media registry ID to set media to or NULL; see
9668 * Machine::i_loadMachineDataFromSettings()
9669 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9670 * @return
9671 */
9672HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9673 const settings::StorageController &data,
9674 const Guid *puuidRegistry,
9675 const Guid *puuidSnapshot)
9676{
9677 HRESULT rc = S_OK;
9678
9679 /* paranoia: detect duplicate attachments */
9680 for (settings::AttachedDevicesList::const_iterator
9681 it = data.llAttachedDevices.begin();
9682 it != data.llAttachedDevices.end();
9683 ++it)
9684 {
9685 const settings::AttachedDevice &ad = *it;
9686
9687 for (settings::AttachedDevicesList::const_iterator it2 = it;
9688 it2 != data.llAttachedDevices.end();
9689 ++it2)
9690 {
9691 if (it == it2)
9692 continue;
9693
9694 const settings::AttachedDevice &ad2 = *it2;
9695
9696 if ( ad.lPort == ad2.lPort
9697 && ad.lDevice == ad2.lDevice)
9698 {
9699 return setError(E_FAIL,
9700 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9701 aStorageController->i_getName().c_str(),
9702 ad.lPort,
9703 ad.lDevice,
9704 mUserData->s.strName.c_str());
9705 }
9706 }
9707 }
9708
9709 for (settings::AttachedDevicesList::const_iterator
9710 it = data.llAttachedDevices.begin();
9711 it != data.llAttachedDevices.end();
9712 ++it)
9713 {
9714 const settings::AttachedDevice &dev = *it;
9715 ComObjPtr<Medium> medium;
9716
9717 switch (dev.deviceType)
9718 {
9719 case DeviceType_Floppy:
9720 case DeviceType_DVD:
9721 if (dev.strHostDriveSrc.isNotEmpty())
9722 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9723 false /* fRefresh */, medium);
9724 else
9725 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9726 dev.uuid,
9727 false /* fRefresh */,
9728 false /* aSetError */,
9729 medium);
9730 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9731 // This is not an error. The host drive or UUID might have vanished, so just go
9732 // ahead without this removeable medium attachment
9733 rc = S_OK;
9734 break;
9735
9736 case DeviceType_HardDisk:
9737 {
9738 /* find a hard disk by UUID */
9739 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9740 if (FAILED(rc))
9741 {
9742 if (i_isSnapshotMachine())
9743 {
9744 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9745 // so the user knows that the bad disk is in a snapshot somewhere
9746 com::ErrorInfo info;
9747 return setError(E_FAIL,
9748 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9749 puuidSnapshot->raw(),
9750 info.getText().raw());
9751 }
9752 else
9753 return rc;
9754 }
9755
9756 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9757
9758 if (medium->i_getType() == MediumType_Immutable)
9759 {
9760 if (i_isSnapshotMachine())
9761 return setError(E_FAIL,
9762 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9763 "of the virtual machine '%s' ('%s')"),
9764 medium->i_getLocationFull().c_str(),
9765 dev.uuid.raw(),
9766 puuidSnapshot->raw(),
9767 mUserData->s.strName.c_str(),
9768 mData->m_strConfigFileFull.c_str());
9769
9770 return setError(E_FAIL,
9771 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9772 medium->i_getLocationFull().c_str(),
9773 dev.uuid.raw(),
9774 mUserData->s.strName.c_str(),
9775 mData->m_strConfigFileFull.c_str());
9776 }
9777
9778 if (medium->i_getType() == MediumType_MultiAttach)
9779 {
9780 if (i_isSnapshotMachine())
9781 return setError(E_FAIL,
9782 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9783 "of the virtual machine '%s' ('%s')"),
9784 medium->i_getLocationFull().c_str(),
9785 dev.uuid.raw(),
9786 puuidSnapshot->raw(),
9787 mUserData->s.strName.c_str(),
9788 mData->m_strConfigFileFull.c_str());
9789
9790 return setError(E_FAIL,
9791 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9792 medium->i_getLocationFull().c_str(),
9793 dev.uuid.raw(),
9794 mUserData->s.strName.c_str(),
9795 mData->m_strConfigFileFull.c_str());
9796 }
9797
9798 if ( !i_isSnapshotMachine()
9799 && medium->i_getChildren().size() != 0
9800 )
9801 return setError(E_FAIL,
9802 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9803 "because it has %d differencing child hard disks"),
9804 medium->i_getLocationFull().c_str(),
9805 dev.uuid.raw(),
9806 mUserData->s.strName.c_str(),
9807 mData->m_strConfigFileFull.c_str(),
9808 medium->i_getChildren().size());
9809
9810 if (i_findAttachment(*mMediumAttachments.data(),
9811 medium))
9812 return setError(E_FAIL,
9813 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9814 medium->i_getLocationFull().c_str(),
9815 dev.uuid.raw(),
9816 mUserData->s.strName.c_str(),
9817 mData->m_strConfigFileFull.c_str());
9818
9819 break;
9820 }
9821
9822 default:
9823 return setError(E_FAIL,
9824 tr("Controller '%s' port %u unit %u has device with unknown type (%d) - virtual machine '%s' ('%s')"),
9825 data.strName.c_str(), dev.lPort, dev.lDevice, dev.deviceType,
9826 mUserData->s.strName.c_str(), mData->m_strConfigFileFull.c_str());
9827 }
9828
9829 if (FAILED(rc))
9830 break;
9831
9832 /* Bandwidth groups are loaded at this point. */
9833 ComObjPtr<BandwidthGroup> pBwGroup;
9834
9835 if (!dev.strBwGroup.isEmpty())
9836 {
9837 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9838 if (FAILED(rc))
9839 return setError(E_FAIL,
9840 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9841 medium->i_getLocationFull().c_str(),
9842 dev.strBwGroup.c_str(),
9843 mUserData->s.strName.c_str(),
9844 mData->m_strConfigFileFull.c_str());
9845 pBwGroup->i_reference();
9846 }
9847
9848 const Utf8Str controllerName = aStorageController->i_getName();
9849 ComObjPtr<MediumAttachment> pAttachment;
9850 pAttachment.createObject();
9851 rc = pAttachment->init(this,
9852 medium,
9853 controllerName,
9854 dev.lPort,
9855 dev.lDevice,
9856 dev.deviceType,
9857 false,
9858 dev.fPassThrough,
9859 dev.fTempEject,
9860 dev.fNonRotational,
9861 dev.fDiscard,
9862 dev.fHotPluggable,
9863 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9864 if (FAILED(rc)) break;
9865
9866 /* associate the medium with this machine and snapshot */
9867 if (!medium.isNull())
9868 {
9869 AutoCaller medCaller(medium);
9870 if (FAILED(medCaller.hrc())) return medCaller.hrc();
9871 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9872
9873 if (i_isSnapshotMachine())
9874 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9875 else
9876 rc = medium->i_addBackReference(mData->mUuid);
9877 /* If the medium->addBackReference fails it sets an appropriate
9878 * error message, so no need to do any guesswork here. */
9879
9880 if (puuidRegistry)
9881 // caller wants registry ID to be set on all attached media (OVF import case)
9882 medium->i_addRegistry(*puuidRegistry);
9883 }
9884
9885 if (FAILED(rc))
9886 break;
9887
9888 /* back up mMediumAttachments to let registeredInit() properly rollback
9889 * on failure (= limited accessibility) */
9890 i_setModified(IsModified_Storage);
9891 mMediumAttachments.backup();
9892 mMediumAttachments->push_back(pAttachment);
9893 }
9894
9895 return rc;
9896}
9897
9898/**
9899 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9900 *
9901 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9902 * @param aSnapshot where to return the found snapshot
9903 * @param aSetError true to set extended error info on failure
9904 */
9905HRESULT Machine::i_findSnapshotById(const Guid &aId,
9906 ComObjPtr<Snapshot> &aSnapshot,
9907 bool aSetError /* = false */)
9908{
9909 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9910
9911 if (!mData->mFirstSnapshot)
9912 {
9913 if (aSetError)
9914 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9915 return E_FAIL;
9916 }
9917
9918 if (aId.isZero())
9919 aSnapshot = mData->mFirstSnapshot;
9920 else
9921 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9922
9923 if (!aSnapshot)
9924 {
9925 if (aSetError)
9926 return setError(E_FAIL,
9927 tr("Could not find a snapshot with UUID {%s}"),
9928 aId.toString().c_str());
9929 return E_FAIL;
9930 }
9931
9932 return S_OK;
9933}
9934
9935/**
9936 * Returns the snapshot with the given name or fails of no such snapshot.
9937 *
9938 * @param strName snapshot name to find
9939 * @param aSnapshot where to return the found snapshot
9940 * @param aSetError true to set extended error info on failure
9941 */
9942HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9943 ComObjPtr<Snapshot> &aSnapshot,
9944 bool aSetError /* = false */)
9945{
9946 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9947
9948 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9949
9950 if (!mData->mFirstSnapshot)
9951 {
9952 if (aSetError)
9953 return setError(VBOX_E_OBJECT_NOT_FOUND,
9954 tr("This machine does not have any snapshots"));
9955 return VBOX_E_OBJECT_NOT_FOUND;
9956 }
9957
9958 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9959
9960 if (!aSnapshot)
9961 {
9962 if (aSetError)
9963 return setError(VBOX_E_OBJECT_NOT_FOUND,
9964 tr("Could not find a snapshot named '%s'"), strName.c_str());
9965 return VBOX_E_OBJECT_NOT_FOUND;
9966 }
9967
9968 return S_OK;
9969}
9970
9971/**
9972 * Returns a storage controller object with the given name.
9973 *
9974 * @param aName storage controller name to find
9975 * @param aStorageController where to return the found storage controller
9976 * @param aSetError true to set extended error info on failure
9977 */
9978HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9979 ComObjPtr<StorageController> &aStorageController,
9980 bool aSetError /* = false */)
9981{
9982 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9983
9984 for (StorageControllerList::const_iterator
9985 it = mStorageControllers->begin();
9986 it != mStorageControllers->end();
9987 ++it)
9988 {
9989 if ((*it)->i_getName() == aName)
9990 {
9991 aStorageController = (*it);
9992 return S_OK;
9993 }
9994 }
9995
9996 if (aSetError)
9997 return setError(VBOX_E_OBJECT_NOT_FOUND,
9998 tr("Could not find a storage controller named '%s'"),
9999 aName.c_str());
10000 return VBOX_E_OBJECT_NOT_FOUND;
10001}
10002
10003/**
10004 * Returns a USB controller object with the given name.
10005 *
10006 * @param aName USB controller name to find
10007 * @param aUSBController where to return the found USB controller
10008 * @param aSetError true to set extended error info on failure
10009 */
10010HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
10011 ComObjPtr<USBController> &aUSBController,
10012 bool aSetError /* = false */)
10013{
10014 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
10015
10016 for (USBControllerList::const_iterator
10017 it = mUSBControllers->begin();
10018 it != mUSBControllers->end();
10019 ++it)
10020 {
10021 if ((*it)->i_getName() == aName)
10022 {
10023 aUSBController = (*it);
10024 return S_OK;
10025 }
10026 }
10027
10028 if (aSetError)
10029 return setError(VBOX_E_OBJECT_NOT_FOUND,
10030 tr("Could not find a storage controller named '%s'"),
10031 aName.c_str());
10032 return VBOX_E_OBJECT_NOT_FOUND;
10033}
10034
10035/**
10036 * Returns the number of USB controller instance of the given type.
10037 *
10038 * @param enmType USB controller type.
10039 */
10040ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
10041{
10042 ULONG cCtrls = 0;
10043
10044 for (USBControllerList::const_iterator
10045 it = mUSBControllers->begin();
10046 it != mUSBControllers->end();
10047 ++it)
10048 {
10049 if ((*it)->i_getControllerType() == enmType)
10050 cCtrls++;
10051 }
10052
10053 return cCtrls;
10054}
10055
10056HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
10057 MediumAttachmentList &atts)
10058{
10059 AutoCaller autoCaller(this);
10060 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
10061
10062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10063
10064 for (MediumAttachmentList::const_iterator
10065 it = mMediumAttachments->begin();
10066 it != mMediumAttachments->end();
10067 ++it)
10068 {
10069 const ComObjPtr<MediumAttachment> &pAtt = *it;
10070 // should never happen, but deal with NULL pointers in the list.
10071 AssertContinue(!pAtt.isNull());
10072
10073 // getControllerName() needs caller+read lock
10074 AutoCaller autoAttCaller(pAtt);
10075 if (FAILED(autoAttCaller.hrc()))
10076 {
10077 atts.clear();
10078 return autoAttCaller.hrc();
10079 }
10080 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
10081
10082 if (pAtt->i_getControllerName() == aName)
10083 atts.push_back(pAtt);
10084 }
10085
10086 return S_OK;
10087}
10088
10089
10090/**
10091 * Helper for #i_saveSettings. Cares about renaming the settings directory and
10092 * file if the machine name was changed and about creating a new settings file
10093 * if this is a new machine.
10094 *
10095 * @note Must be never called directly but only from #saveSettings().
10096 */
10097HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
10098 bool *pfSettingsFileIsNew)
10099{
10100 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10101
10102 HRESULT rc = S_OK;
10103
10104 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
10105 /// @todo need to handle primary group change, too
10106
10107 /* attempt to rename the settings file if machine name is changed */
10108 if ( mUserData->s.fNameSync
10109 && mUserData.isBackedUp()
10110 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
10111 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
10112 )
10113 {
10114 bool dirRenamed = false;
10115 bool fileRenamed = false;
10116
10117 Utf8Str configFile, newConfigFile;
10118 Utf8Str configFilePrev, newConfigFilePrev;
10119 Utf8Str NVRAMFile, newNVRAMFile;
10120 Utf8Str configDir, newConfigDir;
10121
10122 do
10123 {
10124 int vrc = VINF_SUCCESS;
10125
10126 Utf8Str name = mUserData.backedUpData()->s.strName;
10127 Utf8Str newName = mUserData->s.strName;
10128 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
10129 if (group == "/")
10130 group.setNull();
10131 Utf8Str newGroup = mUserData->s.llGroups.front();
10132 if (newGroup == "/")
10133 newGroup.setNull();
10134
10135 configFile = mData->m_strConfigFileFull;
10136
10137 /* first, rename the directory if it matches the group and machine name */
10138 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
10139 /** @todo hack, make somehow use of ComposeMachineFilename */
10140 if (mUserData->s.fDirectoryIncludesUUID)
10141 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10142 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
10143 /** @todo hack, make somehow use of ComposeMachineFilename */
10144 if (mUserData->s.fDirectoryIncludesUUID)
10145 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10146 configDir = configFile;
10147 configDir.stripFilename();
10148 newConfigDir = configDir;
10149 if ( configDir.length() >= groupPlusName.length()
10150 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
10151 groupPlusName.c_str()))
10152 {
10153 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10154 Utf8Str newConfigBaseDir(newConfigDir);
10155 newConfigDir.append(newGroupPlusName);
10156 /* consistency: use \ if appropriate on the platform */
10157 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10158 /* new dir and old dir cannot be equal here because of 'if'
10159 * above and because name != newName */
10160 Assert(configDir != newConfigDir);
10161 if (!fSettingsFileIsNew)
10162 {
10163 /* perform real rename only if the machine is not new */
10164 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10165 if ( vrc == VERR_FILE_NOT_FOUND
10166 || vrc == VERR_PATH_NOT_FOUND)
10167 {
10168 /* create the parent directory, then retry renaming */
10169 Utf8Str parent(newConfigDir);
10170 parent.stripFilename();
10171 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10172 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10173 }
10174 if (RT_FAILURE(vrc))
10175 {
10176 rc = setErrorBoth(E_FAIL, vrc,
10177 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10178 configDir.c_str(),
10179 newConfigDir.c_str(),
10180 vrc);
10181 break;
10182 }
10183 /* delete subdirectories which are no longer needed */
10184 Utf8Str dir(configDir);
10185 dir.stripFilename();
10186 while (dir != newConfigBaseDir && dir != ".")
10187 {
10188 vrc = RTDirRemove(dir.c_str());
10189 if (RT_FAILURE(vrc))
10190 break;
10191 dir.stripFilename();
10192 }
10193 dirRenamed = true;
10194 }
10195 }
10196
10197 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10198
10199 /* then try to rename the settings file itself */
10200 if (newConfigFile != configFile)
10201 {
10202 /* get the path to old settings file in renamed directory */
10203 Assert(mData->m_strConfigFileFull == configFile);
10204 configFile.printf("%s%c%s",
10205 newConfigDir.c_str(),
10206 RTPATH_DELIMITER,
10207 RTPathFilename(mData->m_strConfigFileFull.c_str()));
10208 if (!fSettingsFileIsNew)
10209 {
10210 /* perform real rename only if the machine is not new */
10211 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10212 if (RT_FAILURE(vrc))
10213 {
10214 rc = setErrorBoth(E_FAIL, vrc,
10215 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10216 configFile.c_str(),
10217 newConfigFile.c_str(),
10218 vrc);
10219 break;
10220 }
10221 fileRenamed = true;
10222 configFilePrev = configFile;
10223 configFilePrev += "-prev";
10224 newConfigFilePrev = newConfigFile;
10225 newConfigFilePrev += "-prev";
10226 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10227 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
10228 if (NVRAMFile.isNotEmpty())
10229 {
10230 // in the NVRAM file path, replace the old directory with the new directory
10231 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
10232 {
10233 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
10234 NVRAMFile = newConfigDir + strNVRAMFile;
10235 }
10236 newNVRAMFile = newConfigFile;
10237 newNVRAMFile.stripSuffix();
10238 newNVRAMFile += ".nvram";
10239 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
10240 }
10241 }
10242 }
10243
10244 // update m_strConfigFileFull amd mConfigFile
10245 mData->m_strConfigFileFull = newConfigFile;
10246 // compute the relative path too
10247 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10248
10249 // store the old and new so that VirtualBox::i_saveSettings() can update
10250 // the media registry
10251 if ( mData->mRegistered
10252 && (configDir != newConfigDir || configFile != newConfigFile))
10253 {
10254 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10255
10256 if (pfNeedsGlobalSaveSettings)
10257 *pfNeedsGlobalSaveSettings = true;
10258 }
10259
10260 // in the saved state file path, replace the old directory with the new directory
10261 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10262 {
10263 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
10264 mSSData->strStateFilePath = newConfigDir + strStateFileName;
10265 }
10266 if (newNVRAMFile.isNotEmpty())
10267 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
10268
10269 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
10270 if (mData->mFirstSnapshot)
10271 {
10272 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10273 newConfigDir.c_str());
10274 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
10275 newConfigDir.c_str());
10276 }
10277 }
10278 while (0);
10279
10280 if (FAILED(rc))
10281 {
10282 /* silently try to rename everything back */
10283 if (fileRenamed)
10284 {
10285 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10286 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10287 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
10288 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
10289 }
10290 if (dirRenamed)
10291 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10292 }
10293
10294 if (FAILED(rc)) return rc;
10295 }
10296
10297 if (fSettingsFileIsNew)
10298 {
10299 /* create a virgin config file */
10300 int vrc = VINF_SUCCESS;
10301
10302 /* ensure the settings directory exists */
10303 Utf8Str path(mData->m_strConfigFileFull);
10304 path.stripFilename();
10305 if (!RTDirExists(path.c_str()))
10306 {
10307 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10308 if (RT_FAILURE(vrc))
10309 {
10310 return setErrorBoth(E_FAIL, vrc,
10311 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10312 path.c_str(),
10313 vrc);
10314 }
10315 }
10316
10317 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10318 path = mData->m_strConfigFileFull;
10319 RTFILE f = NIL_RTFILE;
10320 vrc = RTFileOpen(&f, path.c_str(),
10321 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10322 if (RT_FAILURE(vrc))
10323 return setErrorBoth(E_FAIL, vrc,
10324 tr("Could not create the settings file '%s' (%Rrc)"),
10325 path.c_str(),
10326 vrc);
10327 RTFileClose(f);
10328 }
10329 if (pfSettingsFileIsNew)
10330 *pfSettingsFileIsNew = fSettingsFileIsNew;
10331
10332 return rc;
10333}
10334
10335/**
10336 * Saves and commits machine data, user data and hardware data.
10337 *
10338 * Note that on failure, the data remains uncommitted.
10339 *
10340 * @a aFlags may combine the following flags:
10341 *
10342 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10343 * Used when saving settings after an operation that makes them 100%
10344 * correspond to the settings from the current snapshot.
10345 * - SaveS_Force: settings will be saved without doing a deep compare of the
10346 * settings structures. This is used when this is called because snapshots
10347 * have changed to avoid the overhead of the deep compare.
10348 *
10349 * @note Must be called from under this object's write lock. Locks children for
10350 * writing.
10351 *
10352 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10353 * initialized to false and that will be set to true by this function if
10354 * the caller must invoke VirtualBox::i_saveSettings() because the global
10355 * settings have changed. This will happen if a machine rename has been
10356 * saved and the global machine and media registries will therefore need
10357 * updating.
10358 * @param alock Reference to the lock for this machine object.
10359 * @param aFlags Flags.
10360 */
10361HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10362 AutoWriteLock &alock,
10363 int aFlags /*= 0*/)
10364{
10365 LogFlowThisFuncEnter();
10366
10367 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10368
10369 /* make sure child objects are unable to modify the settings while we are
10370 * saving them */
10371 i_ensureNoStateDependencies(alock);
10372
10373 AssertReturn(!i_isSnapshotMachine(),
10374 E_FAIL);
10375
10376 if (!mData->mAccessible)
10377 return setError(VBOX_E_INVALID_VM_STATE,
10378 tr("The machine is not accessible, so cannot save settings"));
10379
10380 HRESULT rc = S_OK;
10381 PCVBOXCRYPTOIF pCryptoIf = NULL;
10382 const char *pszPassword = NULL;
10383 SecretKey *pKey = NULL;
10384
10385#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10386 if (mData->mstrKeyId.isNotEmpty())
10387 {
10388 /* VM is going to be encrypted. */
10389 alock.release(); /** @todo Revise the locking. */
10390 rc = mParent->i_retainCryptoIf(&pCryptoIf);
10391 alock.acquire();
10392 if (FAILED(rc)) return rc; /* Error is set. */
10393
10394 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
10395 if (RT_SUCCESS(vrc))
10396 pszPassword = (const char *)pKey->getKeyBuffer();
10397 else
10398 {
10399 mParent->i_releaseCryptoIf(pCryptoIf);
10400 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
10401 tr("Failed to retain VM encryption password using ID '%s' with %Rrc"),
10402 mData->mstrKeyId.c_str(), vrc);
10403 }
10404 }
10405#else
10406 RT_NOREF(pKey);
10407#endif
10408
10409 bool fNeedsWrite = false;
10410 bool fSettingsFileIsNew = false;
10411
10412 /* First, prepare to save settings. It will care about renaming the
10413 * settings directory and file if the machine name was changed and about
10414 * creating a new settings file if this is a new machine. */
10415 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings,
10416 &fSettingsFileIsNew);
10417 if (FAILED(rc))
10418 {
10419#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10420 if (pCryptoIf)
10421 {
10422 alock.release(); /** @todo Revise the locking. */
10423 mParent->i_releaseCryptoIf(pCryptoIf);
10424 alock.acquire();
10425 }
10426 if (pKey)
10427 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10428#endif
10429 return rc;
10430 }
10431
10432 // keep a pointer to the current settings structures
10433 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10434 settings::MachineConfigFile *pNewConfig = NULL;
10435
10436 try
10437 {
10438 // make a fresh one to have everyone write stuff into
10439 pNewConfig = new settings::MachineConfigFile(NULL);
10440 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10441#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10442 pNewConfig->strKeyId = mData->mstrKeyId;
10443 pNewConfig->strKeyStore = mData->mstrKeyStore;
10444#endif
10445
10446 // now go and copy all the settings data from COM to the settings structures
10447 // (this calls i_saveSettings() on all the COM objects in the machine)
10448 i_copyMachineDataToSettings(*pNewConfig);
10449
10450 if (aFlags & SaveS_ResetCurStateModified)
10451 {
10452 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10453 mData->mCurrentStateModified = FALSE;
10454 fNeedsWrite = true; // always, no need to compare
10455 }
10456 else if (aFlags & SaveS_Force)
10457 {
10458 fNeedsWrite = true; // always, no need to compare
10459 }
10460 else
10461 {
10462 if (!mData->mCurrentStateModified)
10463 {
10464 // do a deep compare of the settings that we just saved with the settings
10465 // previously stored in the config file; this invokes MachineConfigFile::operator==
10466 // which does a deep compare of all the settings, which is expensive but less expensive
10467 // than writing out XML in vain
10468 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10469
10470 // could still be modified if any settings changed
10471 mData->mCurrentStateModified = fAnySettingsChanged;
10472
10473 fNeedsWrite = fAnySettingsChanged;
10474 }
10475 else
10476 fNeedsWrite = true;
10477 }
10478
10479 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10480
10481 if (fNeedsWrite)
10482 {
10483 // now spit it all out!
10484 pNewConfig->write(mData->m_strConfigFileFull, pCryptoIf, pszPassword);
10485 if (aFlags & SaveS_RemoveBackup)
10486 i_deleteFile(mData->m_strConfigFileFull + "-prev", true /* fIgnoreFailures */);
10487 }
10488
10489 mData->pMachineConfigFile = pNewConfig;
10490 delete pOldConfig;
10491 i_commit();
10492
10493 // after saving settings, we are no longer different from the XML on disk
10494 mData->flModifications = 0;
10495 }
10496 catch (HRESULT err)
10497 {
10498 // we assume that error info is set by the thrower
10499 rc = err;
10500
10501 // delete any newly created settings file
10502 if (fSettingsFileIsNew)
10503 i_deleteFile(mData->m_strConfigFileFull, true /* fIgnoreFailures */);
10504
10505 // restore old config
10506 delete pNewConfig;
10507 mData->pMachineConfigFile = pOldConfig;
10508 }
10509 catch (...)
10510 {
10511 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10512 }
10513
10514#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10515 if (pCryptoIf)
10516 {
10517 alock.release(); /** @todo Revise the locking. */
10518 mParent->i_releaseCryptoIf(pCryptoIf);
10519 alock.acquire();
10520 }
10521 if (pKey)
10522 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10523#endif
10524
10525 if (fNeedsWrite)
10526 {
10527 /* Fire the data change event, even on failure (since we've already
10528 * committed all data). This is done only for SessionMachines because
10529 * mutable Machine instances are always not registered (i.e. private
10530 * to the client process that creates them) and thus don't need to
10531 * inform callbacks. */
10532 if (i_isSessionMachine())
10533 mParent->i_onMachineDataChanged(mData->mUuid);
10534 }
10535
10536 LogFlowThisFunc(("rc=%08X\n", rc));
10537 LogFlowThisFuncLeave();
10538 return rc;
10539}
10540
10541/**
10542 * Implementation for saving the machine settings into the given
10543 * settings::MachineConfigFile instance. This copies machine extradata
10544 * from the previous machine config file in the instance data, if any.
10545 *
10546 * This gets called from two locations:
10547 *
10548 * -- Machine::i_saveSettings(), during the regular XML writing;
10549 *
10550 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10551 * exported to OVF and we write the VirtualBox proprietary XML
10552 * into a <vbox:Machine> tag.
10553 *
10554 * This routine fills all the fields in there, including snapshots, *except*
10555 * for the following:
10556 *
10557 * -- fCurrentStateModified. There is some special logic associated with that.
10558 *
10559 * The caller can then call MachineConfigFile::write() or do something else
10560 * with it.
10561 *
10562 * Caller must hold the machine lock!
10563 *
10564 * This throws XML errors and HRESULT, so the caller must have a catch block!
10565 */
10566void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10567{
10568 // deep copy extradata, being extra careful with self assignment (the STL
10569 // map assignment on Mac OS X clang based Xcode isn't checking)
10570 if (&config != mData->pMachineConfigFile)
10571 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10572
10573 config.uuid = mData->mUuid;
10574
10575 // copy name, description, OS type, teleport, UTC etc.
10576 config.machineUserData = mUserData->s;
10577
10578#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10579 config.strStateKeyId = mSSData->strStateKeyId;
10580 config.strStateKeyStore = mSSData->strStateKeyStore;
10581 config.strLogKeyId = mData->mstrLogKeyId;
10582 config.strLogKeyStore = mData->mstrLogKeyStore;
10583#endif
10584
10585 if ( mData->mMachineState == MachineState_Saved
10586 || mData->mMachineState == MachineState_AbortedSaved
10587 || mData->mMachineState == MachineState_Restoring
10588 // when doing certain snapshot operations we may or may not have
10589 // a saved state in the current state, so keep everything as is
10590 || ( ( mData->mMachineState == MachineState_Snapshotting
10591 || mData->mMachineState == MachineState_DeletingSnapshot
10592 || mData->mMachineState == MachineState_RestoringSnapshot)
10593 && (!mSSData->strStateFilePath.isEmpty())
10594 )
10595 )
10596 {
10597 Assert(!mSSData->strStateFilePath.isEmpty());
10598 /* try to make the file name relative to the settings file dir */
10599 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10600 }
10601 else
10602 {
10603 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10604 config.strStateFile.setNull();
10605 }
10606
10607 if (mData->mCurrentSnapshot)
10608 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10609 else
10610 config.uuidCurrentSnapshot.clear();
10611
10612 config.timeLastStateChange = mData->mLastStateChange;
10613 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10614 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10615
10616 HRESULT hrc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart, config.recordingSettings);
10617 if (FAILED(hrc)) throw hrc;
10618
10619 // save machine's media registry if this is VirtualBox 4.0 or later
10620 if (config.canHaveOwnMediaRegistry())
10621 {
10622 // determine machine folder
10623 Utf8Str strMachineFolder = i_getSettingsFileFull();
10624 strMachineFolder.stripFilename();
10625 mParent->i_saveMediaRegistry(config.mediaRegistry,
10626 i_getId(), // only media with registry ID == machine UUID
10627 strMachineFolder);
10628 // this throws HRESULT
10629 }
10630
10631 // save snapshots
10632 hrc = i_saveAllSnapshots(config);
10633 if (FAILED(hrc)) throw hrc;
10634}
10635
10636/**
10637 * Saves all snapshots of the machine into the given machine config file. Called
10638 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10639 * @param config
10640 * @return
10641 */
10642HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10643{
10644 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10645
10646 HRESULT rc = S_OK;
10647
10648 try
10649 {
10650 config.llFirstSnapshot.clear();
10651
10652 if (mData->mFirstSnapshot)
10653 {
10654 // the settings use a list for "the first snapshot"
10655 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10656
10657 // get reference to the snapshot on the list and work on that
10658 // element straight in the list to avoid excessive copying later
10659 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10660 if (FAILED(rc)) throw rc;
10661 }
10662
10663// if (mType == IsSessionMachine)
10664// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10665
10666 }
10667 catch (HRESULT err)
10668 {
10669 /* we assume that error info is set by the thrower */
10670 rc = err;
10671 }
10672 catch (...)
10673 {
10674 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10675 }
10676
10677 return rc;
10678}
10679
10680/**
10681 * Saves the VM hardware configuration. It is assumed that the
10682 * given node is empty.
10683 *
10684 * @param data Reference to the settings object for the hardware config.
10685 * @param pDbg Pointer to the settings object for the debugging config
10686 * which happens to live in mHWData.
10687 * @param pAutostart Pointer to the settings object for the autostart config
10688 * which happens to live in mHWData.
10689 * @param recording Reference to reecording settings.
10690 */
10691HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10692 settings::Autostart *pAutostart, settings::RecordingSettings &recording)
10693{
10694 HRESULT rc = S_OK;
10695
10696 try
10697 {
10698 /* The hardware version attribute (optional).
10699 Automatically upgrade from 1 to current default hardware version
10700 when there is no saved state. (ugly!) */
10701 if ( mHWData->mHWVersion == "1"
10702 && mSSData->strStateFilePath.isEmpty()
10703 )
10704 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10705
10706 data.strVersion = mHWData->mHWVersion;
10707 data.uuid = mHWData->mHardwareUUID;
10708
10709 // CPU
10710 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10711 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10712 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10713 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10714 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10715 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10716 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10717 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10718 data.fPAE = !!mHWData->mPAEEnabled;
10719 data.enmLongMode = mHWData->mLongMode;
10720 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10721 data.fAPIC = !!mHWData->mAPIC;
10722 data.fX2APIC = !!mHWData->mX2APIC;
10723 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10724 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10725 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10726 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10727 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10728 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10729 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10730 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10731 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10732 data.cCPUs = mHWData->mCPUCount;
10733 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10734 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10735 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10736 data.strCpuProfile = mHWData->mCpuProfile;
10737
10738 data.llCpus.clear();
10739 if (data.fCpuHotPlug)
10740 {
10741 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10742 {
10743 if (mHWData->mCPUAttached[idx])
10744 {
10745 settings::Cpu cpu;
10746 cpu.ulId = idx;
10747 data.llCpus.push_back(cpu);
10748 }
10749 }
10750 }
10751
10752 /* Standard and Extended CPUID leafs. */
10753 data.llCpuIdLeafs.clear();
10754 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10755
10756 // memory
10757 data.ulMemorySizeMB = mHWData->mMemorySize;
10758 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10759
10760 // firmware
10761 data.firmwareType = mHWData->mFirmwareType;
10762
10763 // HID
10764 data.pointingHIDType = mHWData->mPointingHIDType;
10765 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10766
10767 // chipset
10768 data.chipsetType = mHWData->mChipsetType;
10769
10770 // iommu
10771 data.iommuType = mHWData->mIommuType;
10772
10773 // paravirt
10774 data.paravirtProvider = mHWData->mParavirtProvider;
10775 data.strParavirtDebug = mHWData->mParavirtDebug;
10776
10777 // emulated USB card reader
10778 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10779
10780 // HPET
10781 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10782
10783 // boot order
10784 data.mapBootOrder.clear();
10785 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10786 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10787
10788 /* VRDEServer settings (optional) */
10789 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10790 if (FAILED(rc)) throw rc;
10791
10792 /* BIOS settings (required) */
10793 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10794 if (FAILED(rc)) throw rc;
10795
10796 /* Recording settings. */
10797 rc = mRecordingSettings->i_saveSettings(recording);
10798 if (FAILED(rc)) throw rc;
10799
10800 /* Trusted Platform Module settings (required) */
10801 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10802 if (FAILED(rc)) throw rc;
10803
10804 /* NVRAM settings (required) */
10805 rc = mNvramStore->i_saveSettings(data.nvramSettings);
10806 if (FAILED(rc)) throw rc;
10807
10808 /* GraphicsAdapter settings (required) */
10809 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10810 if (FAILED(rc)) throw rc;
10811
10812 /* USB Controller (required) */
10813 data.usbSettings.llUSBControllers.clear();
10814 for (USBControllerList::const_iterator
10815 it = mUSBControllers->begin();
10816 it != mUSBControllers->end();
10817 ++it)
10818 {
10819 ComObjPtr<USBController> ctrl = *it;
10820 settings::USBController settingsCtrl;
10821
10822 settingsCtrl.strName = ctrl->i_getName();
10823 settingsCtrl.enmType = ctrl->i_getControllerType();
10824
10825 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10826 }
10827
10828 /* USB device filters (required) */
10829 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10830 if (FAILED(rc)) throw rc;
10831
10832 /* Network adapters (required) */
10833 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10834 data.llNetworkAdapters.clear();
10835 /* Write out only the nominal number of network adapters for this
10836 * chipset type. Since Machine::commit() hasn't been called there
10837 * may be extra NIC settings in the vector. */
10838 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10839 {
10840 settings::NetworkAdapter nic;
10841 nic.ulSlot = (uint32_t)slot;
10842 /* paranoia check... must not be NULL, but must not crash either. */
10843 if (mNetworkAdapters[slot])
10844 {
10845 if (mNetworkAdapters[slot]->i_hasDefaults())
10846 continue;
10847
10848 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10849 if (FAILED(rc)) throw rc;
10850
10851 data.llNetworkAdapters.push_back(nic);
10852 }
10853 }
10854
10855 /* Serial ports */
10856 data.llSerialPorts.clear();
10857 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10858 {
10859 if (mSerialPorts[slot]->i_hasDefaults())
10860 continue;
10861
10862 settings::SerialPort s;
10863 s.ulSlot = slot;
10864 rc = mSerialPorts[slot]->i_saveSettings(s);
10865 if (FAILED(rc)) return rc;
10866
10867 data.llSerialPorts.push_back(s);
10868 }
10869
10870 /* Parallel ports */
10871 data.llParallelPorts.clear();
10872 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10873 {
10874 if (mParallelPorts[slot]->i_hasDefaults())
10875 continue;
10876
10877 settings::ParallelPort p;
10878 p.ulSlot = slot;
10879 rc = mParallelPorts[slot]->i_saveSettings(p);
10880 if (FAILED(rc)) return rc;
10881
10882 data.llParallelPorts.push_back(p);
10883 }
10884
10885 /* Audio settings */
10886 rc = mAudioSettings->i_saveSettings(data.audioAdapter);
10887 if (FAILED(rc)) return rc;
10888
10889 rc = i_saveStorageControllers(data.storage);
10890 if (FAILED(rc)) return rc;
10891
10892 /* Shared folders */
10893 data.llSharedFolders.clear();
10894 for (HWData::SharedFolderList::const_iterator
10895 it = mHWData->mSharedFolders.begin();
10896 it != mHWData->mSharedFolders.end();
10897 ++it)
10898 {
10899 SharedFolder *pSF = *it;
10900 AutoCaller sfCaller(pSF);
10901 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10902 settings::SharedFolder sf;
10903 sf.strName = pSF->i_getName();
10904 sf.strHostPath = pSF->i_getHostPath();
10905 sf.fWritable = !!pSF->i_isWritable();
10906 sf.fAutoMount = !!pSF->i_isAutoMounted();
10907 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10908
10909 data.llSharedFolders.push_back(sf);
10910 }
10911
10912 // clipboard
10913 data.clipboardMode = mHWData->mClipboardMode;
10914 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10915
10916 // drag'n'drop
10917 data.dndMode = mHWData->mDnDMode;
10918
10919 /* Guest */
10920 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10921
10922 // IO settings
10923 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10924 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10925
10926 /* BandwidthControl (required) */
10927 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10928 if (FAILED(rc)) throw rc;
10929
10930 /* Host PCI devices */
10931 data.pciAttachments.clear();
10932 for (HWData::PCIDeviceAssignmentList::const_iterator
10933 it = mHWData->mPCIDeviceAssignments.begin();
10934 it != mHWData->mPCIDeviceAssignments.end();
10935 ++it)
10936 {
10937 ComObjPtr<PCIDeviceAttachment> pda = *it;
10938 settings::HostPCIDeviceAttachment hpda;
10939
10940 rc = pda->i_saveSettings(hpda);
10941 if (FAILED(rc)) throw rc;
10942
10943 data.pciAttachments.push_back(hpda);
10944 }
10945
10946 // guest properties
10947 data.llGuestProperties.clear();
10948#ifdef VBOX_WITH_GUEST_PROPS
10949 for (HWData::GuestPropertyMap::const_iterator
10950 it = mHWData->mGuestProperties.begin();
10951 it != mHWData->mGuestProperties.end();
10952 ++it)
10953 {
10954 HWData::GuestProperty property = it->second;
10955
10956 /* Remove transient guest properties at shutdown unless we
10957 * are saving state. Note that restoring snapshot intentionally
10958 * keeps them, they will be removed if appropriate once the final
10959 * machine state is set (as crashes etc. need to work). */
10960 if ( ( mData->mMachineState == MachineState_PoweredOff
10961 || mData->mMachineState == MachineState_Aborted
10962 || mData->mMachineState == MachineState_Teleported)
10963 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10964 continue;
10965 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10966 prop.strName = it->first;
10967 prop.strValue = property.strValue;
10968 prop.timestamp = (uint64_t)property.mTimestamp;
10969 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10970 GuestPropWriteFlags(property.mFlags, szFlags);
10971 prop.strFlags = szFlags;
10972
10973 data.llGuestProperties.push_back(prop);
10974 }
10975
10976 /* I presume this doesn't require a backup(). */
10977 mData->mGuestPropertiesModified = FALSE;
10978#endif /* VBOX_WITH_GUEST_PROPS defined */
10979
10980 rc = mGuestDebugControl->i_saveSettings(mHWData->mDebugging);
10981 if (FAILED(rc)) throw rc;
10982
10983 *pDbg = mHWData->mDebugging; /// @todo r=aeichner: Move this to guest debug control. */
10984 *pAutostart = mHWData->mAutostart;
10985
10986 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10987 }
10988 catch (std::bad_alloc &)
10989 {
10990 return E_OUTOFMEMORY;
10991 }
10992
10993 AssertComRC(rc);
10994 return rc;
10995}
10996
10997/**
10998 * Saves the storage controller configuration.
10999 *
11000 * @param data storage settings.
11001 */
11002HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
11003{
11004 data.llStorageControllers.clear();
11005
11006 for (StorageControllerList::const_iterator
11007 it = mStorageControllers->begin();
11008 it != mStorageControllers->end();
11009 ++it)
11010 {
11011 HRESULT rc;
11012 ComObjPtr<StorageController> pCtl = *it;
11013
11014 settings::StorageController ctl;
11015 ctl.strName = pCtl->i_getName();
11016 ctl.controllerType = pCtl->i_getControllerType();
11017 ctl.storageBus = pCtl->i_getStorageBus();
11018 ctl.ulInstance = pCtl->i_getInstance();
11019 ctl.fBootable = pCtl->i_getBootable();
11020
11021 /* Save the port count. */
11022 ULONG portCount;
11023 rc = pCtl->COMGETTER(PortCount)(&portCount);
11024 ComAssertComRCRet(rc, rc);
11025 ctl.ulPortCount = portCount;
11026
11027 /* Save fUseHostIOCache */
11028 BOOL fUseHostIOCache;
11029 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
11030 ComAssertComRCRet(rc, rc);
11031 ctl.fUseHostIOCache = !!fUseHostIOCache;
11032
11033 /* save the devices now. */
11034 rc = i_saveStorageDevices(pCtl, ctl);
11035 ComAssertComRCRet(rc, rc);
11036
11037 data.llStorageControllers.push_back(ctl);
11038 }
11039
11040 return S_OK;
11041}
11042
11043/**
11044 * Saves the hard disk configuration.
11045 */
11046HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
11047 settings::StorageController &data)
11048{
11049 MediumAttachmentList atts;
11050
11051 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
11052 if (FAILED(rc)) return rc;
11053
11054 data.llAttachedDevices.clear();
11055 for (MediumAttachmentList::const_iterator
11056 it = atts.begin();
11057 it != atts.end();
11058 ++it)
11059 {
11060 settings::AttachedDevice dev;
11061 IMediumAttachment *iA = *it;
11062 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
11063 Medium *pMedium = pAttach->i_getMedium();
11064
11065 dev.deviceType = pAttach->i_getType();
11066 dev.lPort = pAttach->i_getPort();
11067 dev.lDevice = pAttach->i_getDevice();
11068 dev.fPassThrough = pAttach->i_getPassthrough();
11069 dev.fHotPluggable = pAttach->i_getHotPluggable();
11070 if (pMedium)
11071 {
11072 if (pMedium->i_isHostDrive())
11073 dev.strHostDriveSrc = pMedium->i_getLocationFull();
11074 else
11075 dev.uuid = pMedium->i_getId();
11076 dev.fTempEject = pAttach->i_getTempEject();
11077 dev.fNonRotational = pAttach->i_getNonRotational();
11078 dev.fDiscard = pAttach->i_getDiscard();
11079 }
11080
11081 dev.strBwGroup = pAttach->i_getBandwidthGroup();
11082
11083 data.llAttachedDevices.push_back(dev);
11084 }
11085
11086 return S_OK;
11087}
11088
11089/**
11090 * Saves machine state settings as defined by aFlags
11091 * (SaveSTS_* values).
11092 *
11093 * @param aFlags Combination of SaveSTS_* flags.
11094 *
11095 * @note Locks objects for writing.
11096 */
11097HRESULT Machine::i_saveStateSettings(int aFlags)
11098{
11099 if (aFlags == 0)
11100 return S_OK;
11101
11102 AutoCaller autoCaller(this);
11103 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
11104
11105 /* This object's write lock is also necessary to serialize file access
11106 * (prevent concurrent reads and writes) */
11107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11108
11109 HRESULT rc = S_OK;
11110
11111 Assert(mData->pMachineConfigFile);
11112
11113 try
11114 {
11115 if (aFlags & SaveSTS_CurStateModified)
11116 mData->pMachineConfigFile->fCurrentStateModified = true;
11117
11118 if (aFlags & SaveSTS_StateFilePath)
11119 {
11120 if (!mSSData->strStateFilePath.isEmpty())
11121 /* try to make the file name relative to the settings file dir */
11122 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
11123 else
11124 mData->pMachineConfigFile->strStateFile.setNull();
11125 }
11126
11127 if (aFlags & SaveSTS_StateTimeStamp)
11128 {
11129 Assert( mData->mMachineState != MachineState_Aborted
11130 || mSSData->strStateFilePath.isEmpty());
11131
11132 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
11133
11134 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
11135 || mData->mMachineState == MachineState_AbortedSaved);
11136/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
11137 }
11138
11139 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
11140 }
11141 catch (...)
11142 {
11143 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
11144 }
11145
11146 return rc;
11147}
11148
11149/**
11150 * Ensures that the given medium is added to a media registry. If this machine
11151 * was created with 4.0 or later, then the machine registry is used. Otherwise
11152 * the global VirtualBox media registry is used.
11153 *
11154 * Caller must NOT hold machine lock, media tree or any medium locks!
11155 *
11156 * @param pMedium
11157 */
11158void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
11159{
11160 /* Paranoia checks: do not hold machine or media tree locks. */
11161 AssertReturnVoid(!isWriteLockOnCurrentThread());
11162 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
11163
11164 ComObjPtr<Medium> pBase;
11165 {
11166 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11167 pBase = pMedium->i_getBase();
11168 }
11169
11170 /* Paranoia checks: do not hold medium locks. */
11171 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
11172 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
11173
11174 // decide which medium registry to use now that the medium is attached:
11175 Guid uuid;
11176 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
11177 if (fCanHaveOwnMediaRegistry)
11178 // machine XML is VirtualBox 4.0 or higher:
11179 uuid = i_getId(); // machine UUID
11180 else
11181 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
11182
11183 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
11184 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11185 if (pMedium->i_addRegistry(uuid))
11186 mParent->i_markRegistryModified(uuid);
11187
11188 /* For more complex hard disk structures it can happen that the base
11189 * medium isn't yet associated with any medium registry. Do that now. */
11190 if (pMedium != pBase)
11191 {
11192 /* Tree lock needed by Medium::addRegistryAll. */
11193 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11194 if (fCanHaveOwnMediaRegistry && pBase->i_removeRegistryAll(mParent->i_getGlobalRegistryId()))
11195 {
11196 treeLock.release();
11197 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11198 treeLock.acquire();
11199 }
11200 if (pBase->i_addRegistryAll(uuid))
11201 {
11202 treeLock.release();
11203 mParent->i_markRegistryModified(uuid);
11204 }
11205 }
11206}
11207
11208/**
11209 * Physically deletes a file belonging to a machine.
11210 *
11211 * @returns HRESULT
11212 * @retval VBOX_E_FILE_ERROR on failure.
11213 * @param strFile File to delete.
11214 * @param fIgnoreFailures Whether to ignore deletion failures. Defaults to \c false.
11215 * VERR_FILE_NOT_FOUND and VERR_PATH_NOT_FOUND always will be ignored.
11216 * @param strWhat File hint which will be used when setting an error. Optional.
11217 * @param prc Where to return IPRT's error code on failure. Optional and can be NULL.
11218 */
11219HRESULT Machine::i_deleteFile(const Utf8Str &strFile, bool fIgnoreFailures /* = false */,
11220 const Utf8Str &strWhat /* = "" */, int *prc /* = NULL */)
11221{
11222 AssertReturn(strFile.isNotEmpty(), E_INVALIDARG);
11223
11224 HRESULT hrc = S_OK;
11225
11226 LogFunc(("Deleting file '%s'\n", strFile.c_str()));
11227
11228 int vrc = RTFileDelete(strFile.c_str());
11229 if (RT_FAILURE(vrc))
11230 {
11231 if ( !fIgnoreFailures
11232 /* Don't (externally) bitch about stuff which doesn't exist. */
11233 && ( vrc != VERR_FILE_NOT_FOUND
11234 && vrc != VERR_PATH_NOT_FOUND
11235 )
11236 )
11237 {
11238 LogRel(("Deleting file '%s' failed: %Rrc\n", strFile.c_str(), vrc));
11239
11240 Utf8StrFmt strError("Error deleting %s '%s' (%Rrc)",
11241 strWhat.isEmpty() ? tr("file") : strWhat.c_str(), strFile.c_str(), vrc);
11242 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, strError.c_str(),
11243 strFile.c_str(), vrc);
11244 }
11245
11246 if (prc)
11247 *prc = vrc;
11248 }
11249
11250 return hrc;
11251}
11252
11253/**
11254 * Creates differencing hard disks for all normal hard disks attached to this
11255 * machine and a new set of attachments to refer to created disks.
11256 *
11257 * Used when taking a snapshot or when deleting the current state. Gets called
11258 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
11259 *
11260 * This method assumes that mMediumAttachments contains the original hard disk
11261 * attachments it needs to create diffs for. On success, these attachments will
11262 * be replaced with the created diffs.
11263 *
11264 * Attachments with non-normal hard disks are left as is.
11265 *
11266 * If @a aOnline is @c false then the original hard disks that require implicit
11267 * diffs will be locked for reading. Otherwise it is assumed that they are
11268 * already locked for writing (when the VM was started). Note that in the latter
11269 * case it is responsibility of the caller to lock the newly created diffs for
11270 * writing if this method succeeds.
11271 *
11272 * @param aProgress Progress object to run (must contain at least as
11273 * many operations left as the number of hard disks
11274 * attached).
11275 * @param aWeight Weight of this operation.
11276 * @param aOnline Whether the VM was online prior to this operation.
11277 *
11278 * @note The progress object is not marked as completed, neither on success nor
11279 * on failure. This is a responsibility of the caller.
11280 *
11281 * @note Locks this object and the media tree for writing.
11282 */
11283HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
11284 ULONG aWeight,
11285 bool aOnline)
11286{
11287 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11288
11289 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
11290 AssertReturn(!!pProgressControl, E_INVALIDARG);
11291
11292 AutoCaller autoCaller(this);
11293 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
11294
11295 AutoMultiWriteLock2 alock(this->lockHandle(),
11296 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11297
11298 /* must be in a protective state because we release the lock below */
11299 AssertReturn( mData->mMachineState == MachineState_Snapshotting
11300 || mData->mMachineState == MachineState_OnlineSnapshotting
11301 || mData->mMachineState == MachineState_LiveSnapshotting
11302 || mData->mMachineState == MachineState_RestoringSnapshot
11303 || mData->mMachineState == MachineState_DeletingSnapshot
11304 , E_FAIL);
11305
11306 HRESULT rc = S_OK;
11307
11308 // use appropriate locked media map (online or offline)
11309 MediumLockListMap lockedMediaOffline;
11310 MediumLockListMap *lockedMediaMap;
11311 if (aOnline)
11312 lockedMediaMap = &mData->mSession.mLockedMedia;
11313 else
11314 lockedMediaMap = &lockedMediaOffline;
11315
11316 try
11317 {
11318 if (!aOnline)
11319 {
11320 /* lock all attached hard disks early to detect "in use"
11321 * situations before creating actual diffs */
11322 for (MediumAttachmentList::const_iterator
11323 it = mMediumAttachments->begin();
11324 it != mMediumAttachments->end();
11325 ++it)
11326 {
11327 MediumAttachment *pAtt = *it;
11328 if (pAtt->i_getType() == DeviceType_HardDisk)
11329 {
11330 Medium *pMedium = pAtt->i_getMedium();
11331 Assert(pMedium);
11332
11333 MediumLockList *pMediumLockList(new MediumLockList());
11334 alock.release();
11335 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11336 NULL /* pToLockWrite */,
11337 false /* fMediumLockWriteAll */,
11338 NULL,
11339 *pMediumLockList);
11340 alock.acquire();
11341 if (FAILED(rc))
11342 {
11343 delete pMediumLockList;
11344 throw rc;
11345 }
11346 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11347 if (FAILED(rc))
11348 {
11349 throw setError(rc,
11350 tr("Collecting locking information for all attached media failed"));
11351 }
11352 }
11353 }
11354
11355 /* Now lock all media. If this fails, nothing is locked. */
11356 alock.release();
11357 rc = lockedMediaMap->Lock();
11358 alock.acquire();
11359 if (FAILED(rc))
11360 {
11361 throw setError(rc,
11362 tr("Locking of attached media failed"));
11363 }
11364 }
11365
11366 /* remember the current list (note that we don't use backup() since
11367 * mMediumAttachments may be already backed up) */
11368 MediumAttachmentList atts = *mMediumAttachments.data();
11369
11370 /* start from scratch */
11371 mMediumAttachments->clear();
11372
11373 /* go through remembered attachments and create diffs for normal hard
11374 * disks and attach them */
11375 for (MediumAttachmentList::const_iterator
11376 it = atts.begin();
11377 it != atts.end();
11378 ++it)
11379 {
11380 MediumAttachment *pAtt = *it;
11381
11382 DeviceType_T devType = pAtt->i_getType();
11383 Medium *pMedium = pAtt->i_getMedium();
11384
11385 if ( devType != DeviceType_HardDisk
11386 || pMedium == NULL
11387 || pMedium->i_getType() != MediumType_Normal)
11388 {
11389 /* copy the attachment as is */
11390
11391 /** @todo the progress object created in SessionMachine::TakeSnaphot
11392 * only expects operations for hard disks. Later other
11393 * device types need to show up in the progress as well. */
11394 if (devType == DeviceType_HardDisk)
11395 {
11396 if (pMedium == NULL)
11397 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11398 aWeight); // weight
11399 else
11400 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11401 pMedium->i_getBase()->i_getName().c_str()).raw(),
11402 aWeight); // weight
11403 }
11404
11405 mMediumAttachments->push_back(pAtt);
11406 continue;
11407 }
11408
11409 /* need a diff */
11410 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11411 pMedium->i_getBase()->i_getName().c_str()).raw(),
11412 aWeight); // weight
11413
11414 Utf8Str strFullSnapshotFolder;
11415 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11416
11417 ComObjPtr<Medium> diff;
11418 diff.createObject();
11419 // store the diff in the same registry as the parent
11420 // (this cannot fail here because we can't create implicit diffs for
11421 // unregistered images)
11422 Guid uuidRegistryParent;
11423 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11424 Assert(fInRegistry); NOREF(fInRegistry);
11425 rc = diff->init(mParent,
11426 pMedium->i_getPreferredDiffFormat(),
11427 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11428 uuidRegistryParent,
11429 DeviceType_HardDisk);
11430 if (FAILED(rc)) throw rc;
11431
11432 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11433 * the push_back? Looks like we're going to release medium with the
11434 * wrong kind of lock (general issue with if we fail anywhere at all)
11435 * and an orphaned VDI in the snapshots folder. */
11436
11437 /* update the appropriate lock list */
11438 MediumLockList *pMediumLockList;
11439 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11440 AssertComRCThrowRC(rc);
11441 if (aOnline)
11442 {
11443 alock.release();
11444 /* The currently attached medium will be read-only, change
11445 * the lock type to read. */
11446 rc = pMediumLockList->Update(pMedium, false);
11447 alock.acquire();
11448 AssertComRCThrowRC(rc);
11449 }
11450
11451 /* release the locks before the potentially lengthy operation */
11452 alock.release();
11453 rc = pMedium->i_createDiffStorage(diff,
11454 pMedium->i_getPreferredDiffVariant(),
11455 pMediumLockList,
11456 NULL /* aProgress */,
11457 true /* aWait */,
11458 false /* aNotify */);
11459 alock.acquire();
11460 if (FAILED(rc)) throw rc;
11461
11462 /* actual lock list update is done in Machine::i_commitMedia */
11463
11464 rc = diff->i_addBackReference(mData->mUuid);
11465 AssertComRCThrowRC(rc);
11466
11467 /* add a new attachment */
11468 ComObjPtr<MediumAttachment> attachment;
11469 attachment.createObject();
11470 rc = attachment->init(this,
11471 diff,
11472 pAtt->i_getControllerName(),
11473 pAtt->i_getPort(),
11474 pAtt->i_getDevice(),
11475 DeviceType_HardDisk,
11476 true /* aImplicit */,
11477 false /* aPassthrough */,
11478 false /* aTempEject */,
11479 pAtt->i_getNonRotational(),
11480 pAtt->i_getDiscard(),
11481 pAtt->i_getHotPluggable(),
11482 pAtt->i_getBandwidthGroup());
11483 if (FAILED(rc)) throw rc;
11484
11485 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11486 AssertComRCThrowRC(rc);
11487 mMediumAttachments->push_back(attachment);
11488 }
11489 }
11490 catch (HRESULT aRC) { rc = aRC; }
11491
11492 /* unlock all hard disks we locked when there is no VM */
11493 if (!aOnline)
11494 {
11495 ErrorInfoKeeper eik;
11496
11497 HRESULT rc1 = lockedMediaMap->Clear();
11498 AssertComRC(rc1);
11499 }
11500
11501 return rc;
11502}
11503
11504/**
11505 * Deletes implicit differencing hard disks created either by
11506 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11507 * mMediumAttachments.
11508 *
11509 * Note that to delete hard disks created by #attachDevice() this method is
11510 * called from #i_rollbackMedia() when the changes are rolled back.
11511 *
11512 * @note Locks this object and the media tree for writing.
11513 */
11514HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11515{
11516 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11517
11518 AutoCaller autoCaller(this);
11519 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
11520
11521 AutoMultiWriteLock2 alock(this->lockHandle(),
11522 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11523
11524 /* We absolutely must have backed up state. */
11525 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11526
11527 /* Check if there are any implicitly created diff images. */
11528 bool fImplicitDiffs = false;
11529 for (MediumAttachmentList::const_iterator
11530 it = mMediumAttachments->begin();
11531 it != mMediumAttachments->end();
11532 ++it)
11533 {
11534 const ComObjPtr<MediumAttachment> &pAtt = *it;
11535 if (pAtt->i_isImplicit())
11536 {
11537 fImplicitDiffs = true;
11538 break;
11539 }
11540 }
11541 /* If there is nothing to do, leave early. This saves lots of image locking
11542 * effort. It also avoids a MachineStateChanged event without real reason.
11543 * This is important e.g. when loading a VM config, because there should be
11544 * no events. Otherwise API clients can become thoroughly confused for
11545 * inaccessible VMs (the code for loading VM configs uses this method for
11546 * cleanup if the config makes no sense), as they take such events as an
11547 * indication that the VM is alive, and they would force the VM config to
11548 * be reread, leading to an endless loop. */
11549 if (!fImplicitDiffs)
11550 return S_OK;
11551
11552 HRESULT rc = S_OK;
11553 MachineState_T oldState = mData->mMachineState;
11554
11555 /* will release the lock before the potentially lengthy operation,
11556 * so protect with the special state (unless already protected) */
11557 if ( oldState != MachineState_Snapshotting
11558 && oldState != MachineState_OnlineSnapshotting
11559 && oldState != MachineState_LiveSnapshotting
11560 && oldState != MachineState_RestoringSnapshot
11561 && oldState != MachineState_DeletingSnapshot
11562 && oldState != MachineState_DeletingSnapshotOnline
11563 && oldState != MachineState_DeletingSnapshotPaused
11564 )
11565 i_setMachineState(MachineState_SettingUp);
11566
11567 // use appropriate locked media map (online or offline)
11568 MediumLockListMap lockedMediaOffline;
11569 MediumLockListMap *lockedMediaMap;
11570 if (aOnline)
11571 lockedMediaMap = &mData->mSession.mLockedMedia;
11572 else
11573 lockedMediaMap = &lockedMediaOffline;
11574
11575 try
11576 {
11577 if (!aOnline)
11578 {
11579 /* lock all attached hard disks early to detect "in use"
11580 * situations before deleting actual diffs */
11581 for (MediumAttachmentList::const_iterator
11582 it = mMediumAttachments->begin();
11583 it != mMediumAttachments->end();
11584 ++it)
11585 {
11586 MediumAttachment *pAtt = *it;
11587 if (pAtt->i_getType() == DeviceType_HardDisk)
11588 {
11589 Medium *pMedium = pAtt->i_getMedium();
11590 Assert(pMedium);
11591
11592 MediumLockList *pMediumLockList(new MediumLockList());
11593 alock.release();
11594 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11595 NULL /* pToLockWrite */,
11596 false /* fMediumLockWriteAll */,
11597 NULL,
11598 *pMediumLockList);
11599 alock.acquire();
11600
11601 if (FAILED(rc))
11602 {
11603 delete pMediumLockList;
11604 throw rc;
11605 }
11606
11607 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11608 if (FAILED(rc))
11609 throw rc;
11610 }
11611 }
11612
11613 if (FAILED(rc))
11614 throw rc;
11615 } // end of offline
11616
11617 /* Lock lists are now up to date and include implicitly created media */
11618
11619 /* Go through remembered attachments and delete all implicitly created
11620 * diffs and fix up the attachment information */
11621 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11622 MediumAttachmentList implicitAtts;
11623 for (MediumAttachmentList::const_iterator
11624 it = mMediumAttachments->begin();
11625 it != mMediumAttachments->end();
11626 ++it)
11627 {
11628 ComObjPtr<MediumAttachment> pAtt = *it;
11629 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11630 if (pMedium.isNull())
11631 continue;
11632
11633 // Implicit attachments go on the list for deletion and back references are removed.
11634 if (pAtt->i_isImplicit())
11635 {
11636 /* Deassociate and mark for deletion */
11637 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11638 rc = pMedium->i_removeBackReference(mData->mUuid);
11639 if (FAILED(rc))
11640 throw rc;
11641 implicitAtts.push_back(pAtt);
11642 continue;
11643 }
11644
11645 /* Was this medium attached before? */
11646 if (!i_findAttachment(oldAtts, pMedium))
11647 {
11648 /* no: de-associate */
11649 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11650 rc = pMedium->i_removeBackReference(mData->mUuid);
11651 if (FAILED(rc))
11652 throw rc;
11653 continue;
11654 }
11655 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11656 }
11657
11658 /* If there are implicit attachments to delete, throw away the lock
11659 * map contents (which will unlock all media) since the medium
11660 * attachments will be rolled back. Below we need to completely
11661 * recreate the lock map anyway since it is infinitely complex to
11662 * do this incrementally (would need reconstructing each attachment
11663 * change, which would be extremely hairy). */
11664 if (implicitAtts.size() != 0)
11665 {
11666 ErrorInfoKeeper eik;
11667
11668 HRESULT rc1 = lockedMediaMap->Clear();
11669 AssertComRC(rc1);
11670 }
11671
11672 /* rollback hard disk changes */
11673 mMediumAttachments.rollback();
11674
11675 MultiResult mrc(S_OK);
11676
11677 // Delete unused implicit diffs.
11678 if (implicitAtts.size() != 0)
11679 {
11680 alock.release();
11681
11682 for (MediumAttachmentList::const_iterator
11683 it = implicitAtts.begin();
11684 it != implicitAtts.end();
11685 ++it)
11686 {
11687 // Remove medium associated with this attachment.
11688 ComObjPtr<MediumAttachment> pAtt = *it;
11689 Assert(pAtt);
11690 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11691 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11692 Assert(pMedium);
11693
11694 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11695 // continue on delete failure, just collect error messages
11696 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11697 pMedium->i_getLocationFull().c_str() ));
11698 mrc = rc;
11699 }
11700 // Clear the list of deleted implicit attachments now, while not
11701 // holding the lock, as it will ultimately trigger Medium::uninit()
11702 // calls which assume that the media tree lock isn't held.
11703 implicitAtts.clear();
11704
11705 alock.acquire();
11706
11707 /* if there is a VM recreate media lock map as mentioned above,
11708 * otherwise it is a waste of time and we leave things unlocked */
11709 if (aOnline)
11710 {
11711 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11712 /* must never be NULL, but better safe than sorry */
11713 if (!pMachine.isNull())
11714 {
11715 alock.release();
11716 rc = mData->mSession.mMachine->i_lockMedia();
11717 alock.acquire();
11718 if (FAILED(rc))
11719 throw rc;
11720 }
11721 }
11722 }
11723 }
11724 catch (HRESULT aRC) {rc = aRC;}
11725
11726 if (mData->mMachineState == MachineState_SettingUp)
11727 i_setMachineState(oldState);
11728
11729 /* unlock all hard disks we locked when there is no VM */
11730 if (!aOnline)
11731 {
11732 ErrorInfoKeeper eik;
11733
11734 HRESULT rc1 = lockedMediaMap->Clear();
11735 AssertComRC(rc1);
11736 }
11737
11738 return rc;
11739}
11740
11741
11742/**
11743 * Looks through the given list of media attachments for one with the given parameters
11744 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11745 * can be searched as well if needed.
11746 *
11747 * @param ll
11748 * @param aControllerName
11749 * @param aControllerPort
11750 * @param aDevice
11751 * @return
11752 */
11753MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11754 const Utf8Str &aControllerName,
11755 LONG aControllerPort,
11756 LONG aDevice)
11757{
11758 for (MediumAttachmentList::const_iterator
11759 it = ll.begin();
11760 it != ll.end();
11761 ++it)
11762 {
11763 MediumAttachment *pAttach = *it;
11764 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11765 return pAttach;
11766 }
11767
11768 return NULL;
11769}
11770
11771/**
11772 * Looks through the given list of media attachments for one with the given parameters
11773 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11774 * can be searched as well if needed.
11775 *
11776 * @param ll
11777 * @param pMedium
11778 * @return
11779 */
11780MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11781 ComObjPtr<Medium> pMedium)
11782{
11783 for (MediumAttachmentList::const_iterator
11784 it = ll.begin();
11785 it != ll.end();
11786 ++it)
11787 {
11788 MediumAttachment *pAttach = *it;
11789 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11790 if (pMediumThis == pMedium)
11791 return pAttach;
11792 }
11793
11794 return NULL;
11795}
11796
11797/**
11798 * Looks through the given list of media attachments for one with the given parameters
11799 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11800 * can be searched as well if needed.
11801 *
11802 * @param ll
11803 * @param id
11804 * @return
11805 */
11806MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11807 Guid &id)
11808{
11809 for (MediumAttachmentList::const_iterator
11810 it = ll.begin();
11811 it != ll.end();
11812 ++it)
11813 {
11814 MediumAttachment *pAttach = *it;
11815 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11816 if (pMediumThis->i_getId() == id)
11817 return pAttach;
11818 }
11819
11820 return NULL;
11821}
11822
11823/**
11824 * Main implementation for Machine::DetachDevice. This also gets called
11825 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11826 *
11827 * @param pAttach Medium attachment to detach.
11828 * @param writeLock Machine write lock which the caller must have locked once.
11829 * This may be released temporarily in here.
11830 * @param pSnapshot If NULL, then the detachment is for the current machine.
11831 * Otherwise this is for a SnapshotMachine, and this must be
11832 * its snapshot.
11833 * @return
11834 */
11835HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11836 AutoWriteLock &writeLock,
11837 Snapshot *pSnapshot)
11838{
11839 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11840 DeviceType_T mediumType = pAttach->i_getType();
11841
11842 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11843
11844 if (pAttach->i_isImplicit())
11845 {
11846 /* attempt to implicitly delete the implicitly created diff */
11847
11848 /// @todo move the implicit flag from MediumAttachment to Medium
11849 /// and forbid any hard disk operation when it is implicit. Or maybe
11850 /// a special media state for it to make it even more simple.
11851
11852 Assert(mMediumAttachments.isBackedUp());
11853
11854 /* will release the lock before the potentially lengthy operation, so
11855 * protect with the special state */
11856 MachineState_T oldState = mData->mMachineState;
11857 i_setMachineState(MachineState_SettingUp);
11858
11859 writeLock.release();
11860
11861 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11862 true /*aWait*/,
11863 false /*aNotify*/);
11864
11865 writeLock.acquire();
11866
11867 i_setMachineState(oldState);
11868
11869 if (FAILED(rc)) return rc;
11870 }
11871
11872 i_setModified(IsModified_Storage);
11873 mMediumAttachments.backup();
11874 mMediumAttachments->remove(pAttach);
11875
11876 if (!oldmedium.isNull())
11877 {
11878 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11879 if (pSnapshot)
11880 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11881 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11882 else if (mediumType != DeviceType_HardDisk)
11883 oldmedium->i_removeBackReference(mData->mUuid);
11884 }
11885
11886 return S_OK;
11887}
11888
11889/**
11890 * Goes thru all media of the given list and
11891 *
11892 * 1) calls i_detachDevice() on each of them for this machine and
11893 * 2) adds all Medium objects found in the process to the given list,
11894 * depending on cleanupMode.
11895 *
11896 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11897 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11898 * media to the list.
11899 * CleanupMode_DetachAllReturnHardDisksAndVMRemovable adds hard disk and
11900 * also removable medias if they are located in the VM folder and referenced
11901 * only by this VM (media prepared by unattended installer).
11902 *
11903 * This gets called from Machine::Unregister, both for the actual Machine and
11904 * the SnapshotMachine objects that might be found in the snapshots.
11905 *
11906 * Requires caller and locking. The machine lock must be passed in because it
11907 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11908 *
11909 * @param writeLock Machine lock from top-level caller; this gets passed to
11910 * i_detachDevice.
11911 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11912 * object if called for a SnapshotMachine.
11913 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11914 * added to llMedia; if Full, then all media get added;
11915 * otherwise no media get added.
11916 * @param llMedia Caller's list to receive Medium objects which got detached so
11917 * caller can close() them, depending on cleanupMode.
11918 * @return
11919 */
11920HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11921 Snapshot *pSnapshot,
11922 CleanupMode_T cleanupMode,
11923 MediaList &llMedia)
11924{
11925 Assert(isWriteLockOnCurrentThread());
11926
11927 HRESULT rc;
11928
11929 // make a temporary list because i_detachDevice invalidates iterators into
11930 // mMediumAttachments
11931 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11932
11933 for (MediumAttachmentList::iterator
11934 it = llAttachments2.begin();
11935 it != llAttachments2.end();
11936 ++it)
11937 {
11938 ComObjPtr<MediumAttachment> &pAttach = *it;
11939 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11940
11941 if (!pMedium.isNull())
11942 {
11943 AutoCaller mac(pMedium);
11944 if (FAILED(mac.hrc())) return mac.hrc();
11945 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11946 DeviceType_T devType = pMedium->i_getDeviceType();
11947 size_t cBackRefs = pMedium->i_getMachineBackRefCount();
11948 Utf8Str strMediumLocation = pMedium->i_getLocationFull();
11949 strMediumLocation.stripFilename();
11950 Utf8Str strMachineFolder = i_getSettingsFileFull();
11951 strMachineFolder.stripFilename();
11952 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11953 && devType == DeviceType_HardDisk)
11954 || ( cleanupMode == CleanupMode_DetachAllReturnHardDisksAndVMRemovable
11955 && ( devType == DeviceType_HardDisk
11956 || ( cBackRefs <= 1
11957 && strMediumLocation == strMachineFolder
11958 && *pMedium->i_getFirstMachineBackrefId() == i_getId())))
11959 || (cleanupMode == CleanupMode_Full)
11960 )
11961 {
11962 llMedia.push_back(pMedium);
11963 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11964 /* Not allowed to keep this lock as below we need the parent
11965 * medium lock, and the lock order is parent to child. */
11966 lock.release();
11967 /*
11968 * Search for medias which are not attached to any machine, but
11969 * in the chain to an attached disk. Mediums are only consided
11970 * if they are:
11971 * - have only one child
11972 * - no references to any machines
11973 * - are of normal medium type
11974 */
11975 while (!pParent.isNull())
11976 {
11977 AutoCaller mac1(pParent);
11978 if (FAILED(mac1.hrc())) return mac1.hrc();
11979 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11980 if (pParent->i_getChildren().size() == 1)
11981 {
11982 if ( pParent->i_getMachineBackRefCount() == 0
11983 && pParent->i_getType() == MediumType_Normal
11984 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11985 llMedia.push_back(pParent);
11986 }
11987 else
11988 break;
11989 pParent = pParent->i_getParent();
11990 }
11991 }
11992 }
11993
11994 // real machine: then we need to use the proper method
11995 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11996
11997 if (FAILED(rc))
11998 return rc;
11999 }
12000
12001 return S_OK;
12002}
12003
12004/**
12005 * Perform deferred hard disk detachments.
12006 *
12007 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
12008 * changed (not backed up).
12009 *
12010 * If @a aOnline is @c true then this method will also unlock the old hard
12011 * disks for which the new implicit diffs were created and will lock these new
12012 * diffs for writing.
12013 *
12014 * @param aOnline Whether the VM was online prior to this operation.
12015 *
12016 * @note Locks this object for writing!
12017 */
12018void Machine::i_commitMedia(bool aOnline /*= false*/)
12019{
12020 AutoCaller autoCaller(this);
12021 AssertComRCReturnVoid(autoCaller.hrc());
12022
12023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12024
12025 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
12026
12027 HRESULT rc = S_OK;
12028
12029 /* no attach/detach operations -- nothing to do */
12030 if (!mMediumAttachments.isBackedUp())
12031 return;
12032
12033 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
12034 bool fMediaNeedsLocking = false;
12035
12036 /* enumerate new attachments */
12037 for (MediumAttachmentList::const_iterator
12038 it = mMediumAttachments->begin();
12039 it != mMediumAttachments->end();
12040 ++it)
12041 {
12042 MediumAttachment *pAttach = *it;
12043
12044 pAttach->i_commit();
12045
12046 Medium *pMedium = pAttach->i_getMedium();
12047 bool fImplicit = pAttach->i_isImplicit();
12048
12049 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
12050 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
12051 fImplicit));
12052
12053 /** @todo convert all this Machine-based voodoo to MediumAttachment
12054 * based commit logic. */
12055 if (fImplicit)
12056 {
12057 /* convert implicit attachment to normal */
12058 pAttach->i_setImplicit(false);
12059
12060 if ( aOnline
12061 && pMedium
12062 && pAttach->i_getType() == DeviceType_HardDisk
12063 )
12064 {
12065 /* update the appropriate lock list */
12066 MediumLockList *pMediumLockList;
12067 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
12068 AssertComRC(rc);
12069 if (pMediumLockList)
12070 {
12071 /* unlock if there's a need to change the locking */
12072 if (!fMediaNeedsLocking)
12073 {
12074 Assert(mData->mSession.mLockedMedia.IsLocked());
12075 rc = mData->mSession.mLockedMedia.Unlock();
12076 AssertComRC(rc);
12077 fMediaNeedsLocking = true;
12078 }
12079 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
12080 AssertComRC(rc);
12081 rc = pMediumLockList->Append(pMedium, true);
12082 AssertComRC(rc);
12083 }
12084 }
12085
12086 continue;
12087 }
12088
12089 if (pMedium)
12090 {
12091 /* was this medium attached before? */
12092 for (MediumAttachmentList::iterator
12093 oldIt = oldAtts.begin();
12094 oldIt != oldAtts.end();
12095 ++oldIt)
12096 {
12097 MediumAttachment *pOldAttach = *oldIt;
12098 if (pOldAttach->i_getMedium() == pMedium)
12099 {
12100 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
12101
12102 /* yes: remove from old to avoid de-association */
12103 oldAtts.erase(oldIt);
12104 break;
12105 }
12106 }
12107 }
12108 }
12109
12110 /* enumerate remaining old attachments and de-associate from the
12111 * current machine state */
12112 for (MediumAttachmentList::const_iterator
12113 it = oldAtts.begin();
12114 it != oldAtts.end();
12115 ++it)
12116 {
12117 MediumAttachment *pAttach = *it;
12118 Medium *pMedium = pAttach->i_getMedium();
12119
12120 /* Detach only hard disks, since DVD/floppy media is detached
12121 * instantly in MountMedium. */
12122 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
12123 {
12124 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
12125
12126 /* now de-associate from the current machine state */
12127 rc = pMedium->i_removeBackReference(mData->mUuid);
12128 AssertComRC(rc);
12129
12130 if (aOnline)
12131 {
12132 /* unlock since medium is not used anymore */
12133 MediumLockList *pMediumLockList;
12134 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
12135 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
12136 {
12137 /* this happens for online snapshots, there the attachment
12138 * is changing, but only to a diff image created under
12139 * the old one, so there is no separate lock list */
12140 Assert(!pMediumLockList);
12141 }
12142 else
12143 {
12144 AssertComRC(rc);
12145 if (pMediumLockList)
12146 {
12147 rc = mData->mSession.mLockedMedia.Remove(pAttach);
12148 AssertComRC(rc);
12149 }
12150 }
12151 }
12152 }
12153 }
12154
12155 /* take media locks again so that the locking state is consistent */
12156 if (fMediaNeedsLocking)
12157 {
12158 Assert(aOnline);
12159 rc = mData->mSession.mLockedMedia.Lock();
12160 AssertComRC(rc);
12161 }
12162
12163 /* commit the hard disk changes */
12164 mMediumAttachments.commit();
12165
12166 if (i_isSessionMachine())
12167 {
12168 /*
12169 * Update the parent machine to point to the new owner.
12170 * This is necessary because the stored parent will point to the
12171 * session machine otherwise and cause crashes or errors later
12172 * when the session machine gets invalid.
12173 */
12174 /** @todo Change the MediumAttachment class to behave like any other
12175 * class in this regard by creating peer MediumAttachment
12176 * objects for session machines and share the data with the peer
12177 * machine.
12178 */
12179 for (MediumAttachmentList::const_iterator
12180 it = mMediumAttachments->begin();
12181 it != mMediumAttachments->end();
12182 ++it)
12183 (*it)->i_updateParentMachine(mPeer);
12184
12185 /* attach new data to the primary machine and reshare it */
12186 mPeer->mMediumAttachments.attach(mMediumAttachments);
12187 }
12188
12189 return;
12190}
12191
12192/**
12193 * Perform deferred deletion of implicitly created diffs.
12194 *
12195 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
12196 * changed (not backed up).
12197 *
12198 * @note Locks this object for writing!
12199 */
12200void Machine::i_rollbackMedia()
12201{
12202 AutoCaller autoCaller(this);
12203 AssertComRCReturnVoid(autoCaller.hrc());
12204
12205 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12206 LogFlowThisFunc(("Entering rollbackMedia\n"));
12207
12208 HRESULT rc = S_OK;
12209
12210 /* no attach/detach operations -- nothing to do */
12211 if (!mMediumAttachments.isBackedUp())
12212 return;
12213
12214 /* enumerate new attachments */
12215 for (MediumAttachmentList::const_iterator
12216 it = mMediumAttachments->begin();
12217 it != mMediumAttachments->end();
12218 ++it)
12219 {
12220 MediumAttachment *pAttach = *it;
12221 /* Fix up the backrefs for DVD/floppy media. */
12222 if (pAttach->i_getType() != DeviceType_HardDisk)
12223 {
12224 Medium *pMedium = pAttach->i_getMedium();
12225 if (pMedium)
12226 {
12227 rc = pMedium->i_removeBackReference(mData->mUuid);
12228 AssertComRC(rc);
12229 }
12230 }
12231
12232 (*it)->i_rollback();
12233
12234 pAttach = *it;
12235 /* Fix up the backrefs for DVD/floppy media. */
12236 if (pAttach->i_getType() != DeviceType_HardDisk)
12237 {
12238 Medium *pMedium = pAttach->i_getMedium();
12239 if (pMedium)
12240 {
12241 rc = pMedium->i_addBackReference(mData->mUuid);
12242 AssertComRC(rc);
12243 }
12244 }
12245 }
12246
12247 /** @todo convert all this Machine-based voodoo to MediumAttachment
12248 * based rollback logic. */
12249 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
12250
12251 return;
12252}
12253
12254/**
12255 * Returns true if the settings file is located in the directory named exactly
12256 * as the machine; this means, among other things, that the machine directory
12257 * should be auto-renamed.
12258 *
12259 * @param aSettingsDir if not NULL, the full machine settings file directory
12260 * name will be assigned there.
12261 *
12262 * @note Doesn't lock anything.
12263 * @note Not thread safe (must be called from this object's lock).
12264 */
12265bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
12266{
12267 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12268 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
12269 if (aSettingsDir)
12270 *aSettingsDir = strMachineDirName;
12271 strMachineDirName.stripPath(); // vmname
12272 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12273 strConfigFileOnly.stripPath() // vmname.vbox
12274 .stripSuffix(); // vmname
12275 /** @todo hack, make somehow use of ComposeMachineFilename */
12276 if (mUserData->s.fDirectoryIncludesUUID)
12277 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
12278
12279 AssertReturn(!strMachineDirName.isEmpty(), false);
12280 AssertReturn(!strConfigFileOnly.isEmpty(), false);
12281
12282 return strMachineDirName == strConfigFileOnly;
12283}
12284
12285/**
12286 * Discards all changes to machine settings.
12287 *
12288 * @param aNotify Whether to notify the direct session about changes or not.
12289 *
12290 * @note Locks objects for writing!
12291 */
12292void Machine::i_rollback(bool aNotify)
12293{
12294 AutoCaller autoCaller(this);
12295 AssertComRCReturn(autoCaller.hrc(), (void)0);
12296
12297 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12298
12299 if (!mStorageControllers.isNull())
12300 {
12301 if (mStorageControllers.isBackedUp())
12302 {
12303 /* unitialize all new devices (absent in the backed up list). */
12304 StorageControllerList *backedList = mStorageControllers.backedUpData();
12305 for (StorageControllerList::const_iterator
12306 it = mStorageControllers->begin();
12307 it != mStorageControllers->end();
12308 ++it)
12309 {
12310 if ( std::find(backedList->begin(), backedList->end(), *it)
12311 == backedList->end()
12312 )
12313 {
12314 (*it)->uninit();
12315 }
12316 }
12317
12318 /* restore the list */
12319 mStorageControllers.rollback();
12320 }
12321
12322 /* rollback any changes to devices after restoring the list */
12323 if (mData->flModifications & IsModified_Storage)
12324 {
12325 for (StorageControllerList::const_iterator
12326 it = mStorageControllers->begin();
12327 it != mStorageControllers->end();
12328 ++it)
12329 {
12330 (*it)->i_rollback();
12331 }
12332 }
12333 }
12334
12335 if (!mUSBControllers.isNull())
12336 {
12337 if (mUSBControllers.isBackedUp())
12338 {
12339 /* unitialize all new devices (absent in the backed up list). */
12340 USBControllerList *backedList = mUSBControllers.backedUpData();
12341 for (USBControllerList::const_iterator
12342 it = mUSBControllers->begin();
12343 it != mUSBControllers->end();
12344 ++it)
12345 {
12346 if ( std::find(backedList->begin(), backedList->end(), *it)
12347 == backedList->end()
12348 )
12349 {
12350 (*it)->uninit();
12351 }
12352 }
12353
12354 /* restore the list */
12355 mUSBControllers.rollback();
12356 }
12357
12358 /* rollback any changes to devices after restoring the list */
12359 if (mData->flModifications & IsModified_USB)
12360 {
12361 for (USBControllerList::const_iterator
12362 it = mUSBControllers->begin();
12363 it != mUSBControllers->end();
12364 ++it)
12365 {
12366 (*it)->i_rollback();
12367 }
12368 }
12369 }
12370
12371 mUserData.rollback();
12372
12373 mHWData.rollback();
12374
12375 if (mData->flModifications & IsModified_Storage)
12376 i_rollbackMedia();
12377
12378 if (mBIOSSettings)
12379 mBIOSSettings->i_rollback();
12380
12381 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
12382 mRecordingSettings->i_rollback();
12383
12384 if (mTrustedPlatformModule)
12385 mTrustedPlatformModule->i_rollback();
12386
12387 if (mNvramStore)
12388 mNvramStore->i_rollback();
12389
12390 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
12391 mGraphicsAdapter->i_rollback();
12392
12393 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12394 mVRDEServer->i_rollback();
12395
12396 if (mAudioSettings && (mData->flModifications & IsModified_AudioSettings))
12397 mAudioSettings->i_rollback();
12398
12399 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12400 mUSBDeviceFilters->i_rollback();
12401
12402 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12403 mBandwidthControl->i_rollback();
12404
12405 if (mGuestDebugControl && (mData->flModifications & IsModified_GuestDebugControl))
12406 mGuestDebugControl->i_rollback();
12407
12408 if (!mHWData.isNull())
12409 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12410 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12411 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12412 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12413
12414 if (mData->flModifications & IsModified_NetworkAdapters)
12415 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12416 if ( mNetworkAdapters[slot]
12417 && mNetworkAdapters[slot]->i_isModified())
12418 {
12419 mNetworkAdapters[slot]->i_rollback();
12420 networkAdapters[slot] = mNetworkAdapters[slot];
12421 }
12422
12423 if (mData->flModifications & IsModified_SerialPorts)
12424 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12425 if ( mSerialPorts[slot]
12426 && mSerialPorts[slot]->i_isModified())
12427 {
12428 mSerialPorts[slot]->i_rollback();
12429 serialPorts[slot] = mSerialPorts[slot];
12430 }
12431
12432 if (mData->flModifications & IsModified_ParallelPorts)
12433 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12434 if ( mParallelPorts[slot]
12435 && mParallelPorts[slot]->i_isModified())
12436 {
12437 mParallelPorts[slot]->i_rollback();
12438 parallelPorts[slot] = mParallelPorts[slot];
12439 }
12440
12441 if (aNotify)
12442 {
12443 /* inform the direct session about changes */
12444
12445 ComObjPtr<Machine> that = this;
12446 uint32_t flModifications = mData->flModifications;
12447 alock.release();
12448
12449 if (flModifications & IsModified_SharedFolders)
12450 that->i_onSharedFolderChange();
12451
12452 if (flModifications & IsModified_VRDEServer)
12453 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12454 if (flModifications & IsModified_USB)
12455 that->i_onUSBControllerChange();
12456
12457 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12458 if (networkAdapters[slot])
12459 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12460 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12461 if (serialPorts[slot])
12462 that->i_onSerialPortChange(serialPorts[slot]);
12463 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12464 if (parallelPorts[slot])
12465 that->i_onParallelPortChange(parallelPorts[slot]);
12466
12467 if (flModifications & IsModified_Storage)
12468 {
12469 for (StorageControllerList::const_iterator
12470 it = mStorageControllers->begin();
12471 it != mStorageControllers->end();
12472 ++it)
12473 {
12474 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
12475 }
12476 }
12477
12478 if (flModifications & IsModified_GuestDebugControl)
12479 that->i_onGuestDebugControlChange(mGuestDebugControl);
12480
12481#if 0
12482 if (flModifications & IsModified_BandwidthControl)
12483 that->onBandwidthControlChange();
12484#endif
12485 }
12486}
12487
12488/**
12489 * Commits all the changes to machine settings.
12490 *
12491 * Note that this operation is supposed to never fail.
12492 *
12493 * @note Locks this object and children for writing.
12494 */
12495void Machine::i_commit()
12496{
12497 AutoCaller autoCaller(this);
12498 AssertComRCReturnVoid(autoCaller.hrc());
12499
12500 AutoCaller peerCaller(mPeer);
12501 AssertComRCReturnVoid(peerCaller.hrc());
12502
12503 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12504
12505 /*
12506 * use safe commit to ensure Snapshot machines (that share mUserData)
12507 * will still refer to a valid memory location
12508 */
12509 mUserData.commitCopy();
12510
12511 mHWData.commit();
12512
12513 if (mMediumAttachments.isBackedUp())
12514 i_commitMedia(Global::IsOnline(mData->mMachineState));
12515
12516 mBIOSSettings->i_commit();
12517 mRecordingSettings->i_commit();
12518 mTrustedPlatformModule->i_commit();
12519 mNvramStore->i_commit();
12520 mGraphicsAdapter->i_commit();
12521 mVRDEServer->i_commit();
12522 mAudioSettings->i_commit();
12523 mUSBDeviceFilters->i_commit();
12524 mBandwidthControl->i_commit();
12525 mGuestDebugControl->i_commit();
12526
12527 /* Since mNetworkAdapters is a list which might have been changed (resized)
12528 * without using the Backupable<> template we need to handle the copying
12529 * of the list entries manually, including the creation of peers for the
12530 * new objects. */
12531 bool commitNetworkAdapters = false;
12532 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12533 if (mPeer)
12534 {
12535 /* commit everything, even the ones which will go away */
12536 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12537 mNetworkAdapters[slot]->i_commit();
12538 /* copy over the new entries, creating a peer and uninit the original */
12539 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12540 for (size_t slot = 0; slot < newSize; slot++)
12541 {
12542 /* look if this adapter has a peer device */
12543 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12544 if (!peer)
12545 {
12546 /* no peer means the adapter is a newly created one;
12547 * create a peer owning data this data share it with */
12548 peer.createObject();
12549 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12550 }
12551 mPeer->mNetworkAdapters[slot] = peer;
12552 }
12553 /* uninit any no longer needed network adapters */
12554 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12555 mNetworkAdapters[slot]->uninit();
12556 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12557 {
12558 if (mPeer->mNetworkAdapters[slot])
12559 mPeer->mNetworkAdapters[slot]->uninit();
12560 }
12561 /* Keep the original network adapter count until this point, so that
12562 * discarding a chipset type change will not lose settings. */
12563 mNetworkAdapters.resize(newSize);
12564 mPeer->mNetworkAdapters.resize(newSize);
12565 }
12566 else
12567 {
12568 /* we have no peer (our parent is the newly created machine);
12569 * just commit changes to the network adapters */
12570 commitNetworkAdapters = true;
12571 }
12572 if (commitNetworkAdapters)
12573 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12574 mNetworkAdapters[slot]->i_commit();
12575
12576 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12577 mSerialPorts[slot]->i_commit();
12578 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12579 mParallelPorts[slot]->i_commit();
12580
12581 bool commitStorageControllers = false;
12582
12583 if (mStorageControllers.isBackedUp())
12584 {
12585 mStorageControllers.commit();
12586
12587 if (mPeer)
12588 {
12589 /* Commit all changes to new controllers (this will reshare data with
12590 * peers for those who have peers) */
12591 StorageControllerList *newList = new StorageControllerList();
12592 for (StorageControllerList::const_iterator
12593 it = mStorageControllers->begin();
12594 it != mStorageControllers->end();
12595 ++it)
12596 {
12597 (*it)->i_commit();
12598
12599 /* look if this controller has a peer device */
12600 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12601 if (!peer)
12602 {
12603 /* no peer means the device is a newly created one;
12604 * create a peer owning data this device share it with */
12605 peer.createObject();
12606 peer->init(mPeer, *it, true /* aReshare */);
12607 }
12608 else
12609 {
12610 /* remove peer from the old list */
12611 mPeer->mStorageControllers->remove(peer);
12612 }
12613 /* and add it to the new list */
12614 newList->push_back(peer);
12615 }
12616
12617 /* uninit old peer's controllers that are left */
12618 for (StorageControllerList::const_iterator
12619 it = mPeer->mStorageControllers->begin();
12620 it != mPeer->mStorageControllers->end();
12621 ++it)
12622 {
12623 (*it)->uninit();
12624 }
12625
12626 /* attach new list of controllers to our peer */
12627 mPeer->mStorageControllers.attach(newList);
12628 }
12629 else
12630 {
12631 /* we have no peer (our parent is the newly created machine);
12632 * just commit changes to devices */
12633 commitStorageControllers = true;
12634 }
12635 }
12636 else
12637 {
12638 /* the list of controllers itself is not changed,
12639 * just commit changes to controllers themselves */
12640 commitStorageControllers = true;
12641 }
12642
12643 if (commitStorageControllers)
12644 {
12645 for (StorageControllerList::const_iterator
12646 it = mStorageControllers->begin();
12647 it != mStorageControllers->end();
12648 ++it)
12649 {
12650 (*it)->i_commit();
12651 }
12652 }
12653
12654 bool commitUSBControllers = false;
12655
12656 if (mUSBControllers.isBackedUp())
12657 {
12658 mUSBControllers.commit();
12659
12660 if (mPeer)
12661 {
12662 /* Commit all changes to new controllers (this will reshare data with
12663 * peers for those who have peers) */
12664 USBControllerList *newList = new USBControllerList();
12665 for (USBControllerList::const_iterator
12666 it = mUSBControllers->begin();
12667 it != mUSBControllers->end();
12668 ++it)
12669 {
12670 (*it)->i_commit();
12671
12672 /* look if this controller has a peer device */
12673 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12674 if (!peer)
12675 {
12676 /* no peer means the device is a newly created one;
12677 * create a peer owning data this device share it with */
12678 peer.createObject();
12679 peer->init(mPeer, *it, true /* aReshare */);
12680 }
12681 else
12682 {
12683 /* remove peer from the old list */
12684 mPeer->mUSBControllers->remove(peer);
12685 }
12686 /* and add it to the new list */
12687 newList->push_back(peer);
12688 }
12689
12690 /* uninit old peer's controllers that are left */
12691 for (USBControllerList::const_iterator
12692 it = mPeer->mUSBControllers->begin();
12693 it != mPeer->mUSBControllers->end();
12694 ++it)
12695 {
12696 (*it)->uninit();
12697 }
12698
12699 /* attach new list of controllers to our peer */
12700 mPeer->mUSBControllers.attach(newList);
12701 }
12702 else
12703 {
12704 /* we have no peer (our parent is the newly created machine);
12705 * just commit changes to devices */
12706 commitUSBControllers = true;
12707 }
12708 }
12709 else
12710 {
12711 /* the list of controllers itself is not changed,
12712 * just commit changes to controllers themselves */
12713 commitUSBControllers = true;
12714 }
12715
12716 if (commitUSBControllers)
12717 {
12718 for (USBControllerList::const_iterator
12719 it = mUSBControllers->begin();
12720 it != mUSBControllers->end();
12721 ++it)
12722 {
12723 (*it)->i_commit();
12724 }
12725 }
12726
12727 if (i_isSessionMachine())
12728 {
12729 /* attach new data to the primary machine and reshare it */
12730 mPeer->mUserData.attach(mUserData);
12731 mPeer->mHWData.attach(mHWData);
12732 /* mmMediumAttachments is reshared by fixupMedia */
12733 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12734 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12735 }
12736}
12737
12738/**
12739 * Copies all the hardware data from the given machine.
12740 *
12741 * Currently, only called when the VM is being restored from a snapshot. In
12742 * particular, this implies that the VM is not running during this method's
12743 * call.
12744 *
12745 * @note This method must be called from under this object's lock.
12746 *
12747 * @note This method doesn't call #i_commit(), so all data remains backed up and
12748 * unsaved.
12749 */
12750void Machine::i_copyFrom(Machine *aThat)
12751{
12752 AssertReturnVoid(!i_isSnapshotMachine());
12753 AssertReturnVoid(aThat->i_isSnapshotMachine());
12754
12755 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12756
12757 mHWData.assignCopy(aThat->mHWData);
12758
12759 // create copies of all shared folders (mHWData after attaching a copy
12760 // contains just references to original objects)
12761 for (HWData::SharedFolderList::iterator
12762 it = mHWData->mSharedFolders.begin();
12763 it != mHWData->mSharedFolders.end();
12764 ++it)
12765 {
12766 ComObjPtr<SharedFolder> folder;
12767 folder.createObject();
12768 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12769 AssertComRC(rc);
12770 *it = folder;
12771 }
12772
12773 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12774 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12775 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12776 mNvramStore->i_copyFrom(aThat->mNvramStore);
12777 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12778 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12779 mAudioSettings->i_copyFrom(aThat->mAudioSettings);
12780 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12781 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12782 mGuestDebugControl->i_copyFrom(aThat->mGuestDebugControl);
12783
12784 /* create private copies of all controllers */
12785 mStorageControllers.backup();
12786 mStorageControllers->clear();
12787 for (StorageControllerList::const_iterator
12788 it = aThat->mStorageControllers->begin();
12789 it != aThat->mStorageControllers->end();
12790 ++it)
12791 {
12792 ComObjPtr<StorageController> ctrl;
12793 ctrl.createObject();
12794 ctrl->initCopy(this, *it);
12795 mStorageControllers->push_back(ctrl);
12796 }
12797
12798 /* create private copies of all USB controllers */
12799 mUSBControllers.backup();
12800 mUSBControllers->clear();
12801 for (USBControllerList::const_iterator
12802 it = aThat->mUSBControllers->begin();
12803 it != aThat->mUSBControllers->end();
12804 ++it)
12805 {
12806 ComObjPtr<USBController> ctrl;
12807 ctrl.createObject();
12808 ctrl->initCopy(this, *it);
12809 mUSBControllers->push_back(ctrl);
12810 }
12811
12812 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12813 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12814 {
12815 if (mNetworkAdapters[slot].isNotNull())
12816 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12817 else
12818 {
12819 unconst(mNetworkAdapters[slot]).createObject();
12820 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12821 }
12822 }
12823 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12824 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12825 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12826 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12827}
12828
12829/**
12830 * Returns whether the given storage controller is hotplug capable.
12831 *
12832 * @returns true if the controller supports hotplugging
12833 * false otherwise.
12834 * @param enmCtrlType The controller type to check for.
12835 */
12836bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12837{
12838 ComPtr<ISystemProperties> systemProperties;
12839 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12840 if (FAILED(rc))
12841 return false;
12842
12843 BOOL aHotplugCapable = FALSE;
12844 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12845
12846 return RT_BOOL(aHotplugCapable);
12847}
12848
12849#ifdef VBOX_WITH_RESOURCE_USAGE_API
12850
12851void Machine::i_getDiskList(MediaList &list)
12852{
12853 for (MediumAttachmentList::const_iterator
12854 it = mMediumAttachments->begin();
12855 it != mMediumAttachments->end();
12856 ++it)
12857 {
12858 MediumAttachment *pAttach = *it;
12859 /* just in case */
12860 AssertContinue(pAttach);
12861
12862 AutoCaller localAutoCallerA(pAttach);
12863 if (FAILED(localAutoCallerA.hrc())) continue;
12864
12865 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12866
12867 if (pAttach->i_getType() == DeviceType_HardDisk)
12868 list.push_back(pAttach->i_getMedium());
12869 }
12870}
12871
12872void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12873{
12874 AssertReturnVoid(isWriteLockOnCurrentThread());
12875 AssertPtrReturnVoid(aCollector);
12876
12877 pm::CollectorHAL *hal = aCollector->getHAL();
12878 /* Create sub metrics */
12879 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12880 "Percentage of processor time spent in user mode by the VM process.");
12881 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12882 "Percentage of processor time spent in kernel mode by the VM process.");
12883 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12884 "Size of resident portion of VM process in memory.");
12885 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12886 "Actual size of all VM disks combined.");
12887 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12888 "Network receive rate.");
12889 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12890 "Network transmit rate.");
12891 /* Create and register base metrics */
12892 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12893 cpuLoadUser, cpuLoadKernel);
12894 aCollector->registerBaseMetric(cpuLoad);
12895 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12896 ramUsageUsed);
12897 aCollector->registerBaseMetric(ramUsage);
12898 MediaList disks;
12899 i_getDiskList(disks);
12900 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12901 diskUsageUsed);
12902 aCollector->registerBaseMetric(diskUsage);
12903
12904 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12905 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12906 new pm::AggregateAvg()));
12907 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12908 new pm::AggregateMin()));
12909 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12910 new pm::AggregateMax()));
12911 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12912 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12913 new pm::AggregateAvg()));
12914 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12915 new pm::AggregateMin()));
12916 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12917 new pm::AggregateMax()));
12918
12919 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12920 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12921 new pm::AggregateAvg()));
12922 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12923 new pm::AggregateMin()));
12924 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12925 new pm::AggregateMax()));
12926
12927 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12928 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12929 new pm::AggregateAvg()));
12930 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12931 new pm::AggregateMin()));
12932 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12933 new pm::AggregateMax()));
12934
12935
12936 /* Guest metrics collector */
12937 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12938 aCollector->registerGuest(mCollectorGuest);
12939 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12940
12941 /* Create sub metrics */
12942 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12943 "Percentage of processor time spent in user mode as seen by the guest.");
12944 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12945 "Percentage of processor time spent in kernel mode as seen by the guest.");
12946 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12947 "Percentage of processor time spent idling as seen by the guest.");
12948
12949 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12950 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12951 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12952 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12953 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12954 pm::SubMetric *guestMemCache = new pm::SubMetric(
12955 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12956
12957 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12958 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12959
12960 /* Create and register base metrics */
12961 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12962 machineNetRx, machineNetTx);
12963 aCollector->registerBaseMetric(machineNetRate);
12964
12965 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12966 guestLoadUser, guestLoadKernel, guestLoadIdle);
12967 aCollector->registerBaseMetric(guestCpuLoad);
12968
12969 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12970 guestMemTotal, guestMemFree,
12971 guestMemBalloon, guestMemShared,
12972 guestMemCache, guestPagedTotal);
12973 aCollector->registerBaseMetric(guestCpuMem);
12974
12975 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12976 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12977 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12978 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12979
12980 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12981 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12982 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12983 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12984
12985 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12986 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12987 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12988 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12989
12990 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12991 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12992 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12993 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12994
12995 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12996 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12997 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12998 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12999
13000 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
13001 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
13002 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
13003 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
13004
13005 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
13006 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
13007 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
13008 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
13009
13010 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
13011 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
13012 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
13013 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
13014
13015 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
13016 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
13017 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
13018 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
13019
13020 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
13021 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
13022 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
13023 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
13024
13025 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
13026 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
13027 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
13028 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
13029}
13030
13031void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
13032{
13033 AssertReturnVoid(isWriteLockOnCurrentThread());
13034
13035 if (aCollector)
13036 {
13037 aCollector->unregisterMetricsFor(aMachine);
13038 aCollector->unregisterBaseMetricsFor(aMachine);
13039 }
13040}
13041
13042#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13043
13044
13045////////////////////////////////////////////////////////////////////////////////
13046
13047DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
13048
13049HRESULT SessionMachine::FinalConstruct()
13050{
13051 LogFlowThisFunc(("\n"));
13052
13053 mClientToken = NULL;
13054
13055 return BaseFinalConstruct();
13056}
13057
13058void SessionMachine::FinalRelease()
13059{
13060 LogFlowThisFunc(("\n"));
13061
13062 Assert(!mClientToken);
13063 /* paranoia, should not hang around any more */
13064 if (mClientToken)
13065 {
13066 delete mClientToken;
13067 mClientToken = NULL;
13068 }
13069
13070 uninit(Uninit::Unexpected);
13071
13072 BaseFinalRelease();
13073}
13074
13075/**
13076 * @note Must be called only by Machine::LockMachine() from its own write lock.
13077 */
13078HRESULT SessionMachine::init(Machine *aMachine)
13079{
13080 LogFlowThisFuncEnter();
13081 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
13082
13083 AssertReturn(aMachine, E_INVALIDARG);
13084
13085 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
13086
13087 /* Enclose the state transition NotReady->InInit->Ready */
13088 AutoInitSpan autoInitSpan(this);
13089 AssertReturn(autoInitSpan.isOk(), E_FAIL);
13090
13091 HRESULT rc = S_OK;
13092
13093 RT_ZERO(mAuthLibCtx);
13094
13095 /* create the machine client token */
13096 try
13097 {
13098 mClientToken = new ClientToken(aMachine, this);
13099 if (!mClientToken->isReady())
13100 {
13101 delete mClientToken;
13102 mClientToken = NULL;
13103 rc = E_FAIL;
13104 }
13105 }
13106 catch (std::bad_alloc &)
13107 {
13108 rc = E_OUTOFMEMORY;
13109 }
13110 if (FAILED(rc))
13111 return rc;
13112
13113 /* memorize the peer Machine */
13114 unconst(mPeer) = aMachine;
13115 /* share the parent pointer */
13116 unconst(mParent) = aMachine->mParent;
13117
13118 /* take the pointers to data to share */
13119 mData.share(aMachine->mData);
13120 mSSData.share(aMachine->mSSData);
13121
13122 mUserData.share(aMachine->mUserData);
13123 mHWData.share(aMachine->mHWData);
13124 mMediumAttachments.share(aMachine->mMediumAttachments);
13125
13126 mStorageControllers.allocate();
13127 for (StorageControllerList::const_iterator
13128 it = aMachine->mStorageControllers->begin();
13129 it != aMachine->mStorageControllers->end();
13130 ++it)
13131 {
13132 ComObjPtr<StorageController> ctl;
13133 ctl.createObject();
13134 ctl->init(this, *it);
13135 mStorageControllers->push_back(ctl);
13136 }
13137
13138 mUSBControllers.allocate();
13139 for (USBControllerList::const_iterator
13140 it = aMachine->mUSBControllers->begin();
13141 it != aMachine->mUSBControllers->end();
13142 ++it)
13143 {
13144 ComObjPtr<USBController> ctl;
13145 ctl.createObject();
13146 ctl->init(this, *it);
13147 mUSBControllers->push_back(ctl);
13148 }
13149
13150 unconst(mBIOSSettings).createObject();
13151 mBIOSSettings->init(this, aMachine->mBIOSSettings);
13152
13153 unconst(mRecordingSettings).createObject();
13154 mRecordingSettings->init(this, aMachine->mRecordingSettings);
13155
13156 unconst(mTrustedPlatformModule).createObject();
13157 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
13158
13159 unconst(mNvramStore).createObject();
13160 mNvramStore->init(this, aMachine->mNvramStore);
13161
13162 /* create another GraphicsAdapter object that will be mutable */
13163 unconst(mGraphicsAdapter).createObject();
13164 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
13165 /* create another VRDEServer object that will be mutable */
13166 unconst(mVRDEServer).createObject();
13167 mVRDEServer->init(this, aMachine->mVRDEServer);
13168 /* create another audio settings object that will be mutable */
13169 unconst(mAudioSettings).createObject();
13170 mAudioSettings->init(this, aMachine->mAudioSettings);
13171 /* create a list of serial ports that will be mutable */
13172 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
13173 {
13174 unconst(mSerialPorts[slot]).createObject();
13175 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
13176 }
13177 /* create a list of parallel ports that will be mutable */
13178 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
13179 {
13180 unconst(mParallelPorts[slot]).createObject();
13181 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
13182 }
13183
13184 /* create another USB device filters object that will be mutable */
13185 unconst(mUSBDeviceFilters).createObject();
13186 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
13187
13188 /* create a list of network adapters that will be mutable */
13189 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
13190 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13191 {
13192 unconst(mNetworkAdapters[slot]).createObject();
13193 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
13194 }
13195
13196 /* create another bandwidth control object that will be mutable */
13197 unconst(mBandwidthControl).createObject();
13198 mBandwidthControl->init(this, aMachine->mBandwidthControl);
13199
13200 unconst(mGuestDebugControl).createObject();
13201 mGuestDebugControl->init(this, aMachine->mGuestDebugControl);
13202
13203 /* default is to delete saved state on Saved -> PoweredOff transition */
13204 mRemoveSavedState = true;
13205
13206 /* Confirm a successful initialization when it's the case */
13207 autoInitSpan.setSucceeded();
13208
13209 miNATNetworksStarted = 0;
13210
13211 LogFlowThisFuncLeave();
13212 return rc;
13213}
13214
13215/**
13216 * Uninitializes this session object. If the reason is other than
13217 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
13218 * or the client watcher code.
13219 *
13220 * @param aReason uninitialization reason
13221 *
13222 * @note Locks mParent + this object for writing.
13223 */
13224void SessionMachine::uninit(Uninit::Reason aReason)
13225{
13226 LogFlowThisFuncEnter();
13227 LogFlowThisFunc(("reason=%d\n", aReason));
13228
13229 /*
13230 * Strongly reference ourselves to prevent this object deletion after
13231 * mData->mSession.mMachine.setNull() below (which can release the last
13232 * reference and call the destructor). Important: this must be done before
13233 * accessing any members (and before AutoUninitSpan that does it as well).
13234 * This self reference will be released as the very last step on return.
13235 */
13236 ComObjPtr<SessionMachine> selfRef;
13237 if (aReason != Uninit::Unexpected)
13238 selfRef = this;
13239
13240 /* Enclose the state transition Ready->InUninit->NotReady */
13241 AutoUninitSpan autoUninitSpan(this);
13242 if (autoUninitSpan.uninitDone())
13243 {
13244 LogFlowThisFunc(("Already uninitialized\n"));
13245 LogFlowThisFuncLeave();
13246 return;
13247 }
13248
13249 if (autoUninitSpan.initFailed())
13250 {
13251 /* We've been called by init() because it's failed. It's not really
13252 * necessary (nor it's safe) to perform the regular uninit sequence
13253 * below, the following is enough.
13254 */
13255 LogFlowThisFunc(("Initialization failed.\n"));
13256 /* destroy the machine client token */
13257 if (mClientToken)
13258 {
13259 delete mClientToken;
13260 mClientToken = NULL;
13261 }
13262 uninitDataAndChildObjects();
13263 mData.free();
13264 unconst(mParent) = NULL;
13265 unconst(mPeer) = NULL;
13266 LogFlowThisFuncLeave();
13267 return;
13268 }
13269
13270 MachineState_T lastState;
13271 {
13272 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
13273 lastState = mData->mMachineState;
13274 }
13275 NOREF(lastState);
13276
13277#ifdef VBOX_WITH_USB
13278 // release all captured USB devices, but do this before requesting the locks below
13279 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
13280 {
13281 /* Console::captureUSBDevices() is called in the VM process only after
13282 * setting the machine state to Starting or Restoring.
13283 * Console::detachAllUSBDevices() will be called upon successful
13284 * termination. So, we need to release USB devices only if there was
13285 * an abnormal termination of a running VM.
13286 *
13287 * This is identical to SessionMachine::DetachAllUSBDevices except
13288 * for the aAbnormal argument. */
13289 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13290 AssertComRC(rc);
13291 NOREF(rc);
13292
13293 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13294 if (service)
13295 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
13296 }
13297#endif /* VBOX_WITH_USB */
13298
13299 // we need to lock this object in uninit() because the lock is shared
13300 // with mPeer (as well as data we modify below). mParent lock is needed
13301 // by several calls to it.
13302 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
13303
13304#ifdef VBOX_WITH_RESOURCE_USAGE_API
13305 /*
13306 * It is safe to call Machine::i_unregisterMetrics() here because
13307 * PerformanceCollector::samplerCallback no longer accesses guest methods
13308 * holding the lock.
13309 */
13310 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
13311 /* The guest must be unregistered after its metrics (@bugref{5949}). */
13312 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
13313 if (mCollectorGuest)
13314 {
13315 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
13316 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
13317 mCollectorGuest = NULL;
13318 }
13319#endif
13320
13321 if (aReason == Uninit::Abnormal)
13322 {
13323 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
13324
13325 /*
13326 * Move the VM to the 'Aborted' machine state unless we are restoring a
13327 * VM that was in the 'Saved' machine state. In that case, if the VM
13328 * fails before reaching either the 'Restoring' machine state or the
13329 * 'Running' machine state then we set the machine state to
13330 * 'AbortedSaved' in order to preserve the saved state file so that the
13331 * VM can be restored in the future.
13332 */
13333 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
13334 i_setMachineState(MachineState_AbortedSaved);
13335 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
13336 i_setMachineState(MachineState_Aborted);
13337 }
13338
13339 // any machine settings modified?
13340 if (mData->flModifications)
13341 {
13342 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
13343 i_rollback(false /* aNotify */);
13344 }
13345
13346 mData->mSession.mPID = NIL_RTPROCESS;
13347
13348 if (aReason == Uninit::Unexpected)
13349 {
13350 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
13351 * client watcher thread to update the set of machines that have open
13352 * sessions. */
13353 mParent->i_updateClientWatcher();
13354 }
13355
13356 /* uninitialize all remote controls */
13357 if (mData->mSession.mRemoteControls.size())
13358 {
13359 LogFlowThisFunc(("Closing remote sessions (%d):\n",
13360 mData->mSession.mRemoteControls.size()));
13361
13362 /* Always restart a the beginning, since the iterator is invalidated
13363 * by using erase(). */
13364 for (Data::Session::RemoteControlList::iterator
13365 it = mData->mSession.mRemoteControls.begin();
13366 it != mData->mSession.mRemoteControls.end();
13367 it = mData->mSession.mRemoteControls.begin())
13368 {
13369 ComPtr<IInternalSessionControl> pControl = *it;
13370 mData->mSession.mRemoteControls.erase(it);
13371 multilock.release();
13372 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
13373 HRESULT rc = pControl->Uninitialize();
13374 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
13375 if (FAILED(rc))
13376 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
13377 multilock.acquire();
13378 }
13379 mData->mSession.mRemoteControls.clear();
13380 }
13381
13382 /* Remove all references to the NAT network service. The service will stop
13383 * if all references (also from other VMs) are removed. */
13384 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
13385 {
13386 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13387 {
13388 BOOL enabled;
13389 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13390 if ( FAILED(hrc)
13391 || !enabled)
13392 continue;
13393
13394 NetworkAttachmentType_T type;
13395 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13396 if ( SUCCEEDED(hrc)
13397 && type == NetworkAttachmentType_NATNetwork)
13398 {
13399 Bstr name;
13400 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13401 if (SUCCEEDED(hrc))
13402 {
13403 multilock.release();
13404 Utf8Str strName(name);
13405 LogRel(("VM '%s' stops using NAT network '%s'\n",
13406 mUserData->s.strName.c_str(), strName.c_str()));
13407 mParent->i_natNetworkRefDec(strName);
13408 multilock.acquire();
13409 }
13410 }
13411 }
13412 }
13413
13414 /*
13415 * An expected uninitialization can come only from #i_checkForDeath().
13416 * Otherwise it means that something's gone really wrong (for example,
13417 * the Session implementation has released the VirtualBox reference
13418 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
13419 * etc). However, it's also possible, that the client releases the IPC
13420 * semaphore correctly (i.e. before it releases the VirtualBox reference),
13421 * but the VirtualBox release event comes first to the server process.
13422 * This case is practically possible, so we should not assert on an
13423 * unexpected uninit, just log a warning.
13424 */
13425
13426 if (aReason == Uninit::Unexpected)
13427 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
13428
13429 if (aReason != Uninit::Normal)
13430 {
13431 mData->mSession.mDirectControl.setNull();
13432 }
13433 else
13434 {
13435 /* this must be null here (see #OnSessionEnd()) */
13436 Assert(mData->mSession.mDirectControl.isNull());
13437 Assert(mData->mSession.mState == SessionState_Unlocking);
13438 Assert(!mData->mSession.mProgress.isNull());
13439 }
13440 if (mData->mSession.mProgress)
13441 {
13442 if (aReason == Uninit::Normal)
13443 mData->mSession.mProgress->i_notifyComplete(S_OK);
13444 else
13445 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
13446 COM_IIDOF(ISession),
13447 getComponentName(),
13448 tr("The VM session was aborted"));
13449 mData->mSession.mProgress.setNull();
13450 }
13451
13452 if (mConsoleTaskData.mProgress)
13453 {
13454 Assert(aReason == Uninit::Abnormal);
13455 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
13456 COM_IIDOF(ISession),
13457 getComponentName(),
13458 tr("The VM session was aborted"));
13459 mConsoleTaskData.mProgress.setNull();
13460 }
13461
13462 /* remove the association between the peer machine and this session machine */
13463 Assert( (SessionMachine*)mData->mSession.mMachine == this
13464 || aReason == Uninit::Unexpected);
13465
13466 /* reset the rest of session data */
13467 mData->mSession.mLockType = LockType_Null;
13468 mData->mSession.mMachine.setNull();
13469 mData->mSession.mState = SessionState_Unlocked;
13470 mData->mSession.mName.setNull();
13471
13472 /* destroy the machine client token before leaving the exclusive lock */
13473 if (mClientToken)
13474 {
13475 delete mClientToken;
13476 mClientToken = NULL;
13477 }
13478
13479 /* fire an event */
13480 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
13481
13482 uninitDataAndChildObjects();
13483
13484 /* free the essential data structure last */
13485 mData.free();
13486
13487 /* release the exclusive lock before setting the below two to NULL */
13488 multilock.release();
13489
13490 unconst(mParent) = NULL;
13491 unconst(mPeer) = NULL;
13492
13493 AuthLibUnload(&mAuthLibCtx);
13494
13495 LogFlowThisFuncLeave();
13496}
13497
13498// util::Lockable interface
13499////////////////////////////////////////////////////////////////////////////////
13500
13501/**
13502 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13503 * with the primary Machine instance (mPeer).
13504 */
13505RWLockHandle *SessionMachine::lockHandle() const
13506{
13507 AssertReturn(mPeer != NULL, NULL);
13508 return mPeer->lockHandle();
13509}
13510
13511// IInternalMachineControl methods
13512////////////////////////////////////////////////////////////////////////////////
13513
13514/**
13515 * Passes collected guest statistics to performance collector object
13516 */
13517HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13518 ULONG aCpuKernel, ULONG aCpuIdle,
13519 ULONG aMemTotal, ULONG aMemFree,
13520 ULONG aMemBalloon, ULONG aMemShared,
13521 ULONG aMemCache, ULONG aPageTotal,
13522 ULONG aAllocVMM, ULONG aFreeVMM,
13523 ULONG aBalloonedVMM, ULONG aSharedVMM,
13524 ULONG aVmNetRx, ULONG aVmNetTx)
13525{
13526#ifdef VBOX_WITH_RESOURCE_USAGE_API
13527 if (mCollectorGuest)
13528 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13529 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13530 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13531 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13532
13533 return S_OK;
13534#else
13535 NOREF(aValidStats);
13536 NOREF(aCpuUser);
13537 NOREF(aCpuKernel);
13538 NOREF(aCpuIdle);
13539 NOREF(aMemTotal);
13540 NOREF(aMemFree);
13541 NOREF(aMemBalloon);
13542 NOREF(aMemShared);
13543 NOREF(aMemCache);
13544 NOREF(aPageTotal);
13545 NOREF(aAllocVMM);
13546 NOREF(aFreeVMM);
13547 NOREF(aBalloonedVMM);
13548 NOREF(aSharedVMM);
13549 NOREF(aVmNetRx);
13550 NOREF(aVmNetTx);
13551 return E_NOTIMPL;
13552#endif
13553}
13554
13555////////////////////////////////////////////////////////////////////////////////
13556//
13557// SessionMachine task records
13558//
13559////////////////////////////////////////////////////////////////////////////////
13560
13561/**
13562 * Task record for saving the machine state.
13563 */
13564class SessionMachine::SaveStateTask
13565 : public Machine::Task
13566{
13567public:
13568 SaveStateTask(SessionMachine *m,
13569 Progress *p,
13570 const Utf8Str &t,
13571 Reason_T enmReason,
13572 const Utf8Str &strStateFilePath)
13573 : Task(m, p, t),
13574 m_enmReason(enmReason),
13575 m_strStateFilePath(strStateFilePath)
13576 {}
13577
13578private:
13579 void handler()
13580 {
13581 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13582 }
13583
13584 Reason_T m_enmReason;
13585 Utf8Str m_strStateFilePath;
13586
13587 friend class SessionMachine;
13588};
13589
13590/**
13591 * Task thread implementation for SessionMachine::SaveState(), called from
13592 * SessionMachine::taskHandler().
13593 *
13594 * @note Locks this object for writing.
13595 *
13596 * @param task
13597 * @return
13598 */
13599void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13600{
13601 LogFlowThisFuncEnter();
13602
13603 AutoCaller autoCaller(this);
13604 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13605 if (FAILED(autoCaller.hrc()))
13606 {
13607 /* we might have been uninitialized because the session was accidentally
13608 * closed by the client, so don't assert */
13609 HRESULT rc = setError(E_FAIL,
13610 tr("The session has been accidentally closed"));
13611 task.m_pProgress->i_notifyComplete(rc);
13612 LogFlowThisFuncLeave();
13613 return;
13614 }
13615
13616 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13617
13618 HRESULT rc = S_OK;
13619
13620 try
13621 {
13622 ComPtr<IInternalSessionControl> directControl;
13623 if (mData->mSession.mLockType == LockType_VM)
13624 directControl = mData->mSession.mDirectControl;
13625 if (directControl.isNull())
13626 throw setError(VBOX_E_INVALID_VM_STATE,
13627 tr("Trying to save state without a running VM"));
13628 alock.release();
13629 BOOL fSuspendedBySave;
13630 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13631 Assert(!fSuspendedBySave);
13632 alock.acquire();
13633
13634 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13635 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13636 throw E_FAIL);
13637
13638 if (SUCCEEDED(rc))
13639 {
13640 mSSData->strStateFilePath = task.m_strStateFilePath;
13641
13642 /* save all VM settings */
13643 rc = i_saveSettings(NULL, alock);
13644 // no need to check whether VirtualBox.xml needs saving also since
13645 // we can't have a name change pending at this point
13646 }
13647 else
13648 {
13649 // On failure, set the state to the state we had at the beginning.
13650 i_setMachineState(task.m_machineStateBackup);
13651 i_updateMachineStateOnClient();
13652
13653 // Delete the saved state file (might have been already created).
13654 // No need to check whether this is shared with a snapshot here
13655 // because we certainly created a fresh saved state file here.
13656 i_deleteFile(task.m_strStateFilePath, true /* fIgnoreFailures */);
13657 }
13658 }
13659 catch (HRESULT aRC) { rc = aRC; }
13660
13661 task.m_pProgress->i_notifyComplete(rc);
13662
13663 LogFlowThisFuncLeave();
13664}
13665
13666/**
13667 * @note Locks this object for writing.
13668 */
13669HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13670{
13671 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13672}
13673
13674HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13675{
13676 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13677
13678 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13679 if (FAILED(rc)) return rc;
13680
13681 if ( mData->mMachineState != MachineState_Running
13682 && mData->mMachineState != MachineState_Paused
13683 )
13684 return setError(VBOX_E_INVALID_VM_STATE,
13685 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13686 Global::stringifyMachineState(mData->mMachineState));
13687
13688 ComObjPtr<Progress> pProgress;
13689 pProgress.createObject();
13690 rc = pProgress->init(i_getVirtualBox(),
13691 static_cast<IMachine *>(this) /* aInitiator */,
13692 tr("Saving the execution state of the virtual machine"),
13693 FALSE /* aCancelable */);
13694 if (FAILED(rc))
13695 return rc;
13696
13697 Utf8Str strStateFilePath;
13698 i_composeSavedStateFilename(strStateFilePath);
13699
13700 /* create and start the task on a separate thread (note that it will not
13701 * start working until we release alock) */
13702 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13703 rc = pTask->createThread();
13704 if (FAILED(rc))
13705 return rc;
13706
13707 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13708 i_setMachineState(MachineState_Saving);
13709 i_updateMachineStateOnClient();
13710
13711 pProgress.queryInterfaceTo(aProgress.asOutParam());
13712
13713 return S_OK;
13714}
13715
13716/**
13717 * @note Locks this object for writing.
13718 */
13719HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13720{
13721 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13722
13723 HRESULT rc = i_checkStateDependency(MutableStateDep);
13724 if (FAILED(rc)) return rc;
13725
13726 if ( mData->mMachineState != MachineState_PoweredOff
13727 && mData->mMachineState != MachineState_Teleported
13728 && mData->mMachineState != MachineState_Aborted
13729 )
13730 return setError(VBOX_E_INVALID_VM_STATE,
13731 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13732 Global::stringifyMachineState(mData->mMachineState));
13733
13734 com::Utf8Str stateFilePathFull;
13735 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13736 if (RT_FAILURE(vrc))
13737 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13738 tr("Invalid saved state file path '%s' (%Rrc)"),
13739 aSavedStateFile.c_str(),
13740 vrc);
13741
13742 mSSData->strStateFilePath = stateFilePathFull;
13743
13744 /* The below i_setMachineState() will detect the state transition and will
13745 * update the settings file */
13746
13747 return i_setMachineState(MachineState_Saved);
13748}
13749
13750/**
13751 * @note Locks this object for writing.
13752 */
13753HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13754{
13755 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13756
13757 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13758 if (FAILED(rc)) return rc;
13759
13760 if ( mData->mMachineState != MachineState_Saved
13761 && mData->mMachineState != MachineState_AbortedSaved)
13762 return setError(VBOX_E_INVALID_VM_STATE,
13763 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13764 Global::stringifyMachineState(mData->mMachineState));
13765
13766 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13767
13768 /*
13769 * Saved -> PoweredOff transition will be detected in the SessionMachine
13770 * and properly handled.
13771 */
13772 rc = i_setMachineState(MachineState_PoweredOff);
13773 return rc;
13774}
13775
13776
13777/**
13778 * @note Locks the same as #i_setMachineState() does.
13779 */
13780HRESULT SessionMachine::updateState(MachineState_T aState)
13781{
13782 return i_setMachineState(aState);
13783}
13784
13785/**
13786 * @note Locks this object for writing.
13787 */
13788HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13789{
13790 IProgress *pProgress(aProgress);
13791
13792 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13793
13794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13795
13796 if (mData->mSession.mState != SessionState_Locked)
13797 return VBOX_E_INVALID_OBJECT_STATE;
13798
13799 if (!mData->mSession.mProgress.isNull())
13800 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13801
13802 /* If we didn't reference the NAT network service yet, add a reference to
13803 * force a start */
13804 if (miNATNetworksStarted < 1)
13805 {
13806 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13807 {
13808 BOOL enabled;
13809 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13810 if ( FAILED(hrc)
13811 || !enabled)
13812 continue;
13813
13814 NetworkAttachmentType_T type;
13815 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13816 if ( SUCCEEDED(hrc)
13817 && type == NetworkAttachmentType_NATNetwork)
13818 {
13819 Bstr name;
13820 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13821 if (SUCCEEDED(hrc))
13822 {
13823 Utf8Str strName(name);
13824 LogRel(("VM '%s' starts using NAT network '%s'\n",
13825 mUserData->s.strName.c_str(), strName.c_str()));
13826 mPeer->lockHandle()->unlockWrite();
13827 mParent->i_natNetworkRefInc(strName);
13828#ifdef RT_LOCK_STRICT
13829 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13830#else
13831 mPeer->lockHandle()->lockWrite();
13832#endif
13833 }
13834 }
13835 }
13836 miNATNetworksStarted++;
13837 }
13838
13839 LogFlowThisFunc(("returns S_OK.\n"));
13840 return S_OK;
13841}
13842
13843/**
13844 * @note Locks this object for writing.
13845 */
13846HRESULT SessionMachine::endPowerUp(LONG aResult)
13847{
13848 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13849
13850 if (mData->mSession.mState != SessionState_Locked)
13851 return VBOX_E_INVALID_OBJECT_STATE;
13852
13853 /* Finalize the LaunchVMProcess progress object. */
13854 if (mData->mSession.mProgress)
13855 {
13856 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13857 mData->mSession.mProgress.setNull();
13858 }
13859
13860 if (SUCCEEDED((HRESULT)aResult))
13861 {
13862#ifdef VBOX_WITH_RESOURCE_USAGE_API
13863 /* The VM has been powered up successfully, so it makes sense
13864 * now to offer the performance metrics for a running machine
13865 * object. Doing it earlier wouldn't be safe. */
13866 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13867 mData->mSession.mPID);
13868#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13869 }
13870
13871 return S_OK;
13872}
13873
13874/**
13875 * @note Locks this object for writing.
13876 */
13877HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13878{
13879 LogFlowThisFuncEnter();
13880
13881 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13882
13883 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13884 E_FAIL);
13885
13886 /* create a progress object to track operation completion */
13887 ComObjPtr<Progress> pProgress;
13888 pProgress.createObject();
13889 pProgress->init(i_getVirtualBox(),
13890 static_cast<IMachine *>(this) /* aInitiator */,
13891 tr("Stopping the virtual machine"),
13892 FALSE /* aCancelable */);
13893
13894 /* fill in the console task data */
13895 mConsoleTaskData.mLastState = mData->mMachineState;
13896 mConsoleTaskData.mProgress = pProgress;
13897
13898 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13899 i_setMachineState(MachineState_Stopping);
13900
13901 pProgress.queryInterfaceTo(aProgress.asOutParam());
13902
13903 return S_OK;
13904}
13905
13906/**
13907 * @note Locks this object for writing.
13908 */
13909HRESULT SessionMachine::endPoweringDown(LONG aResult,
13910 const com::Utf8Str &aErrMsg)
13911{
13912 HRESULT const hrcResult = (HRESULT)aResult;
13913 LogFlowThisFuncEnter();
13914
13915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13916
13917 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13918 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13919 && mConsoleTaskData.mLastState != MachineState_Null,
13920 E_FAIL);
13921
13922 /*
13923 * On failure, set the state to the state we had when BeginPoweringDown()
13924 * was called (this is expected by Console::PowerDown() and the associated
13925 * task). On success the VM process already changed the state to
13926 * MachineState_PoweredOff, so no need to do anything.
13927 */
13928 if (FAILED(hrcResult))
13929 i_setMachineState(mConsoleTaskData.mLastState);
13930
13931 /* notify the progress object about operation completion */
13932 Assert(mConsoleTaskData.mProgress);
13933 if (SUCCEEDED(hrcResult))
13934 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13935 else
13936 {
13937 if (aErrMsg.length())
13938 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13939 COM_IIDOF(ISession),
13940 getComponentName(),
13941 aErrMsg.c_str());
13942 else
13943 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13944 }
13945
13946 /* clear out the temporary saved state data */
13947 mConsoleTaskData.mLastState = MachineState_Null;
13948 mConsoleTaskData.mProgress.setNull();
13949
13950 LogFlowThisFuncLeave();
13951 return S_OK;
13952}
13953
13954
13955/**
13956 * Goes through the USB filters of the given machine to see if the given
13957 * device matches any filter or not.
13958 *
13959 * @note Locks the same as USBController::hasMatchingFilter() does.
13960 */
13961HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13962 BOOL *aMatched,
13963 ULONG *aMaskedInterfaces)
13964{
13965 LogFlowThisFunc(("\n"));
13966
13967#ifdef VBOX_WITH_USB
13968 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13969#else
13970 NOREF(aDevice);
13971 NOREF(aMaskedInterfaces);
13972 *aMatched = FALSE;
13973#endif
13974
13975 return S_OK;
13976}
13977
13978/**
13979 * @note Locks the same as Host::captureUSBDevice() does.
13980 */
13981HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13982{
13983 LogFlowThisFunc(("\n"));
13984
13985#ifdef VBOX_WITH_USB
13986 /* if captureDeviceForVM() fails, it must have set extended error info */
13987 clearError();
13988 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13989 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13990 return rc;
13991
13992 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13993 AssertReturn(service, E_FAIL);
13994 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13995#else
13996 RT_NOREF(aId, aCaptureFilename);
13997 return E_NOTIMPL;
13998#endif
13999}
14000
14001/**
14002 * @note Locks the same as Host::detachUSBDevice() does.
14003 */
14004HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
14005 BOOL aDone)
14006{
14007 LogFlowThisFunc(("\n"));
14008
14009#ifdef VBOX_WITH_USB
14010 USBProxyService *service = mParent->i_host()->i_usbProxyService();
14011 AssertReturn(service, E_FAIL);
14012 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
14013#else
14014 NOREF(aId);
14015 NOREF(aDone);
14016 return E_NOTIMPL;
14017#endif
14018}
14019
14020/**
14021 * Inserts all machine filters to the USB proxy service and then calls
14022 * Host::autoCaptureUSBDevices().
14023 *
14024 * Called by Console from the VM process upon VM startup.
14025 *
14026 * @note Locks what called methods lock.
14027 */
14028HRESULT SessionMachine::autoCaptureUSBDevices()
14029{
14030 LogFlowThisFunc(("\n"));
14031
14032#ifdef VBOX_WITH_USB
14033 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
14034 AssertComRC(rc);
14035 NOREF(rc);
14036
14037 USBProxyService *service = mParent->i_host()->i_usbProxyService();
14038 AssertReturn(service, E_FAIL);
14039 return service->autoCaptureDevicesForVM(this);
14040#else
14041 return S_OK;
14042#endif
14043}
14044
14045/**
14046 * Removes all machine filters from the USB proxy service and then calls
14047 * Host::detachAllUSBDevices().
14048 *
14049 * Called by Console from the VM process upon normal VM termination or by
14050 * SessionMachine::uninit() upon abnormal VM termination (from under the
14051 * Machine/SessionMachine lock).
14052 *
14053 * @note Locks what called methods lock.
14054 */
14055HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
14056{
14057 LogFlowThisFunc(("\n"));
14058
14059#ifdef VBOX_WITH_USB
14060 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
14061 AssertComRC(rc);
14062 NOREF(rc);
14063
14064 USBProxyService *service = mParent->i_host()->i_usbProxyService();
14065 AssertReturn(service, E_FAIL);
14066 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
14067#else
14068 NOREF(aDone);
14069 return S_OK;
14070#endif
14071}
14072
14073/**
14074 * @note Locks this object for writing.
14075 */
14076HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
14077 ComPtr<IProgress> &aProgress)
14078{
14079 LogFlowThisFuncEnter();
14080
14081 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
14082 /*
14083 * We don't assert below because it might happen that a non-direct session
14084 * informs us it is closed right after we've been uninitialized -- it's ok.
14085 */
14086
14087 /* get IInternalSessionControl interface */
14088 ComPtr<IInternalSessionControl> control(aSession);
14089
14090 ComAssertRet(!control.isNull(), E_INVALIDARG);
14091
14092 /* Creating a Progress object requires the VirtualBox lock, and
14093 * thus locking it here is required by the lock order rules. */
14094 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
14095
14096 if (control == mData->mSession.mDirectControl)
14097 {
14098 /* The direct session is being normally closed by the client process
14099 * ----------------------------------------------------------------- */
14100
14101 /* go to the closing state (essential for all open*Session() calls and
14102 * for #i_checkForDeath()) */
14103 Assert(mData->mSession.mState == SessionState_Locked);
14104 mData->mSession.mState = SessionState_Unlocking;
14105
14106 /* set direct control to NULL to release the remote instance */
14107 mData->mSession.mDirectControl.setNull();
14108 LogFlowThisFunc(("Direct control is set to NULL\n"));
14109
14110 if (mData->mSession.mProgress)
14111 {
14112 /* finalize the progress, someone might wait if a frontend
14113 * closes the session before powering on the VM. */
14114 mData->mSession.mProgress->notifyComplete(E_FAIL,
14115 COM_IIDOF(ISession),
14116 getComponentName(),
14117 tr("The VM session was closed before any attempt to power it on"));
14118 mData->mSession.mProgress.setNull();
14119 }
14120
14121 /* Create the progress object the client will use to wait until
14122 * #i_checkForDeath() is called to uninitialize this session object after
14123 * it releases the IPC semaphore.
14124 * Note! Because we're "reusing" mProgress here, this must be a proxy
14125 * object just like for LaunchVMProcess. */
14126 Assert(mData->mSession.mProgress.isNull());
14127 ComObjPtr<ProgressProxy> progress;
14128 progress.createObject();
14129 ComPtr<IUnknown> pPeer(mPeer);
14130 progress->init(mParent, pPeer,
14131 Bstr(tr("Closing session")).raw(),
14132 FALSE /* aCancelable */);
14133 progress.queryInterfaceTo(aProgress.asOutParam());
14134 mData->mSession.mProgress = progress;
14135 }
14136 else
14137 {
14138 /* the remote session is being normally closed */
14139 bool found = false;
14140 for (Data::Session::RemoteControlList::iterator
14141 it = mData->mSession.mRemoteControls.begin();
14142 it != mData->mSession.mRemoteControls.end();
14143 ++it)
14144 {
14145 if (control == *it)
14146 {
14147 found = true;
14148 // This MUST be erase(it), not remove(*it) as the latter
14149 // triggers a very nasty use after free due to the place where
14150 // the value "lives".
14151 mData->mSession.mRemoteControls.erase(it);
14152 break;
14153 }
14154 }
14155 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
14156 E_INVALIDARG);
14157 }
14158
14159 /* signal the client watcher thread, because the client is going away */
14160 mParent->i_updateClientWatcher();
14161
14162 LogFlowThisFuncLeave();
14163 return S_OK;
14164}
14165
14166HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14167 std::vector<com::Utf8Str> &aValues,
14168 std::vector<LONG64> &aTimestamps,
14169 std::vector<com::Utf8Str> &aFlags)
14170{
14171 LogFlowThisFunc(("\n"));
14172
14173#ifdef VBOX_WITH_GUEST_PROPS
14174 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14175
14176 size_t cEntries = mHWData->mGuestProperties.size();
14177 aNames.resize(cEntries);
14178 aValues.resize(cEntries);
14179 aTimestamps.resize(cEntries);
14180 aFlags.resize(cEntries);
14181
14182 size_t i = 0;
14183 for (HWData::GuestPropertyMap::const_iterator
14184 it = mHWData->mGuestProperties.begin();
14185 it != mHWData->mGuestProperties.end();
14186 ++it, ++i)
14187 {
14188 aNames[i] = it->first;
14189 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
14190 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
14191
14192 aValues[i] = it->second.strValue;
14193 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
14194 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
14195
14196 aTimestamps[i] = it->second.mTimestamp;
14197
14198 /* If it is NULL, keep it NULL. */
14199 if (it->second.mFlags)
14200 {
14201 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
14202 GuestPropWriteFlags(it->second.mFlags, szFlags);
14203 aFlags[i] = szFlags;
14204 }
14205 else
14206 aFlags[i] = "";
14207 }
14208 return S_OK;
14209#else
14210 ReturnComNotImplemented();
14211#endif
14212}
14213
14214HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
14215 const com::Utf8Str &aValue,
14216 LONG64 aTimestamp,
14217 const com::Utf8Str &aFlags,
14218 BOOL fWasDeleted)
14219{
14220 LogFlowThisFunc(("\n"));
14221
14222#ifdef VBOX_WITH_GUEST_PROPS
14223 try
14224 {
14225 /*
14226 * Convert input up front.
14227 */
14228 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
14229 if (aFlags.length())
14230 {
14231 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
14232 AssertRCReturn(vrc, E_INVALIDARG);
14233 }
14234
14235 /*
14236 * Now grab the object lock, validate the state and do the update.
14237 */
14238
14239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14240
14241 if (!Global::IsOnline(mData->mMachineState))
14242 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
14243
14244 i_setModified(IsModified_MachineData);
14245 mHWData.backup();
14246
14247 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
14248 if (it != mHWData->mGuestProperties.end())
14249 {
14250 if (!fWasDeleted)
14251 {
14252 it->second.strValue = aValue;
14253 it->second.mTimestamp = aTimestamp;
14254 it->second.mFlags = fFlags;
14255 }
14256 else
14257 mHWData->mGuestProperties.erase(it);
14258
14259 mData->mGuestPropertiesModified = TRUE;
14260 }
14261 else if (!fWasDeleted)
14262 {
14263 HWData::GuestProperty prop;
14264 prop.strValue = aValue;
14265 prop.mTimestamp = aTimestamp;
14266 prop.mFlags = fFlags;
14267
14268 mHWData->mGuestProperties[aName] = prop;
14269 mData->mGuestPropertiesModified = TRUE;
14270 }
14271
14272 alock.release();
14273
14274 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fWasDeleted);
14275 }
14276 catch (...)
14277 {
14278 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
14279 }
14280 return S_OK;
14281#else
14282 ReturnComNotImplemented();
14283#endif
14284}
14285
14286
14287HRESULT SessionMachine::lockMedia()
14288{
14289 AutoMultiWriteLock2 alock(this->lockHandle(),
14290 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14291
14292 AssertReturn( mData->mMachineState == MachineState_Starting
14293 || mData->mMachineState == MachineState_Restoring
14294 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
14295
14296 clearError();
14297 alock.release();
14298 return i_lockMedia();
14299}
14300
14301HRESULT SessionMachine::unlockMedia()
14302{
14303 HRESULT hrc = i_unlockMedia();
14304 return hrc;
14305}
14306
14307HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14308 ComPtr<IMediumAttachment> &aNewAttachment)
14309{
14310 // request the host lock first, since might be calling Host methods for getting host drives;
14311 // next, protect the media tree all the while we're in here, as well as our member variables
14312 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
14313 this->lockHandle(),
14314 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14315
14316 IMediumAttachment *iAttach = aAttachment;
14317 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
14318
14319 Utf8Str ctrlName;
14320 LONG lPort;
14321 LONG lDevice;
14322 bool fTempEject;
14323 {
14324 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14325
14326 /* Need to query the details first, as the IMediumAttachment reference
14327 * might be to the original settings, which we are going to change. */
14328 ctrlName = pAttach->i_getControllerName();
14329 lPort = pAttach->i_getPort();
14330 lDevice = pAttach->i_getDevice();
14331 fTempEject = pAttach->i_getTempEject();
14332 }
14333
14334 if (!fTempEject)
14335 {
14336 /* Remember previously mounted medium. The medium before taking the
14337 * backup is not necessarily the same thing. */
14338 ComObjPtr<Medium> oldmedium;
14339 oldmedium = pAttach->i_getMedium();
14340
14341 i_setModified(IsModified_Storage);
14342 mMediumAttachments.backup();
14343
14344 // The backup operation makes the pAttach reference point to the
14345 // old settings. Re-get the correct reference.
14346 pAttach = i_findAttachment(*mMediumAttachments.data(),
14347 ctrlName,
14348 lPort,
14349 lDevice);
14350
14351 {
14352 AutoCaller autoAttachCaller(this);
14353 if (FAILED(autoAttachCaller.hrc())) return autoAttachCaller.hrc();
14354
14355 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14356 if (!oldmedium.isNull())
14357 oldmedium->i_removeBackReference(mData->mUuid);
14358
14359 pAttach->i_updateMedium(NULL);
14360 pAttach->i_updateEjected();
14361 }
14362
14363 i_setModified(IsModified_Storage);
14364 }
14365 else
14366 {
14367 {
14368 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14369 pAttach->i_updateEjected();
14370 }
14371 }
14372
14373 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
14374
14375 return S_OK;
14376}
14377
14378HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
14379 com::Utf8Str &aResult)
14380{
14381 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14382
14383 HRESULT hr = S_OK;
14384
14385 if (!mAuthLibCtx.hAuthLibrary)
14386 {
14387 /* Load the external authentication library. */
14388 Bstr authLibrary;
14389 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
14390
14391 Utf8Str filename = authLibrary;
14392
14393 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
14394 if (RT_FAILURE(vrc))
14395 hr = setErrorBoth(E_FAIL, vrc,
14396 tr("Could not load the external authentication library '%s' (%Rrc)"),
14397 filename.c_str(), vrc);
14398 }
14399
14400 /* The auth library might need the machine lock. */
14401 alock.release();
14402
14403 if (FAILED(hr))
14404 return hr;
14405
14406 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
14407 {
14408 enum VRDEAuthParams
14409 {
14410 parmUuid = 1,
14411 parmGuestJudgement,
14412 parmUser,
14413 parmPassword,
14414 parmDomain,
14415 parmClientId
14416 };
14417
14418 AuthResult result = AuthResultAccessDenied;
14419
14420 Guid uuid(aAuthParams[parmUuid]);
14421 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
14422 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
14423
14424 result = AuthLibAuthenticate(&mAuthLibCtx,
14425 uuid.raw(), guestJudgement,
14426 aAuthParams[parmUser].c_str(),
14427 aAuthParams[parmPassword].c_str(),
14428 aAuthParams[parmDomain].c_str(),
14429 u32ClientId);
14430
14431 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
14432 size_t cbPassword = aAuthParams[parmPassword].length();
14433 if (cbPassword)
14434 {
14435 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
14436 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
14437 }
14438
14439 if (result == AuthResultAccessGranted)
14440 aResult = "granted";
14441 else
14442 aResult = "denied";
14443
14444 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
14445 aAuthParams[parmUser].c_str(), aResult.c_str()));
14446 }
14447 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
14448 {
14449 enum VRDEAuthDisconnectParams
14450 {
14451 parmUuid = 1,
14452 parmClientId
14453 };
14454
14455 Guid uuid(aAuthParams[parmUuid]);
14456 uint32_t u32ClientId = 0;
14457 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
14458 }
14459 else
14460 {
14461 hr = E_INVALIDARG;
14462 }
14463
14464 return hr;
14465}
14466
14467// public methods only for internal purposes
14468/////////////////////////////////////////////////////////////////////////////
14469
14470#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14471/**
14472 * Called from the client watcher thread to check for expected or unexpected
14473 * death of the client process that has a direct session to this machine.
14474 *
14475 * On Win32 and on OS/2, this method is called only when we've got the
14476 * mutex (i.e. the client has either died or terminated normally) so it always
14477 * returns @c true (the client is terminated, the session machine is
14478 * uninitialized).
14479 *
14480 * On other platforms, the method returns @c true if the client process has
14481 * terminated normally or abnormally and the session machine was uninitialized,
14482 * and @c false if the client process is still alive.
14483 *
14484 * @note Locks this object for writing.
14485 */
14486bool SessionMachine::i_checkForDeath()
14487{
14488 Uninit::Reason reason;
14489 bool terminated = false;
14490
14491 /* Enclose autoCaller with a block because calling uninit() from under it
14492 * will deadlock. */
14493 {
14494 AutoCaller autoCaller(this);
14495 if (!autoCaller.isOk())
14496 {
14497 /* return true if not ready, to cause the client watcher to exclude
14498 * the corresponding session from watching */
14499 LogFlowThisFunc(("Already uninitialized!\n"));
14500 return true;
14501 }
14502
14503 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14504
14505 /* Determine the reason of death: if the session state is Closing here,
14506 * everything is fine. Otherwise it means that the client did not call
14507 * OnSessionEnd() before it released the IPC semaphore. This may happen
14508 * either because the client process has abnormally terminated, or
14509 * because it simply forgot to call ISession::Close() before exiting. We
14510 * threat the latter also as an abnormal termination (see
14511 * Session::uninit() for details). */
14512 reason = mData->mSession.mState == SessionState_Unlocking ?
14513 Uninit::Normal :
14514 Uninit::Abnormal;
14515
14516 if (mClientToken)
14517 terminated = mClientToken->release();
14518 } /* AutoCaller block */
14519
14520 if (terminated)
14521 uninit(reason);
14522
14523 return terminated;
14524}
14525
14526void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14527{
14528 LogFlowThisFunc(("\n"));
14529
14530 strTokenId.setNull();
14531
14532 AutoCaller autoCaller(this);
14533 AssertComRCReturnVoid(autoCaller.hrc());
14534
14535 Assert(mClientToken);
14536 if (mClientToken)
14537 mClientToken->getId(strTokenId);
14538}
14539#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14540IToken *SessionMachine::i_getToken()
14541{
14542 LogFlowThisFunc(("\n"));
14543
14544 AutoCaller autoCaller(this);
14545 AssertComRCReturn(autoCaller.hrc(), NULL);
14546
14547 Assert(mClientToken);
14548 if (mClientToken)
14549 return mClientToken->getToken();
14550 else
14551 return NULL;
14552}
14553#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14554
14555Machine::ClientToken *SessionMachine::i_getClientToken()
14556{
14557 LogFlowThisFunc(("\n"));
14558
14559 AutoCaller autoCaller(this);
14560 AssertComRCReturn(autoCaller.hrc(), NULL);
14561
14562 return mClientToken;
14563}
14564
14565
14566/**
14567 * @note Locks this object for reading.
14568 */
14569HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14570{
14571 LogFlowThisFunc(("\n"));
14572
14573 AutoCaller autoCaller(this);
14574 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14575
14576 ComPtr<IInternalSessionControl> directControl;
14577 {
14578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14579 if (mData->mSession.mLockType == LockType_VM)
14580 directControl = mData->mSession.mDirectControl;
14581 }
14582
14583 /* ignore notifications sent after #OnSessionEnd() is called */
14584 if (!directControl)
14585 return S_OK;
14586
14587 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14588}
14589
14590/**
14591 * @note Locks this object for reading.
14592 */
14593HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
14594 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
14595 const Utf8Str &aGuestIp, LONG aGuestPort)
14596{
14597 LogFlowThisFunc(("\n"));
14598
14599 AutoCaller autoCaller(this);
14600 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14601
14602 ComPtr<IInternalSessionControl> directControl;
14603 {
14604 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14605 if (mData->mSession.mLockType == LockType_VM)
14606 directControl = mData->mSession.mDirectControl;
14607 }
14608
14609 /* ignore notifications sent after #OnSessionEnd() is called */
14610 if (!directControl)
14611 return S_OK;
14612 /*
14613 * instead acting like callback we ask IVirtualBox deliver corresponding event
14614 */
14615
14616 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14617 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14618 return S_OK;
14619}
14620
14621/**
14622 * @note Locks this object for reading.
14623 */
14624HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14625{
14626 LogFlowThisFunc(("\n"));
14627
14628 AutoCaller autoCaller(this);
14629 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14630
14631 ComPtr<IInternalSessionControl> directControl;
14632 {
14633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14634 if (mData->mSession.mLockType == LockType_VM)
14635 directControl = mData->mSession.mDirectControl;
14636 }
14637
14638 /* ignore notifications sent after #OnSessionEnd() is called */
14639 if (!directControl)
14640 return S_OK;
14641
14642 return directControl->OnAudioAdapterChange(audioAdapter);
14643}
14644
14645/**
14646 * @note Locks this object for reading.
14647 */
14648HRESULT SessionMachine::i_onHostAudioDeviceChange(IHostAudioDevice *aDevice, BOOL aNew, AudioDeviceState_T aState, IVirtualBoxErrorInfo *aErrInfo)
14649{
14650 LogFlowThisFunc(("\n"));
14651
14652 AutoCaller autoCaller(this);
14653 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14654
14655 ComPtr<IInternalSessionControl> directControl;
14656 {
14657 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14658 if (mData->mSession.mLockType == LockType_VM)
14659 directControl = mData->mSession.mDirectControl;
14660 }
14661
14662 /* ignore notifications sent after #OnSessionEnd() is called */
14663 if (!directControl)
14664 return S_OK;
14665
14666 return directControl->OnHostAudioDeviceChange(aDevice, aNew, aState, aErrInfo);
14667}
14668
14669/**
14670 * @note Locks this object for reading.
14671 */
14672HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14673{
14674 LogFlowThisFunc(("\n"));
14675
14676 AutoCaller autoCaller(this);
14677 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14678
14679 ComPtr<IInternalSessionControl> directControl;
14680 {
14681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14682 if (mData->mSession.mLockType == LockType_VM)
14683 directControl = mData->mSession.mDirectControl;
14684 }
14685
14686 /* ignore notifications sent after #OnSessionEnd() is called */
14687 if (!directControl)
14688 return S_OK;
14689
14690 return directControl->OnSerialPortChange(serialPort);
14691}
14692
14693/**
14694 * @note Locks this object for reading.
14695 */
14696HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14697{
14698 LogFlowThisFunc(("\n"));
14699
14700 AutoCaller autoCaller(this);
14701 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14702
14703 ComPtr<IInternalSessionControl> directControl;
14704 {
14705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14706 if (mData->mSession.mLockType == LockType_VM)
14707 directControl = mData->mSession.mDirectControl;
14708 }
14709
14710 /* ignore notifications sent after #OnSessionEnd() is called */
14711 if (!directControl)
14712 return S_OK;
14713
14714 return directControl->OnParallelPortChange(parallelPort);
14715}
14716
14717/**
14718 * @note Locks this object for reading.
14719 */
14720HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14721{
14722 LogFlowThisFunc(("\n"));
14723
14724 AutoCaller autoCaller(this);
14725 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14726
14727 ComPtr<IInternalSessionControl> directControl;
14728 {
14729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14730 if (mData->mSession.mLockType == LockType_VM)
14731 directControl = mData->mSession.mDirectControl;
14732 }
14733
14734 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14735
14736 /* ignore notifications sent after #OnSessionEnd() is called */
14737 if (!directControl)
14738 return S_OK;
14739
14740 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14741}
14742
14743/**
14744 * @note Locks this object for reading.
14745 */
14746HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14747{
14748 LogFlowThisFunc(("\n"));
14749
14750 AutoCaller autoCaller(this);
14751 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14752
14753 ComPtr<IInternalSessionControl> directControl;
14754 {
14755 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14756 if (mData->mSession.mLockType == LockType_VM)
14757 directControl = mData->mSession.mDirectControl;
14758 }
14759
14760 mParent->i_onMediumChanged(aAttachment);
14761
14762 /* ignore notifications sent after #OnSessionEnd() is called */
14763 if (!directControl)
14764 return S_OK;
14765
14766 return directControl->OnMediumChange(aAttachment, aForce);
14767}
14768
14769HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14770{
14771 LogFlowThisFunc(("\n"));
14772
14773 AutoCaller autoCaller(this);
14774 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14775
14776 ComPtr<IInternalSessionControl> directControl;
14777 {
14778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14779 if (mData->mSession.mLockType == LockType_VM)
14780 directControl = mData->mSession.mDirectControl;
14781 }
14782
14783 /* ignore notifications sent after #OnSessionEnd() is called */
14784 if (!directControl)
14785 return S_OK;
14786
14787 return directControl->OnVMProcessPriorityChange(aPriority);
14788}
14789
14790/**
14791 * @note Locks this object for reading.
14792 */
14793HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14794{
14795 LogFlowThisFunc(("\n"));
14796
14797 AutoCaller autoCaller(this);
14798 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14799
14800 ComPtr<IInternalSessionControl> directControl;
14801 {
14802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14803 if (mData->mSession.mLockType == LockType_VM)
14804 directControl = mData->mSession.mDirectControl;
14805 }
14806
14807 /* ignore notifications sent after #OnSessionEnd() is called */
14808 if (!directControl)
14809 return S_OK;
14810
14811 return directControl->OnCPUChange(aCPU, aRemove);
14812}
14813
14814HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14815{
14816 LogFlowThisFunc(("\n"));
14817
14818 AutoCaller autoCaller(this);
14819 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14820
14821 ComPtr<IInternalSessionControl> directControl;
14822 {
14823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14824 if (mData->mSession.mLockType == LockType_VM)
14825 directControl = mData->mSession.mDirectControl;
14826 }
14827
14828 /* ignore notifications sent after #OnSessionEnd() is called */
14829 if (!directControl)
14830 return S_OK;
14831
14832 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14833}
14834
14835/**
14836 * @note Locks this object for reading.
14837 */
14838HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14839{
14840 LogFlowThisFunc(("\n"));
14841
14842 AutoCaller autoCaller(this);
14843 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14844
14845 ComPtr<IInternalSessionControl> directControl;
14846 {
14847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14848 if (mData->mSession.mLockType == LockType_VM)
14849 directControl = mData->mSession.mDirectControl;
14850 }
14851
14852 /* ignore notifications sent after #OnSessionEnd() is called */
14853 if (!directControl)
14854 return S_OK;
14855
14856 return directControl->OnVRDEServerChange(aRestart);
14857}
14858
14859/**
14860 * @note Locks this object for reading.
14861 */
14862HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14863{
14864 LogFlowThisFunc(("\n"));
14865
14866 AutoCaller autoCaller(this);
14867 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14868
14869 ComPtr<IInternalSessionControl> directControl;
14870 {
14871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14872 if (mData->mSession.mLockType == LockType_VM)
14873 directControl = mData->mSession.mDirectControl;
14874 }
14875
14876 /* ignore notifications sent after #OnSessionEnd() is called */
14877 if (!directControl)
14878 return S_OK;
14879
14880 return directControl->OnRecordingChange(aEnable);
14881}
14882
14883/**
14884 * @note Locks this object for reading.
14885 */
14886HRESULT SessionMachine::i_onUSBControllerChange()
14887{
14888 LogFlowThisFunc(("\n"));
14889
14890 AutoCaller autoCaller(this);
14891 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14892
14893 ComPtr<IInternalSessionControl> directControl;
14894 {
14895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14896 if (mData->mSession.mLockType == LockType_VM)
14897 directControl = mData->mSession.mDirectControl;
14898 }
14899
14900 /* ignore notifications sent after #OnSessionEnd() is called */
14901 if (!directControl)
14902 return S_OK;
14903
14904 return directControl->OnUSBControllerChange();
14905}
14906
14907/**
14908 * @note Locks this object for reading.
14909 */
14910HRESULT SessionMachine::i_onSharedFolderChange()
14911{
14912 LogFlowThisFunc(("\n"));
14913
14914 AutoCaller autoCaller(this);
14915 AssertComRCReturnRC(autoCaller.hrc());
14916
14917 ComPtr<IInternalSessionControl> directControl;
14918 {
14919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14920 if (mData->mSession.mLockType == LockType_VM)
14921 directControl = mData->mSession.mDirectControl;
14922 }
14923
14924 /* ignore notifications sent after #OnSessionEnd() is called */
14925 if (!directControl)
14926 return S_OK;
14927
14928 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14929}
14930
14931/**
14932 * @note Locks this object for reading.
14933 */
14934HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14935{
14936 LogFlowThisFunc(("\n"));
14937
14938 AutoCaller autoCaller(this);
14939 AssertComRCReturnRC(autoCaller.hrc());
14940
14941 ComPtr<IInternalSessionControl> directControl;
14942 {
14943 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14944 if (mData->mSession.mLockType == LockType_VM)
14945 directControl = mData->mSession.mDirectControl;
14946 }
14947
14948 /* ignore notifications sent after #OnSessionEnd() is called */
14949 if (!directControl)
14950 return S_OK;
14951
14952 return directControl->OnClipboardModeChange(aClipboardMode);
14953}
14954
14955/**
14956 * @note Locks this object for reading.
14957 */
14958HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14959{
14960 LogFlowThisFunc(("\n"));
14961
14962 AutoCaller autoCaller(this);
14963 AssertComRCReturnRC(autoCaller.hrc());
14964
14965 ComPtr<IInternalSessionControl> directControl;
14966 {
14967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14968 if (mData->mSession.mLockType == LockType_VM)
14969 directControl = mData->mSession.mDirectControl;
14970 }
14971
14972 /* ignore notifications sent after #OnSessionEnd() is called */
14973 if (!directControl)
14974 return S_OK;
14975
14976 return directControl->OnClipboardFileTransferModeChange(aEnable);
14977}
14978
14979/**
14980 * @note Locks this object for reading.
14981 */
14982HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14983{
14984 LogFlowThisFunc(("\n"));
14985
14986 AutoCaller autoCaller(this);
14987 AssertComRCReturnRC(autoCaller.hrc());
14988
14989 ComPtr<IInternalSessionControl> directControl;
14990 {
14991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14992 if (mData->mSession.mLockType == LockType_VM)
14993 directControl = mData->mSession.mDirectControl;
14994 }
14995
14996 /* ignore notifications sent after #OnSessionEnd() is called */
14997 if (!directControl)
14998 return S_OK;
14999
15000 return directControl->OnDnDModeChange(aDnDMode);
15001}
15002
15003/**
15004 * @note Locks this object for reading.
15005 */
15006HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
15007{
15008 LogFlowThisFunc(("\n"));
15009
15010 AutoCaller autoCaller(this);
15011 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
15012
15013 ComPtr<IInternalSessionControl> directControl;
15014 {
15015 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15016 if (mData->mSession.mLockType == LockType_VM)
15017 directControl = mData->mSession.mDirectControl;
15018 }
15019
15020 /* ignore notifications sent after #OnSessionEnd() is called */
15021 if (!directControl)
15022 return S_OK;
15023
15024 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
15025}
15026
15027/**
15028 * @note Locks this object for reading.
15029 */
15030HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
15031{
15032 LogFlowThisFunc(("\n"));
15033
15034 AutoCaller autoCaller(this);
15035 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
15036
15037 ComPtr<IInternalSessionControl> directControl;
15038 {
15039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15040 if (mData->mSession.mLockType == LockType_VM)
15041 directControl = mData->mSession.mDirectControl;
15042 }
15043
15044 /* ignore notifications sent after #OnSessionEnd() is called */
15045 if (!directControl)
15046 return S_OK;
15047
15048 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
15049}
15050
15051/**
15052 * @note Locks this object for reading.
15053 */
15054HRESULT SessionMachine::i_onGuestDebugControlChange(IGuestDebugControl *guestDebugControl)
15055{
15056 LogFlowThisFunc(("\n"));
15057
15058 AutoCaller autoCaller(this);
15059 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
15060
15061 ComPtr<IInternalSessionControl> directControl;
15062 {
15063 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15064 if (mData->mSession.mLockType == LockType_VM)
15065 directControl = mData->mSession.mDirectControl;
15066 }
15067
15068 /* ignore notifications sent after #OnSessionEnd() is called */
15069 if (!directControl)
15070 return S_OK;
15071
15072 return directControl->OnGuestDebugControlChange(guestDebugControl);
15073}
15074
15075/**
15076 * Returns @c true if this machine's USB controller reports it has a matching
15077 * filter for the given USB device and @c false otherwise.
15078 *
15079 * @note locks this object for reading.
15080 */
15081bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
15082{
15083 AutoCaller autoCaller(this);
15084 /* silently return if not ready -- this method may be called after the
15085 * direct machine session has been called */
15086 if (!autoCaller.isOk())
15087 return false;
15088
15089#ifdef VBOX_WITH_USB
15090 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15091
15092 switch (mData->mMachineState)
15093 {
15094 case MachineState_Starting:
15095 case MachineState_Restoring:
15096 case MachineState_TeleportingIn:
15097 case MachineState_Paused:
15098 case MachineState_Running:
15099 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
15100 * elsewhere... */
15101 alock.release();
15102 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
15103 default: break;
15104 }
15105#else
15106 NOREF(aDevice);
15107 NOREF(aMaskedIfs);
15108#endif
15109 return false;
15110}
15111
15112/**
15113 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
15114 */
15115HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
15116 IVirtualBoxErrorInfo *aError,
15117 ULONG aMaskedIfs,
15118 const com::Utf8Str &aCaptureFilename)
15119{
15120 LogFlowThisFunc(("\n"));
15121
15122 AutoCaller autoCaller(this);
15123
15124 /* This notification may happen after the machine object has been
15125 * uninitialized (the session was closed), so don't assert. */
15126 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
15127
15128 ComPtr<IInternalSessionControl> directControl;
15129 {
15130 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15131 if (mData->mSession.mLockType == LockType_VM)
15132 directControl = mData->mSession.mDirectControl;
15133 }
15134
15135 /* fail on notifications sent after #OnSessionEnd() is called, it is
15136 * expected by the caller */
15137 if (!directControl)
15138 return E_FAIL;
15139
15140 /* No locks should be held at this point. */
15141 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
15142 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
15143
15144 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
15145}
15146
15147/**
15148 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
15149 */
15150HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
15151 IVirtualBoxErrorInfo *aError)
15152{
15153 LogFlowThisFunc(("\n"));
15154
15155 AutoCaller autoCaller(this);
15156
15157 /* This notification may happen after the machine object has been
15158 * uninitialized (the session was closed), so don't assert. */
15159 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
15160
15161 ComPtr<IInternalSessionControl> directControl;
15162 {
15163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15164 if (mData->mSession.mLockType == LockType_VM)
15165 directControl = mData->mSession.mDirectControl;
15166 }
15167
15168 /* fail on notifications sent after #OnSessionEnd() is called, it is
15169 * expected by the caller */
15170 if (!directControl)
15171 return E_FAIL;
15172
15173 /* No locks should be held at this point. */
15174 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
15175 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
15176
15177 return directControl->OnUSBDeviceDetach(aId, aError);
15178}
15179
15180// protected methods
15181/////////////////////////////////////////////////////////////////////////////
15182
15183/**
15184 * Deletes the given file if it is no longer in use by either the current machine state
15185 * (if the machine is "saved") or any of the machine's snapshots.
15186 *
15187 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
15188 * but is different for each SnapshotMachine. When calling this, the order of calling this
15189 * function on the one hand and changing that variable OR the snapshots tree on the other hand
15190 * is therefore critical. I know, it's all rather messy.
15191 *
15192 * @param strStateFile
15193 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
15194 * the test for whether the saved state file is in use.
15195 */
15196void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
15197 Snapshot *pSnapshotToIgnore)
15198{
15199 // it is safe to delete this saved state file if it is not currently in use by the machine ...
15200 if ( (strStateFile.isNotEmpty())
15201 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
15202 )
15203 // ... and it must also not be shared with other snapshots
15204 if ( !mData->mFirstSnapshot
15205 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
15206 // this checks the SnapshotMachine's state file paths
15207 )
15208 i_deleteFile(strStateFile, true /* fIgnoreFailures */);
15209}
15210
15211/**
15212 * Locks the attached media.
15213 *
15214 * All attached hard disks are locked for writing and DVD/floppy are locked for
15215 * reading. Parents of attached hard disks (if any) are locked for reading.
15216 *
15217 * This method also performs accessibility check of all media it locks: if some
15218 * media is inaccessible, the method will return a failure and a bunch of
15219 * extended error info objects per each inaccessible medium.
15220 *
15221 * Note that this method is atomic: if it returns a success, all media are
15222 * locked as described above; on failure no media is locked at all (all
15223 * succeeded individual locks will be undone).
15224 *
15225 * The caller is responsible for doing the necessary state sanity checks.
15226 *
15227 * The locks made by this method must be undone by calling #unlockMedia() when
15228 * no more needed.
15229 */
15230HRESULT SessionMachine::i_lockMedia()
15231{
15232 AutoCaller autoCaller(this);
15233 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
15234
15235 AutoMultiWriteLock2 alock(this->lockHandle(),
15236 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
15237
15238 /* bail out if trying to lock things with already set up locking */
15239 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
15240
15241 MultiResult mrc(S_OK);
15242
15243 /* Collect locking information for all medium objects attached to the VM. */
15244 for (MediumAttachmentList::const_iterator
15245 it = mMediumAttachments->begin();
15246 it != mMediumAttachments->end();
15247 ++it)
15248 {
15249 MediumAttachment *pAtt = *it;
15250 DeviceType_T devType = pAtt->i_getType();
15251 Medium *pMedium = pAtt->i_getMedium();
15252
15253 MediumLockList *pMediumLockList(new MediumLockList());
15254 // There can be attachments without a medium (floppy/dvd), and thus
15255 // it's impossible to create a medium lock list. It still makes sense
15256 // to have the empty medium lock list in the map in case a medium is
15257 // attached later.
15258 if (pMedium != NULL)
15259 {
15260 MediumType_T mediumType = pMedium->i_getType();
15261 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
15262 || mediumType == MediumType_Shareable;
15263 bool fIsVitalImage = (devType == DeviceType_HardDisk);
15264
15265 alock.release();
15266 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
15267 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
15268 false /* fMediumLockWriteAll */,
15269 NULL,
15270 *pMediumLockList);
15271 alock.acquire();
15272 if (FAILED(mrc))
15273 {
15274 delete pMediumLockList;
15275 mData->mSession.mLockedMedia.Clear();
15276 break;
15277 }
15278 }
15279
15280 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
15281 if (FAILED(rc))
15282 {
15283 mData->mSession.mLockedMedia.Clear();
15284 mrc = setError(rc,
15285 tr("Collecting locking information for all attached media failed"));
15286 break;
15287 }
15288 }
15289
15290 if (SUCCEEDED(mrc))
15291 {
15292 /* Now lock all media. If this fails, nothing is locked. */
15293 alock.release();
15294 HRESULT rc = mData->mSession.mLockedMedia.Lock();
15295 alock.acquire();
15296 if (FAILED(rc))
15297 {
15298 mrc = setError(rc,
15299 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
15300 }
15301 }
15302
15303 return mrc;
15304}
15305
15306/**
15307 * Undoes the locks made by by #lockMedia().
15308 */
15309HRESULT SessionMachine::i_unlockMedia()
15310{
15311 AutoCaller autoCaller(this);
15312 AssertComRCReturn(autoCaller.hrc(),autoCaller.hrc());
15313
15314 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15315
15316 /* we may be holding important error info on the current thread;
15317 * preserve it */
15318 ErrorInfoKeeper eik;
15319
15320 HRESULT rc = mData->mSession.mLockedMedia.Clear();
15321 AssertComRC(rc);
15322 return rc;
15323}
15324
15325/**
15326 * Helper to change the machine state (reimplementation).
15327 *
15328 * @note Locks this object for writing.
15329 * @note This method must not call i_saveSettings or SaveSettings, otherwise
15330 * it can cause crashes in random places due to unexpectedly committing
15331 * the current settings. The caller is responsible for that. The call
15332 * to saveStateSettings is fine, because this method does not commit.
15333 */
15334HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
15335{
15336 LogFlowThisFuncEnter();
15337
15338 AutoCaller autoCaller(this);
15339 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
15340
15341 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15342
15343 MachineState_T oldMachineState = mData->mMachineState;
15344
15345 AssertMsgReturn(oldMachineState != aMachineState,
15346 ("oldMachineState=%s, aMachineState=%s\n",
15347 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
15348 E_FAIL);
15349
15350 HRESULT rc = S_OK;
15351
15352 int stsFlags = 0;
15353 bool deleteSavedState = false;
15354
15355 /* detect some state transitions */
15356
15357 if ( ( ( oldMachineState == MachineState_Saved
15358 || oldMachineState == MachineState_AbortedSaved
15359 )
15360 && aMachineState == MachineState_Restoring
15361 )
15362 || ( ( oldMachineState == MachineState_PoweredOff
15363 || oldMachineState == MachineState_Teleported
15364 || oldMachineState == MachineState_Aborted
15365 )
15366 && ( aMachineState == MachineState_TeleportingIn
15367 || aMachineState == MachineState_Starting
15368 )
15369 )
15370 )
15371 {
15372 /* The EMT thread is about to start */
15373
15374 /* Nothing to do here for now... */
15375
15376 /// @todo NEWMEDIA don't let mDVDDrive and other children
15377 /// change anything when in the Starting/Restoring state
15378 }
15379 else if ( ( oldMachineState == MachineState_Running
15380 || oldMachineState == MachineState_Paused
15381 || oldMachineState == MachineState_Teleporting
15382 || oldMachineState == MachineState_OnlineSnapshotting
15383 || oldMachineState == MachineState_LiveSnapshotting
15384 || oldMachineState == MachineState_Stuck
15385 || oldMachineState == MachineState_Starting
15386 || oldMachineState == MachineState_Stopping
15387 || oldMachineState == MachineState_Saving
15388 || oldMachineState == MachineState_Restoring
15389 || oldMachineState == MachineState_TeleportingPausedVM
15390 || oldMachineState == MachineState_TeleportingIn
15391 )
15392 && ( aMachineState == MachineState_PoweredOff
15393 || aMachineState == MachineState_Saved
15394 || aMachineState == MachineState_Teleported
15395 || aMachineState == MachineState_Aborted
15396 || aMachineState == MachineState_AbortedSaved
15397 )
15398 )
15399 {
15400 /* The EMT thread has just stopped, unlock attached media. Note that as
15401 * opposed to locking that is done from Console, we do unlocking here
15402 * because the VM process may have aborted before having a chance to
15403 * properly unlock all media it locked. */
15404
15405 unlockMedia();
15406 }
15407
15408 if (oldMachineState == MachineState_Restoring)
15409 {
15410 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
15411 {
15412 /*
15413 * delete the saved state file once the machine has finished
15414 * restoring from it (note that Console sets the state from
15415 * Restoring to AbortedSaved if the VM couldn't restore successfully,
15416 * to give the user an ability to fix an error and retry --
15417 * we keep the saved state file in this case)
15418 */
15419 deleteSavedState = true;
15420 }
15421 }
15422 else if ( oldMachineState == MachineState_Saved
15423 && ( aMachineState == MachineState_PoweredOff
15424 || aMachineState == MachineState_Teleported
15425 )
15426 )
15427 {
15428 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
15429 deleteSavedState = true;
15430 mData->mCurrentStateModified = TRUE;
15431 stsFlags |= SaveSTS_CurStateModified;
15432 }
15433 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
15434 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
15435
15436 if ( aMachineState == MachineState_Starting
15437 || aMachineState == MachineState_Restoring
15438 || aMachineState == MachineState_TeleportingIn
15439 )
15440 {
15441 /* set the current state modified flag to indicate that the current
15442 * state is no more identical to the state in the
15443 * current snapshot */
15444 if (!mData->mCurrentSnapshot.isNull())
15445 {
15446 mData->mCurrentStateModified = TRUE;
15447 stsFlags |= SaveSTS_CurStateModified;
15448 }
15449 }
15450
15451 if (deleteSavedState)
15452 {
15453 if (mRemoveSavedState)
15454 {
15455 Assert(!mSSData->strStateFilePath.isEmpty());
15456
15457 // it is safe to delete the saved state file if ...
15458 if ( !mData->mFirstSnapshot // ... we have no snapshots or
15459 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
15460 // ... none of the snapshots share the saved state file
15461 )
15462 i_deleteFile(mSSData->strStateFilePath, true /* fIgnoreFailures */);
15463 }
15464
15465 mSSData->strStateFilePath.setNull();
15466 stsFlags |= SaveSTS_StateFilePath;
15467 }
15468
15469 /* redirect to the underlying peer machine */
15470 mPeer->i_setMachineState(aMachineState);
15471
15472 if ( oldMachineState != MachineState_RestoringSnapshot
15473 && ( aMachineState == MachineState_PoweredOff
15474 || aMachineState == MachineState_Teleported
15475 || aMachineState == MachineState_Aborted
15476 || aMachineState == MachineState_AbortedSaved
15477 || aMachineState == MachineState_Saved))
15478 {
15479 /* the machine has stopped execution
15480 * (or the saved state file was adopted) */
15481 stsFlags |= SaveSTS_StateTimeStamp;
15482 }
15483
15484 if ( ( oldMachineState == MachineState_PoweredOff
15485 || oldMachineState == MachineState_Aborted
15486 || oldMachineState == MachineState_Teleported
15487 )
15488 && aMachineState == MachineState_Saved)
15489 {
15490 /* the saved state file was adopted */
15491 Assert(!mSSData->strStateFilePath.isEmpty());
15492 stsFlags |= SaveSTS_StateFilePath;
15493 }
15494
15495#ifdef VBOX_WITH_GUEST_PROPS
15496 if ( aMachineState == MachineState_PoweredOff
15497 || aMachineState == MachineState_Aborted
15498 || aMachineState == MachineState_Teleported)
15499 {
15500 /* Make sure any transient guest properties get removed from the
15501 * property store on shutdown. */
15502 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
15503
15504 /* remove it from the settings representation */
15505 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
15506 for (settings::GuestPropertiesList::iterator
15507 it = llGuestProperties.begin();
15508 it != llGuestProperties.end();
15509 /*nothing*/)
15510 {
15511 const settings::GuestProperty &prop = *it;
15512 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
15513 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
15514 {
15515 it = llGuestProperties.erase(it);
15516 fNeedsSaving = true;
15517 }
15518 else
15519 {
15520 ++it;
15521 }
15522 }
15523
15524 /* Additionally remove it from the HWData representation. Required to
15525 * keep everything in sync, as this is what the API keeps using. */
15526 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
15527 for (HWData::GuestPropertyMap::iterator
15528 it = llHWGuestProperties.begin();
15529 it != llHWGuestProperties.end();
15530 /*nothing*/)
15531 {
15532 uint32_t fFlags = it->second.mFlags;
15533 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
15534 {
15535 /* iterator where we need to continue after the erase call
15536 * (C++03 is a fact still, and it doesn't return the iterator
15537 * which would allow continuing) */
15538 HWData::GuestPropertyMap::iterator it2 = it;
15539 ++it2;
15540 llHWGuestProperties.erase(it);
15541 it = it2;
15542 fNeedsSaving = true;
15543 }
15544 else
15545 {
15546 ++it;
15547 }
15548 }
15549
15550 if (fNeedsSaving)
15551 {
15552 mData->mCurrentStateModified = TRUE;
15553 stsFlags |= SaveSTS_CurStateModified;
15554 }
15555 }
15556#endif /* VBOX_WITH_GUEST_PROPS */
15557
15558 rc = i_saveStateSettings(stsFlags);
15559
15560 if ( ( oldMachineState != MachineState_PoweredOff
15561 && oldMachineState != MachineState_Aborted
15562 && oldMachineState != MachineState_Teleported
15563 )
15564 && ( aMachineState == MachineState_PoweredOff
15565 || aMachineState == MachineState_Aborted
15566 || aMachineState == MachineState_Teleported
15567 )
15568 )
15569 {
15570 /* we've been shut down for any reason */
15571 /* no special action so far */
15572 }
15573
15574 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, ::stringifyMachineState(mData->mMachineState) ));
15575 LogFlowThisFuncLeave();
15576 return rc;
15577}
15578
15579/**
15580 * Sends the current machine state value to the VM process.
15581 *
15582 * @note Locks this object for reading, then calls a client process.
15583 */
15584HRESULT SessionMachine::i_updateMachineStateOnClient()
15585{
15586 AutoCaller autoCaller(this);
15587 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
15588
15589 ComPtr<IInternalSessionControl> directControl;
15590 {
15591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15592 AssertReturn(!!mData, E_FAIL);
15593 if (mData->mSession.mLockType == LockType_VM)
15594 directControl = mData->mSession.mDirectControl;
15595
15596 /* directControl may be already set to NULL here in #OnSessionEnd()
15597 * called too early by the direct session process while there is still
15598 * some operation (like deleting the snapshot) in progress. The client
15599 * process in this case is waiting inside Session::close() for the
15600 * "end session" process object to complete, while #uninit() called by
15601 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15602 * operation to complete. For now, we accept this inconsistent behavior
15603 * and simply do nothing here. */
15604
15605 if (mData->mSession.mState == SessionState_Unlocking)
15606 return S_OK;
15607 }
15608
15609 /* ignore notifications sent after #OnSessionEnd() is called */
15610 if (!directControl)
15611 return S_OK;
15612
15613 return directControl->UpdateMachineState(mData->mMachineState);
15614}
15615
15616
15617/*static*/
15618HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15619{
15620 va_list args;
15621 va_start(args, pcszMsg);
15622 HRESULT rc = setErrorInternalV(aResultCode,
15623 getStaticClassIID(),
15624 getStaticComponentName(),
15625 pcszMsg, args,
15626 false /* aWarning */,
15627 true /* aLogIt */);
15628 va_end(args);
15629 return rc;
15630}
15631
15632
15633HRESULT Machine::updateState(MachineState_T aState)
15634{
15635 NOREF(aState);
15636 ReturnComNotImplemented();
15637}
15638
15639HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15640{
15641 NOREF(aProgress);
15642 ReturnComNotImplemented();
15643}
15644
15645HRESULT Machine::endPowerUp(LONG aResult)
15646{
15647 NOREF(aResult);
15648 ReturnComNotImplemented();
15649}
15650
15651HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15652{
15653 NOREF(aProgress);
15654 ReturnComNotImplemented();
15655}
15656
15657HRESULT Machine::endPoweringDown(LONG aResult,
15658 const com::Utf8Str &aErrMsg)
15659{
15660 NOREF(aResult);
15661 NOREF(aErrMsg);
15662 ReturnComNotImplemented();
15663}
15664
15665HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15666 BOOL *aMatched,
15667 ULONG *aMaskedInterfaces)
15668{
15669 NOREF(aDevice);
15670 NOREF(aMatched);
15671 NOREF(aMaskedInterfaces);
15672 ReturnComNotImplemented();
15673
15674}
15675
15676HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15677{
15678 NOREF(aId); NOREF(aCaptureFilename);
15679 ReturnComNotImplemented();
15680}
15681
15682HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15683 BOOL aDone)
15684{
15685 NOREF(aId);
15686 NOREF(aDone);
15687 ReturnComNotImplemented();
15688}
15689
15690HRESULT Machine::autoCaptureUSBDevices()
15691{
15692 ReturnComNotImplemented();
15693}
15694
15695HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15696{
15697 NOREF(aDone);
15698 ReturnComNotImplemented();
15699}
15700
15701HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15702 ComPtr<IProgress> &aProgress)
15703{
15704 NOREF(aSession);
15705 NOREF(aProgress);
15706 ReturnComNotImplemented();
15707}
15708
15709HRESULT Machine::finishOnlineMergeMedium()
15710{
15711 ReturnComNotImplemented();
15712}
15713
15714HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15715 std::vector<com::Utf8Str> &aValues,
15716 std::vector<LONG64> &aTimestamps,
15717 std::vector<com::Utf8Str> &aFlags)
15718{
15719 NOREF(aNames);
15720 NOREF(aValues);
15721 NOREF(aTimestamps);
15722 NOREF(aFlags);
15723 ReturnComNotImplemented();
15724}
15725
15726HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15727 const com::Utf8Str &aValue,
15728 LONG64 aTimestamp,
15729 const com::Utf8Str &aFlags,
15730 BOOL fWasDeleted)
15731{
15732 NOREF(aName);
15733 NOREF(aValue);
15734 NOREF(aTimestamp);
15735 NOREF(aFlags);
15736 NOREF(fWasDeleted);
15737 ReturnComNotImplemented();
15738}
15739
15740HRESULT Machine::lockMedia()
15741{
15742 ReturnComNotImplemented();
15743}
15744
15745HRESULT Machine::unlockMedia()
15746{
15747 ReturnComNotImplemented();
15748}
15749
15750HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15751 ComPtr<IMediumAttachment> &aNewAttachment)
15752{
15753 NOREF(aAttachment);
15754 NOREF(aNewAttachment);
15755 ReturnComNotImplemented();
15756}
15757
15758HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15759 ULONG aCpuUser,
15760 ULONG aCpuKernel,
15761 ULONG aCpuIdle,
15762 ULONG aMemTotal,
15763 ULONG aMemFree,
15764 ULONG aMemBalloon,
15765 ULONG aMemShared,
15766 ULONG aMemCache,
15767 ULONG aPagedTotal,
15768 ULONG aMemAllocTotal,
15769 ULONG aMemFreeTotal,
15770 ULONG aMemBalloonTotal,
15771 ULONG aMemSharedTotal,
15772 ULONG aVmNetRx,
15773 ULONG aVmNetTx)
15774{
15775 NOREF(aValidStats);
15776 NOREF(aCpuUser);
15777 NOREF(aCpuKernel);
15778 NOREF(aCpuIdle);
15779 NOREF(aMemTotal);
15780 NOREF(aMemFree);
15781 NOREF(aMemBalloon);
15782 NOREF(aMemShared);
15783 NOREF(aMemCache);
15784 NOREF(aPagedTotal);
15785 NOREF(aMemAllocTotal);
15786 NOREF(aMemFreeTotal);
15787 NOREF(aMemBalloonTotal);
15788 NOREF(aMemSharedTotal);
15789 NOREF(aVmNetRx);
15790 NOREF(aVmNetTx);
15791 ReturnComNotImplemented();
15792}
15793
15794HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15795 com::Utf8Str &aResult)
15796{
15797 NOREF(aAuthParams);
15798 NOREF(aResult);
15799 ReturnComNotImplemented();
15800}
15801
15802com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15803{
15804 com::Utf8Str strControllerName = "Unknown";
15805 switch (aBusType)
15806 {
15807 case StorageBus_IDE:
15808 {
15809 strControllerName = "IDE";
15810 break;
15811 }
15812 case StorageBus_SATA:
15813 {
15814 strControllerName = "SATA";
15815 break;
15816 }
15817 case StorageBus_SCSI:
15818 {
15819 strControllerName = "SCSI";
15820 break;
15821 }
15822 case StorageBus_Floppy:
15823 {
15824 strControllerName = "Floppy";
15825 break;
15826 }
15827 case StorageBus_SAS:
15828 {
15829 strControllerName = "SAS";
15830 break;
15831 }
15832 case StorageBus_USB:
15833 {
15834 strControllerName = "USB";
15835 break;
15836 }
15837 default:
15838 break;
15839 }
15840 return strControllerName;
15841}
15842
15843HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15844{
15845 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15846
15847 AutoCaller autoCaller(this);
15848 AssertComRCReturn(autoCaller.hrc(),autoCaller.hrc());
15849
15850 HRESULT rc = S_OK;
15851
15852 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15853 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15854 rc = getUSBDeviceFilters(usbDeviceFilters);
15855 if (FAILED(rc)) return rc;
15856
15857 NOREF(aFlags);
15858 com::Utf8Str osTypeId;
15859 ComObjPtr<GuestOSType> osType = NULL;
15860
15861 /* Get the guest os type as a string from the VB. */
15862 rc = getOSTypeId(osTypeId);
15863 if (FAILED(rc)) return rc;
15864
15865 /* Get the os type obj that coresponds, can be used to get
15866 * the defaults for this guest OS. */
15867 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15868 if (FAILED(rc)) return rc;
15869
15870 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15871
15872 /* Let the OS type select 64-bit ness. */
15873 mHWData->mLongMode = osType->i_is64Bit()
15874 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15875
15876 /* Let the OS type enable the X2APIC */
15877 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15878
15879 /* This one covers IOAPICEnabled. */
15880 mBIOSSettings->i_applyDefaults(osType);
15881
15882 /* Initialize default record settings. */
15883 mRecordingSettings->i_applyDefaults();
15884
15885 /* Initialize default BIOS settings here */
15886 /* Hardware virtualization must be ON by default */
15887 mHWData->mAPIC = true;
15888 mHWData->mHWVirtExEnabled = true;
15889
15890 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15891 if (FAILED(rc)) return rc;
15892
15893 rc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15894 if (FAILED(rc)) return rc;
15895
15896 /* Graphics stuff. */
15897 GraphicsControllerType_T graphicsController;
15898 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15899 if (FAILED(rc)) return rc;
15900
15901 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15902 if (FAILED(rc)) return rc;
15903
15904 ULONG vramSize;
15905 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15906 if (FAILED(rc)) return rc;
15907
15908 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15909 if (FAILED(rc)) return rc;
15910
15911 BOOL fAccelerate2DVideoEnabled;
15912 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15913 if (FAILED(rc)) return rc;
15914
15915 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15916 if (FAILED(rc)) return rc;
15917
15918 BOOL fAccelerate3DEnabled;
15919 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15920 if (FAILED(rc)) return rc;
15921
15922 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15923 if (FAILED(rc)) return rc;
15924
15925 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15926 if (FAILED(rc)) return rc;
15927
15928 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15929 if (FAILED(rc)) return rc;
15930
15931 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15932 if (FAILED(rc)) return rc;
15933
15934 BOOL mRTCUseUTC;
15935 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15936 if (FAILED(rc)) return rc;
15937
15938 setRTCUseUTC(mRTCUseUTC);
15939 if (FAILED(rc)) return rc;
15940
15941 /* the setter does more than just the assignment, so use it */
15942 ChipsetType_T enmChipsetType;
15943 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15944 if (FAILED(rc)) return rc;
15945
15946 rc = COMSETTER(ChipsetType)(enmChipsetType);
15947 if (FAILED(rc)) return rc;
15948
15949 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15950 if (FAILED(rc)) return rc;
15951
15952 /* Apply IOMMU defaults. */
15953 IommuType_T enmIommuType;
15954 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15955 if (FAILED(rc)) return rc;
15956
15957 rc = COMSETTER(IommuType)(enmIommuType);
15958 if (FAILED(rc)) return rc;
15959
15960 /* Apply network adapters defaults */
15961 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15962 mNetworkAdapters[slot]->i_applyDefaults(osType);
15963
15964 /* Apply serial port defaults */
15965 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15966 mSerialPorts[slot]->i_applyDefaults(osType);
15967
15968 /* Apply parallel port defaults - not OS dependent*/
15969 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15970 mParallelPorts[slot]->i_applyDefaults();
15971
15972 /* This one covers the TPM type. */
15973 mTrustedPlatformModule->i_applyDefaults(osType);
15974
15975 /* This one covers secure boot. */
15976 rc = mNvramStore->i_applyDefaults(osType);
15977 if (FAILED(rc)) return rc;
15978
15979 /* Audio stuff. */
15980 rc = mAudioSettings->i_applyDefaults(osType);
15981 if (FAILED(rc)) return rc;
15982
15983 /* Storage Controllers */
15984 StorageControllerType_T hdStorageControllerType;
15985 StorageBus_T hdStorageBusType;
15986 StorageControllerType_T dvdStorageControllerType;
15987 StorageBus_T dvdStorageBusType;
15988 BOOL recommendedFloppy;
15989 ComPtr<IStorageController> floppyController;
15990 ComPtr<IStorageController> hdController;
15991 ComPtr<IStorageController> dvdController;
15992 Utf8Str strFloppyName, strDVDName, strHDName;
15993
15994 /* GUI auto generates controller names using bus type. Do the same*/
15995 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15996
15997 /* Floppy recommended? add one. */
15998 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15999 if (FAILED(rc)) return rc;
16000 if (recommendedFloppy)
16001 {
16002 rc = addStorageController(strFloppyName,
16003 StorageBus_Floppy,
16004 floppyController);
16005 if (FAILED(rc)) return rc;
16006 }
16007
16008 /* Setup one DVD storage controller. */
16009 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
16010 if (FAILED(rc)) return rc;
16011
16012 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
16013 if (FAILED(rc)) return rc;
16014
16015 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
16016
16017 rc = addStorageController(strDVDName,
16018 dvdStorageBusType,
16019 dvdController);
16020 if (FAILED(rc)) return rc;
16021
16022 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
16023 if (FAILED(rc)) return rc;
16024
16025 /* Setup one HDD storage controller. */
16026 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
16027 if (FAILED(rc)) return rc;
16028
16029 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
16030 if (FAILED(rc)) return rc;
16031
16032 strHDName = i_controllerNameFromBusType(hdStorageBusType);
16033
16034 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
16035 {
16036 rc = addStorageController(strHDName,
16037 hdStorageBusType,
16038 hdController);
16039 if (FAILED(rc)) return rc;
16040
16041 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
16042 if (FAILED(rc)) return rc;
16043 }
16044 else
16045 {
16046 /* The HD controller is the same as DVD: */
16047 hdController = dvdController;
16048 }
16049
16050 /* Limit the AHCI port count if it's used because windows has trouble with
16051 * too many ports and other guest (OS X in particular) may take extra long
16052 * boot: */
16053
16054 // pParent = static_cast<Medium*>(aP)
16055 IStorageController *temp = hdController;
16056 ComObjPtr<StorageController> storageController;
16057 storageController = static_cast<StorageController *>(temp);
16058
16059 // tempHDController = aHDController;
16060 if (hdStorageControllerType == StorageControllerType_IntelAhci)
16061 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
16062 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
16063 storageController->COMSETTER(PortCount)(1);
16064
16065 /* USB stuff */
16066
16067 bool ohciEnabled = false;
16068
16069 ComPtr<IUSBController> usbController;
16070 BOOL recommendedUSB3;
16071 BOOL recommendedUSB;
16072 BOOL usbProxyAvailable;
16073
16074 getUSBProxyAvailable(&usbProxyAvailable);
16075 if (FAILED(rc)) return rc;
16076
16077 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
16078 if (FAILED(rc)) return rc;
16079 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
16080 if (FAILED(rc)) return rc;
16081
16082 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
16083 {
16084 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
16085 if (FAILED(rc)) return rc;
16086
16087 /* xHci includes OHCI */
16088 ohciEnabled = true;
16089 }
16090 if ( !ohciEnabled
16091 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
16092 {
16093 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16094 if (FAILED(rc)) return rc;
16095 ohciEnabled = true;
16096
16097 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
16098 if (FAILED(rc)) return rc;
16099 }
16100
16101 /* Set recommended human interface device types: */
16102 BOOL recommendedUSBHID;
16103 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
16104 if (FAILED(rc)) return rc;
16105
16106 if (recommendedUSBHID)
16107 {
16108 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
16109 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
16110 if (!ohciEnabled && !usbDeviceFilters.isNull())
16111 {
16112 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16113 if (FAILED(rc)) return rc;
16114 }
16115 }
16116
16117 BOOL recommendedUSBTablet;
16118 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
16119 if (FAILED(rc)) return rc;
16120
16121 if (recommendedUSBTablet)
16122 {
16123 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
16124 if (!ohciEnabled && !usbDeviceFilters.isNull())
16125 {
16126 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16127 if (FAILED(rc)) return rc;
16128 }
16129 }
16130
16131 /* Enable the VMMDev testing feature for bootsector VMs: */
16132 if (osTypeId == "VBoxBS_64")
16133 {
16134 rc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
16135 if (FAILED(rc))
16136 return rc;
16137 }
16138
16139 return S_OK;
16140}
16141
16142#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
16143/**
16144 * Task record for change encryption settins.
16145 */
16146class Machine::ChangeEncryptionTask
16147 : public Machine::Task
16148{
16149public:
16150 ChangeEncryptionTask(Machine *m,
16151 Progress *p,
16152 const Utf8Str &t,
16153 const com::Utf8Str &aCurrentPassword,
16154 const com::Utf8Str &aCipher,
16155 const com::Utf8Str &aNewPassword,
16156 const com::Utf8Str &aNewPasswordId,
16157 const BOOL aForce,
16158 const MediaList &llMedia)
16159 : Task(m, p, t),
16160 mstrNewPassword(aNewPassword),
16161 mstrCurrentPassword(aCurrentPassword),
16162 mstrCipher(aCipher),
16163 mstrNewPasswordId(aNewPasswordId),
16164 mForce(aForce),
16165 mllMedia(llMedia)
16166 {}
16167
16168 ~ChangeEncryptionTask()
16169 {
16170 if (mstrNewPassword.length())
16171 RTMemWipeThoroughly(mstrNewPassword.mutableRaw(), mstrNewPassword.length(), 10 /* cPasses */);
16172 if (mstrCurrentPassword.length())
16173 RTMemWipeThoroughly(mstrCurrentPassword.mutableRaw(), mstrCurrentPassword.length(), 10 /* cPasses */);
16174 if (m_pCryptoIf)
16175 {
16176 m_pMachine->i_getVirtualBox()->i_releaseCryptoIf(m_pCryptoIf);
16177 m_pCryptoIf = NULL;
16178 }
16179 }
16180
16181 Utf8Str mstrNewPassword;
16182 Utf8Str mstrCurrentPassword;
16183 Utf8Str mstrCipher;
16184 Utf8Str mstrNewPasswordId;
16185 BOOL mForce;
16186 MediaList mllMedia;
16187 PCVBOXCRYPTOIF m_pCryptoIf;
16188private:
16189 void handler()
16190 {
16191 try
16192 {
16193 m_pMachine->i_changeEncryptionHandler(*this);
16194 }
16195 catch (...)
16196 {
16197 LogRel(("Some exception in the function Machine::i_changeEncryptionHandler()\n"));
16198 }
16199 }
16200
16201 friend void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task);
16202};
16203
16204/**
16205 * Scans specified directory and fills list by files found
16206 *
16207 * @returns VBox status code.
16208 * @param lstFiles
16209 * @param strDir
16210 * @param filePattern
16211 */
16212int Machine::i_findFiles(std::list<com::Utf8Str> &lstFiles, const com::Utf8Str &strDir,
16213 const com::Utf8Str &strPattern)
16214{
16215 /* To get all entries including subdirectories. */
16216 char *pszFilePattern = RTPathJoinA(strDir.c_str(), "*");
16217 if (!pszFilePattern)
16218 return VERR_NO_STR_MEMORY;
16219
16220 PRTDIRENTRYEX pDirEntry = NULL;
16221 RTDIR hDir;
16222 size_t cbDirEntry = sizeof(RTDIRENTRYEX);
16223 int rc = RTDirOpenFiltered(&hDir, pszFilePattern, RTDIRFILTER_WINNT, 0 /*fFlags*/);
16224 if (RT_SUCCESS(rc))
16225 {
16226 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
16227 if (pDirEntry)
16228 {
16229 while ( (rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK))
16230 != VERR_NO_MORE_FILES)
16231 {
16232 char *pszFilePath = NULL;
16233
16234 if (rc == VERR_BUFFER_OVERFLOW)
16235 {
16236 /* allocate new buffer. */
16237 RTMemFree(pDirEntry);
16238 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbDirEntry);
16239 if (!pDirEntry)
16240 {
16241 rc = VERR_NO_MEMORY;
16242 break;
16243 }
16244 /* Retry. */
16245 rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
16246 if (RT_FAILURE(rc))
16247 break;
16248 }
16249 else if (RT_FAILURE(rc))
16250 break;
16251
16252 /* Exclude . and .. */
16253 if ( (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '\0')
16254 || (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '.' && pDirEntry->szName[2] == '\0'))
16255 continue;
16256 if (RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode))
16257 {
16258 char *pszSubDirPath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
16259 if (!pszSubDirPath)
16260 {
16261 rc = VERR_NO_STR_MEMORY;
16262 break;
16263 }
16264 rc = i_findFiles(lstFiles, pszSubDirPath, strPattern);
16265 RTMemFree(pszSubDirPath);
16266 if (RT_FAILURE(rc))
16267 break;
16268 continue;
16269 }
16270
16271 /* We got the new entry. */
16272 if (!RTFS_IS_FILE(pDirEntry->Info.Attr.fMode))
16273 continue;
16274
16275 if (!RTStrSimplePatternMatch(strPattern.c_str(), pDirEntry->szName))
16276 continue;
16277
16278 /* Prepend the path to the libraries. */
16279 pszFilePath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
16280 if (!pszFilePath)
16281 {
16282 rc = VERR_NO_STR_MEMORY;
16283 break;
16284 }
16285
16286 lstFiles.push_back(pszFilePath);
16287 RTStrFree(pszFilePath);
16288 }
16289
16290 RTMemFree(pDirEntry);
16291 }
16292 else
16293 rc = VERR_NO_MEMORY;
16294
16295 RTDirClose(hDir);
16296 }
16297 else
16298 {
16299 /* On Windows the above immediately signals that there are no
16300 * files matching, while on other platforms enumerating the
16301 * files below fails. Either way: stop searching. */
16302 }
16303
16304 if ( rc == VERR_NO_MORE_FILES
16305 || rc == VERR_FILE_NOT_FOUND
16306 || rc == VERR_PATH_NOT_FOUND)
16307 rc = VINF_SUCCESS;
16308 RTStrFree(pszFilePattern);
16309 return rc;
16310}
16311
16312/**
16313 * Helper to set up an I/O stream to read or write a possibly encrypted file.
16314 *
16315 * @returns VBox status code.
16316 * @param pszFilename The file to open.
16317 * @param pCryptoIf Pointer to the cryptographic interface if the file should be encrypted or contains encrypted data.
16318 * @param pszKeyStore The keystore if the file should be encrypted or contains encrypted data.
16319 * @param pszPassword The password if the file should be encrypted or contains encrypted data.
16320 * @param fOpen The open flags for the file.
16321 * @param phVfsIos Where to store the handle to the I/O stream on success.
16322 */
16323int Machine::i_createIoStreamForFile(const char *pszFilename, PCVBOXCRYPTOIF pCryptoIf,
16324 const char *pszKeyStore, const char *pszPassword,
16325 uint64_t fOpen, PRTVFSIOSTREAM phVfsIos)
16326{
16327 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
16328 int vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
16329 if (RT_SUCCESS(vrc))
16330 {
16331 if (pCryptoIf)
16332 {
16333 RTVFSFILE hVfsFileCrypto = NIL_RTVFSFILE;
16334 vrc = pCryptoIf->pfnCryptoFileFromVfsFile(hVfsFile, pszKeyStore, pszPassword, &hVfsFileCrypto);
16335 if (RT_SUCCESS(vrc))
16336 {
16337 RTVfsFileRelease(hVfsFile);
16338 hVfsFile = hVfsFileCrypto;
16339 }
16340 }
16341
16342 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
16343 RTVfsFileRelease(hVfsFile);
16344 }
16345
16346 return vrc;
16347}
16348
16349/**
16350 * Helper function processing all actions for one component (saved state files,
16351 * NVRAM files, etc). Used by Machine::i_changeEncryptionHandler only.
16352 *
16353 * @param task
16354 * @param strDirectory
16355 * @param strFilePattern
16356 * @param strMagic
16357 * @param strKeyStore
16358 * @param strKeyId
16359 * @return
16360 */
16361HRESULT Machine::i_changeEncryptionForComponent(ChangeEncryptionTask &task, const com::Utf8Str strDirectory,
16362 const com::Utf8Str strFilePattern, com::Utf8Str &strKeyStore,
16363 com::Utf8Str &strKeyId, int iCipherMode)
16364{
16365 bool fDecrypt = task.mstrCurrentPassword.isNotEmpty()
16366 && task.mstrCipher.isEmpty()
16367 && task.mstrNewPassword.isEmpty()
16368 && task.mstrNewPasswordId.isEmpty();
16369 bool fEncrypt = task.mstrCurrentPassword.isEmpty()
16370 && task.mstrCipher.isNotEmpty()
16371 && task.mstrNewPassword.isNotEmpty()
16372 && task.mstrNewPasswordId.isNotEmpty();
16373
16374 /* check if the cipher is changed which causes the reencryption*/
16375
16376 const char *pszTaskCipher = NULL;
16377 if (task.mstrCipher.isNotEmpty())
16378 pszTaskCipher = getCipherString(task.mstrCipher.c_str(), iCipherMode);
16379
16380 if (!task.mForce && !fDecrypt && !fEncrypt)
16381 {
16382 char *pszCipher = NULL;
16383 int vrc = task.m_pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(strKeyStore.c_str(),
16384 NULL /*pszPassword*/,
16385 NULL /*ppbKey*/,
16386 NULL /*pcbKey*/,
16387 &pszCipher);
16388 if (RT_SUCCESS(vrc))
16389 {
16390 task.mForce = strcmp(pszTaskCipher, pszCipher) != 0;
16391 RTMemFree(pszCipher);
16392 }
16393 else
16394 return setErrorBoth(E_FAIL, vrc, tr("Obtain cipher for '%s' files failed (%Rrc)"),
16395 strFilePattern.c_str(), vrc);
16396 }
16397
16398 /* Only the password needs to be changed */
16399 if (!task.mForce && !fDecrypt && !fEncrypt)
16400 {
16401 Assert(task.m_pCryptoIf);
16402
16403 VBOXCRYPTOCTX hCryptoCtx;
16404 int vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(strKeyStore.c_str(), task.mstrCurrentPassword.c_str(), &hCryptoCtx);
16405 if (RT_FAILURE(vrc))
16406 return setErrorBoth(E_FAIL, vrc, tr("Loading old key store for '%s' files failed, (%Rrc)"),
16407 strFilePattern.c_str(), vrc);
16408 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
16409 if (RT_FAILURE(vrc))
16410 return setErrorBoth(E_FAIL, vrc, tr("Changing the password for '%s' files failed, (%Rrc)"),
16411 strFilePattern.c_str(), vrc);
16412
16413 char *pszKeyStore = NULL;
16414 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16415 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16416 if (RT_FAILURE(vrc))
16417 return setErrorBoth(E_FAIL, vrc, tr("Saving the key store for '%s' files failed, (%Rrc)"),
16418 strFilePattern.c_str(), vrc);
16419 strKeyStore = pszKeyStore;
16420 RTMemFree(pszKeyStore);
16421 strKeyId = task.mstrNewPasswordId;
16422 return S_OK;
16423 }
16424
16425 /* Reencryption required */
16426 HRESULT rc = S_OK;
16427 int vrc = VINF_SUCCESS;
16428
16429 std::list<com::Utf8Str> lstFiles;
16430 if (SUCCEEDED(rc))
16431 {
16432 vrc = i_findFiles(lstFiles, strDirectory, strFilePattern);
16433 if (RT_FAILURE(vrc))
16434 rc = setErrorBoth(E_FAIL, vrc, tr("Getting file list for '%s' files failed, (%Rrc)"),
16435 strFilePattern.c_str(), vrc);
16436 }
16437 com::Utf8Str strNewKeyStore;
16438 if (SUCCEEDED(rc))
16439 {
16440 if (!fDecrypt)
16441 {
16442 VBOXCRYPTOCTX hCryptoCtx;
16443 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(pszTaskCipher, task.mstrNewPassword.c_str(), &hCryptoCtx);
16444 if (RT_FAILURE(vrc))
16445 return setErrorBoth(E_FAIL, vrc, tr("Create new key store for '%s' files failed, (%Rrc)"),
16446 strFilePattern.c_str(), vrc);
16447
16448 char *pszKeyStore = NULL;
16449 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16450 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16451 if (RT_FAILURE(vrc))
16452 return setErrorBoth(E_FAIL, vrc, tr("Saving the new key store for '%s' files failed, (%Rrc)"),
16453 strFilePattern.c_str(), vrc);
16454 strNewKeyStore = pszKeyStore;
16455 RTMemFree(pszKeyStore);
16456 }
16457
16458 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
16459 it != lstFiles.end();
16460 ++it)
16461 {
16462 RTVFSIOSTREAM hVfsIosOld = NIL_RTVFSIOSTREAM;
16463 RTVFSIOSTREAM hVfsIosNew = NIL_RTVFSIOSTREAM;
16464
16465 uint64_t fOpenForRead = RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE;
16466 uint64_t fOpenForWrite = RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE;
16467
16468 vrc = i_createIoStreamForFile((*it).c_str(),
16469 fEncrypt ? NULL : task.m_pCryptoIf,
16470 fEncrypt ? NULL : strKeyStore.c_str(),
16471 fEncrypt ? NULL : task.mstrCurrentPassword.c_str(),
16472 fOpenForRead, &hVfsIosOld);
16473 if (RT_SUCCESS(vrc))
16474 {
16475 vrc = i_createIoStreamForFile((*it + ".tmp").c_str(),
16476 fDecrypt ? NULL : task.m_pCryptoIf,
16477 fDecrypt ? NULL : strNewKeyStore.c_str(),
16478 fDecrypt ? NULL : task.mstrNewPassword.c_str(),
16479 fOpenForWrite, &hVfsIosNew);
16480 if (RT_FAILURE(vrc))
16481 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
16482 (*it + ".tmp").c_str(), vrc);
16483 }
16484 else
16485 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
16486 (*it).c_str(), vrc);
16487
16488 if (RT_SUCCESS(vrc))
16489 {
16490 vrc = RTVfsUtilPumpIoStreams(hVfsIosOld, hVfsIosNew, BUF_DATA_SIZE);
16491 if (RT_FAILURE(vrc))
16492 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Changing encryption of the file '%s' failed with %Rrc"),
16493 (*it).c_str(), vrc);
16494 }
16495
16496 if (hVfsIosOld != NIL_RTVFSIOSTREAM)
16497 RTVfsIoStrmRelease(hVfsIosOld);
16498 if (hVfsIosNew != NIL_RTVFSIOSTREAM)
16499 RTVfsIoStrmRelease(hVfsIosNew);
16500 }
16501 }
16502
16503 if (SUCCEEDED(rc))
16504 {
16505 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
16506 it != lstFiles.end();
16507 ++it)
16508 {
16509 vrc = RTFileRename((*it + ".tmp").c_str(), (*it).c_str(), RTPATHRENAME_FLAGS_REPLACE);
16510 if (RT_FAILURE(vrc))
16511 {
16512 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming the file '%s' failed, (%Rrc)"),
16513 (*it + ".tmp").c_str(), vrc);
16514 break;
16515 }
16516 }
16517 }
16518
16519 if (SUCCEEDED(rc))
16520 {
16521 strKeyStore = strNewKeyStore;
16522 strKeyId = task.mstrNewPasswordId;
16523 }
16524
16525 return rc;
16526}
16527
16528/**
16529 * Task thread implementation for Machine::changeEncryption(), called from
16530 * Machine::taskHandler().
16531 *
16532 * @note Locks this object for writing.
16533 *
16534 * @param task
16535 * @return
16536 */
16537void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task)
16538{
16539 LogFlowThisFuncEnter();
16540
16541 AutoCaller autoCaller(this);
16542 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
16543 if (FAILED(autoCaller.hrc()))
16544 {
16545 /* we might have been uninitialized because the session was accidentally
16546 * closed by the client, so don't assert */
16547 HRESULT rc = setError(E_FAIL,
16548 tr("The session has been accidentally closed"));
16549 task.m_pProgress->i_notifyComplete(rc);
16550 LogFlowThisFuncLeave();
16551 return;
16552 }
16553
16554 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16555
16556 HRESULT rc = S_OK;
16557 com::Utf8Str strOldKeyId = mData->mstrKeyId;
16558 com::Utf8Str strOldKeyStore = mData->mstrKeyStore;
16559 try
16560 {
16561 rc = this->i_getVirtualBox()->i_retainCryptoIf(&task.m_pCryptoIf);
16562 if (FAILED(rc))
16563 throw rc;
16564
16565 if (task.mstrCurrentPassword.isEmpty())
16566 {
16567 if (mData->mstrKeyStore.isNotEmpty())
16568 throw setError(VBOX_E_PASSWORD_INCORRECT,
16569 tr("The password given for the encrypted VM is incorrect"));
16570 }
16571 else
16572 {
16573 if (mData->mstrKeyStore.isEmpty())
16574 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16575 tr("The VM is not configured for encryption"));
16576 rc = checkEncryptionPassword(task.mstrCurrentPassword);
16577 if (rc == VBOX_E_PASSWORD_INCORRECT)
16578 throw setError(VBOX_E_PASSWORD_INCORRECT,
16579 tr("The password to decrypt the VM is incorrect"));
16580 }
16581
16582 if (task.mstrCipher.isNotEmpty())
16583 {
16584 if ( task.mstrNewPassword.isEmpty()
16585 && task.mstrNewPasswordId.isEmpty()
16586 && task.mstrCurrentPassword.isNotEmpty())
16587 {
16588 /* An empty password and password ID will default to the current password. */
16589 task.mstrNewPassword = task.mstrCurrentPassword;
16590 }
16591 else if (task.mstrNewPassword.isEmpty())
16592 throw setError(VBOX_E_OBJECT_NOT_FOUND,
16593 tr("A password must be given for the VM encryption"));
16594 else if (task.mstrNewPasswordId.isEmpty())
16595 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16596 tr("A valid identifier for the password must be given"));
16597 }
16598 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
16599 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16600 tr("The password and password identifier must be empty if the output should be unencrypted"));
16601
16602 /*
16603 * Save config.
16604 * Must be first operation to prevent making encrypted copies
16605 * for old version of the config file.
16606 */
16607 int fSave = Machine::SaveS_Force;
16608 if (task.mstrNewPassword.isNotEmpty())
16609 {
16610 VBOXCRYPTOCTX hCryptoCtx;
16611
16612 int vrc = VINF_SUCCESS;
16613 if (task.mForce || task.mstrCurrentPassword.isEmpty() || task.mstrCipher.isNotEmpty())
16614 {
16615 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(getCipherString(task.mstrCipher.c_str(), CipherModeGcm),
16616 task.mstrNewPassword.c_str(), &hCryptoCtx);
16617 if (RT_FAILURE(vrc))
16618 throw setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
16619 }
16620 else
16621 {
16622 vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(mData->mstrKeyStore.c_str(),
16623 task.mstrCurrentPassword.c_str(),
16624 &hCryptoCtx);
16625 if (RT_FAILURE(vrc))
16626 throw setErrorBoth(E_FAIL, vrc, tr("Loading old key store failed, (%Rrc)"), vrc);
16627 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
16628 if (RT_FAILURE(vrc))
16629 throw setErrorBoth(E_FAIL, vrc, tr("Changing the password failed, (%Rrc)"), vrc);
16630 }
16631
16632 char *pszKeyStore;
16633 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16634 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16635 if (RT_FAILURE(vrc))
16636 throw setErrorBoth(E_FAIL, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
16637 mData->mstrKeyStore = pszKeyStore;
16638 RTStrFree(pszKeyStore);
16639 mData->mstrKeyId = task.mstrNewPasswordId;
16640 size_t cbPassword = task.mstrNewPassword.length() + 1;
16641 uint8_t *pbPassword = (uint8_t *)task.mstrNewPassword.c_str();
16642 mData->mpKeyStore->deleteSecretKey(task.mstrNewPasswordId);
16643 mData->mpKeyStore->addSecretKey(task.mstrNewPasswordId, pbPassword, cbPassword);
16644 mNvramStore->i_addPassword(task.mstrNewPasswordId, task.mstrNewPassword);
16645
16646 /*
16647 * Remove backuped config after saving because it can contain
16648 * unencrypted version of the config
16649 */
16650 fSave |= Machine::SaveS_RemoveBackup;
16651 }
16652 else
16653 {
16654 mData->mstrKeyId.setNull();
16655 mData->mstrKeyStore.setNull();
16656 }
16657
16658 Bstr bstrCurrentPassword(task.mstrCurrentPassword);
16659 Bstr bstrCipher(getCipherString(task.mstrCipher.c_str(), CipherModeXts));
16660 Bstr bstrNewPassword(task.mstrNewPassword);
16661 Bstr bstrNewPasswordId(task.mstrNewPasswordId);
16662 /* encrypt mediums */
16663 alock.release();
16664 for (MediaList::iterator it = task.mllMedia.begin();
16665 it != task.mllMedia.end();
16666 ++it)
16667 {
16668 ComPtr<IProgress> pProgress1;
16669 HRESULT hrc = (*it)->ChangeEncryption(bstrCurrentPassword.raw(), bstrCipher.raw(),
16670 bstrNewPassword.raw(), bstrNewPasswordId.raw(),
16671 pProgress1.asOutParam());
16672 if (FAILED(hrc)) throw hrc;
16673 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress1, 0 /* indefinite wait */);
16674 if (FAILED(hrc)) throw hrc;
16675 }
16676 alock.acquire();
16677
16678 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the SAV files")).raw(), 1);
16679
16680 Utf8Str strFullSnapshotFolder;
16681 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
16682
16683 /* .sav files (main and snapshots) */
16684 rc = i_changeEncryptionForComponent(task, strFullSnapshotFolder, "*.sav",
16685 mSSData->strStateKeyStore, mSSData->strStateKeyId, CipherModeGcm);
16686 if (FAILED(rc))
16687 /* the helper function already sets error object */
16688 throw rc;
16689
16690 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the NVRAM files")).raw(), 1);
16691
16692 /* .nvram files */
16693 com::Utf8Str strNVRAMKeyId;
16694 com::Utf8Str strNVRAMKeyStore;
16695 rc = mNvramStore->i_getEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16696 if (FAILED(rc))
16697 throw setError(rc, tr("Getting NVRAM encryption settings failed (%Rhrc)"), rc);
16698
16699 Utf8Str strMachineFolder;
16700 i_calculateFullPath(".", strMachineFolder);
16701
16702 rc = i_changeEncryptionForComponent(task, strMachineFolder, "*.nvram",
16703 strNVRAMKeyStore, strNVRAMKeyId, CipherModeGcm);
16704 if (FAILED(rc))
16705 /* the helper function already sets error object */
16706 throw rc;
16707
16708 rc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16709 if (FAILED(rc))
16710 throw setError(rc, tr("Setting NVRAM encryption settings failed (%Rhrc)"), rc);
16711
16712 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of log files")).raw(), 1);
16713
16714 /* .log files */
16715 com::Utf8Str strLogFolder;
16716 i_getLogFolder(strLogFolder);
16717 rc = i_changeEncryptionForComponent(task, strLogFolder, "VBox.log*",
16718 mData->mstrLogKeyStore, mData->mstrLogKeyId, CipherModeCtr);
16719 if (FAILED(rc))
16720 /* the helper function already sets error object */
16721 throw rc;
16722
16723 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the config file")).raw(), 1);
16724
16725 i_saveSettings(NULL, alock, fSave);
16726 }
16727 catch (HRESULT aRC)
16728 {
16729 rc = aRC;
16730 mData->mstrKeyId = strOldKeyId;
16731 mData->mstrKeyStore = strOldKeyStore;
16732 }
16733
16734 task.m_pProgress->i_notifyComplete(rc);
16735
16736 LogFlowThisFuncLeave();
16737}
16738#endif /*!VBOX_WITH_FULL_VM_ENCRYPTION*/
16739
16740HRESULT Machine::changeEncryption(const com::Utf8Str &aCurrentPassword,
16741 const com::Utf8Str &aCipher,
16742 const com::Utf8Str &aNewPassword,
16743 const com::Utf8Str &aNewPasswordId,
16744 BOOL aForce,
16745 ComPtr<IProgress> &aProgress)
16746{
16747 LogFlowFuncEnter();
16748
16749#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16750 RT_NOREF(aCurrentPassword, aCipher, aNewPassword, aNewPasswordId, aForce, aProgress);
16751 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16752#else
16753 /* make the VM accessible */
16754 if (!mData->mAccessible)
16755 {
16756 if ( aCurrentPassword.isEmpty()
16757 || mData->mstrKeyId.isEmpty())
16758 return setError(E_ACCESSDENIED, tr("Machine is inaccessible"));
16759
16760 HRESULT rc = addEncryptionPassword(mData->mstrKeyId, aCurrentPassword);
16761 if (FAILED(rc))
16762 return rc;
16763 }
16764
16765 AutoLimitedCaller autoCaller(this);
16766 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16767
16768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16769
16770 /* define mediums to be change encryption */
16771
16772 MediaList llMedia;
16773 for (MediumAttachmentList::iterator
16774 it = mMediumAttachments->begin();
16775 it != mMediumAttachments->end();
16776 ++it)
16777 {
16778 ComObjPtr<MediumAttachment> &pAttach = *it;
16779 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
16780
16781 if (!pMedium.isNull())
16782 {
16783 AutoCaller mac(pMedium);
16784 if (FAILED(mac.hrc())) return mac.hrc();
16785 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
16786 DeviceType_T devType = pMedium->i_getDeviceType();
16787 if (devType == DeviceType_HardDisk)
16788 {
16789 /*
16790 * We need to move to last child because the Medium::changeEncryption
16791 * encrypts all chain of specified medium with its parents.
16792 * Also we perform cheking of back reference and children for
16793 * all media in the chain to raise error before we start any action.
16794 * So, we first move into root parent and then we will move to last child
16795 * keeping latter in the list for encryption.
16796 */
16797
16798 /* move to root parent */
16799 ComObjPtr<Medium> pTmpMedium = pMedium;
16800 while (pTmpMedium.isNotNull())
16801 {
16802 AutoCaller mediumAC(pTmpMedium);
16803 if (FAILED(mediumAC.hrc())) return mac.hrc();
16804 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16805
16806 /* Cannot encrypt media which are attached to more than one virtual machine. */
16807 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16808 if (cBackRefs > 1)
16809 return setError(VBOX_E_INVALID_OBJECT_STATE,
16810 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16811 pTmpMedium->i_getName().c_str(), cBackRefs);
16812
16813 size_t cChildren = pTmpMedium->i_getChildren().size();
16814 if (cChildren > 1)
16815 return setError(VBOX_E_INVALID_OBJECT_STATE,
16816 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16817 pTmpMedium->i_getName().c_str(), cChildren);
16818
16819 pTmpMedium = pTmpMedium->i_getParent();
16820 }
16821 /* move to last child */
16822 pTmpMedium = pMedium;
16823 while (pTmpMedium.isNotNull() && pTmpMedium->i_getChildren().size() != 0)
16824 {
16825 AutoCaller mediumAC(pTmpMedium);
16826 if (FAILED(mediumAC.hrc())) return mac.hrc();
16827 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16828
16829 /* Cannot encrypt media which are attached to more than one virtual machine. */
16830 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16831 if (cBackRefs > 1)
16832 return setError(VBOX_E_INVALID_OBJECT_STATE,
16833 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16834 pTmpMedium->i_getName().c_str(), cBackRefs);
16835
16836 size_t cChildren = pTmpMedium->i_getChildren().size();
16837 if (cChildren > 1)
16838 return setError(VBOX_E_INVALID_OBJECT_STATE,
16839 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16840 pTmpMedium->i_getName().c_str(), cChildren);
16841
16842 pTmpMedium = pTmpMedium->i_getChildren().front();
16843 }
16844 llMedia.push_back(pTmpMedium);
16845 }
16846 }
16847 }
16848
16849 ComObjPtr<Progress> pProgress;
16850 pProgress.createObject();
16851 HRESULT rc = pProgress->init(i_getVirtualBox(),
16852 static_cast<IMachine*>(this) /* aInitiator */,
16853 tr("Change encryption"),
16854 TRUE /* fCancellable */,
16855 (ULONG)(4 + + llMedia.size()), // cOperations
16856 tr("Change encryption of the mediuma"));
16857 if (FAILED(rc))
16858 return rc;
16859
16860 /* create and start the task on a separate thread (note that it will not
16861 * start working until we release alock) */
16862 ChangeEncryptionTask *pTask = new ChangeEncryptionTask(this, pProgress, "VM encryption",
16863 aCurrentPassword, aCipher, aNewPassword,
16864 aNewPasswordId, aForce, llMedia);
16865 rc = pTask->createThread();
16866 pTask = NULL;
16867 if (FAILED(rc))
16868 return rc;
16869
16870 pProgress.queryInterfaceTo(aProgress.asOutParam());
16871
16872 LogFlowFuncLeave();
16873
16874 return S_OK;
16875#endif
16876}
16877
16878HRESULT Machine::getEncryptionSettings(com::Utf8Str &aCipher,
16879 com::Utf8Str &aPasswordId)
16880{
16881#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16882 RT_NOREF(aCipher, aPasswordId);
16883 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16884#else
16885 AutoLimitedCaller autoCaller(this);
16886 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16887
16888 PCVBOXCRYPTOIF pCryptoIf = NULL;
16889 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16890 if (FAILED(hrc)) return hrc; /* Error is set */
16891
16892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16893
16894 if (mData->mstrKeyStore.isNotEmpty())
16895 {
16896 char *pszCipher = NULL;
16897 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), NULL /*pszPassword*/,
16898 NULL /*ppbKey*/, NULL /*pcbKey*/, &pszCipher);
16899 if (RT_SUCCESS(vrc))
16900 {
16901 aCipher = getCipherStringWithoutMode(pszCipher);
16902 RTStrFree(pszCipher);
16903 aPasswordId = mData->mstrKeyId;
16904 }
16905 else
16906 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
16907 tr("Failed to query the encryption settings with %Rrc"),
16908 vrc);
16909 }
16910 else
16911 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16912
16913 mParent->i_releaseCryptoIf(pCryptoIf);
16914
16915 return hrc;
16916#endif
16917}
16918
16919HRESULT Machine::checkEncryptionPassword(const com::Utf8Str &aPassword)
16920{
16921#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16922 RT_NOREF(aPassword);
16923 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16924#else
16925 AutoLimitedCaller autoCaller(this);
16926 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16927
16928 PCVBOXCRYPTOIF pCryptoIf = NULL;
16929 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16930 if (FAILED(hrc)) return hrc; /* Error is set */
16931
16932 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16933
16934 if (mData->mstrKeyStore.isNotEmpty())
16935 {
16936 char *pszCipher = NULL;
16937 uint8_t *pbDek = NULL;
16938 size_t cbDek = 0;
16939 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), aPassword.c_str(),
16940 &pbDek, &cbDek, &pszCipher);
16941 if (RT_SUCCESS(vrc))
16942 {
16943 RTStrFree(pszCipher);
16944 RTMemSaferFree(pbDek, cbDek);
16945 }
16946 else
16947 hrc = setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc,
16948 tr("The password supplied for the encrypted machine is incorrect"));
16949 }
16950 else
16951 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16952
16953 mParent->i_releaseCryptoIf(pCryptoIf);
16954
16955 return hrc;
16956#endif
16957}
16958
16959HRESULT Machine::addEncryptionPassword(const com::Utf8Str &aId,
16960 const com::Utf8Str &aPassword)
16961{
16962#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16963 RT_NOREF(aId, aPassword);
16964 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16965#else
16966 AutoLimitedCaller autoCaller(this);
16967 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16968
16969 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16970
16971 size_t cbPassword = aPassword.length() + 1;
16972 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
16973
16974 mData->mpKeyStore->addSecretKey(aId, pbPassword, cbPassword);
16975
16976 if ( mData->mAccessible
16977 && mData->mSession.mState == SessionState_Locked
16978 && mData->mSession.mLockType == LockType_VM
16979 && mData->mSession.mDirectControl != NULL)
16980 {
16981 /* get the console from the direct session */
16982 ComPtr<IConsole> console;
16983 HRESULT rc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16984 ComAssertComRC(rc);
16985 /* send passsword to console */
16986 console->AddEncryptionPassword(Bstr(aId).raw(),
16987 Bstr(aPassword).raw(),
16988 TRUE);
16989 }
16990
16991 if (mData->mstrKeyId == aId)
16992 {
16993 HRESULT hrc = checkEncryptionPassword(aPassword);
16994 if (FAILED(hrc))
16995 return hrc;
16996
16997 if (SUCCEEDED(hrc))
16998 {
16999 /*
17000 * Encryption is used and password is correct,
17001 * Reinit the machine if required.
17002 */
17003 BOOL fAccessible;
17004 alock.release();
17005 getAccessible(&fAccessible);
17006 alock.acquire();
17007 }
17008 }
17009
17010 /*
17011 * Add the password into the NvramStore only after
17012 * the machine becomes accessible and the NvramStore
17013 * contains key id and key store.
17014 */
17015 if (mNvramStore.isNotNull())
17016 mNvramStore->i_addPassword(aId, aPassword);
17017
17018 return S_OK;
17019#endif
17020}
17021
17022HRESULT Machine::addEncryptionPasswords(const std::vector<com::Utf8Str> &aIds,
17023 const std::vector<com::Utf8Str> &aPasswords)
17024{
17025#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
17026 RT_NOREF(aIds, aPasswords);
17027 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
17028#else
17029 if (aIds.size() != aPasswords.size())
17030 return setError(E_INVALIDARG, tr("Id and passwords arrays must have the same size"));
17031
17032 HRESULT hrc = S_OK;
17033 for (size_t i = 0; i < aIds.size() && SUCCEEDED(hrc); ++i)
17034 hrc = addEncryptionPassword(aIds[i], aPasswords[i]);
17035
17036 return hrc;
17037#endif
17038}
17039
17040HRESULT Machine::removeEncryptionPassword(AutoCaller &autoCaller, const com::Utf8Str &aId)
17041{
17042#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
17043 RT_NOREF(autoCaller, aId);
17044 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
17045#else
17046 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
17047
17048 if ( mData->mAccessible
17049 && mData->mSession.mState == SessionState_Locked
17050 && mData->mSession.mLockType == LockType_VM
17051 && mData->mSession.mDirectControl != NULL)
17052 {
17053 /* get the console from the direct session */
17054 ComPtr<IConsole> console;
17055 HRESULT rc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
17056 ComAssertComRC(rc);
17057 /* send passsword to console */
17058 console->RemoveEncryptionPassword(Bstr(aId).raw());
17059 }
17060
17061 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty() && mData->mstrKeyId == aId)
17062 {
17063 if (Global::IsOnlineOrTransient(mData->mMachineState))
17064 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
17065 alock.release();
17066 autoCaller.release();
17067 /* return because all passwords are purged when machine becomes inaccessible; */
17068 return i_setInaccessible();
17069 }
17070
17071 if (mNvramStore.isNotNull())
17072 mNvramStore->i_removePassword(aId);
17073 mData->mpKeyStore->deleteSecretKey(aId);
17074 return S_OK;
17075#endif
17076}
17077
17078HRESULT Machine::clearAllEncryptionPasswords(AutoCaller &autoCaller)
17079{
17080#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
17081 RT_NOREF(autoCaller);
17082 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
17083#else
17084 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
17085
17086 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty())
17087 {
17088 if (Global::IsOnlineOrTransient(mData->mMachineState))
17089 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
17090 alock.release();
17091 autoCaller.release();
17092 /* return because all passwords are purged when machine becomes inaccessible; */
17093 return i_setInaccessible();
17094 }
17095
17096 mNvramStore->i_removeAllPasswords();
17097 mData->mpKeyStore->deleteAllSecretKeys(false, true);
17098 return S_OK;
17099#endif
17100}
17101
17102#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
17103HRESULT Machine::i_setInaccessible()
17104{
17105 if (!mData->mAccessible)
17106 return S_OK;
17107
17108 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
17109 VirtualBox *pParent = mParent;
17110 com::Utf8Str strConfigFile = mData->m_strConfigFile;
17111 Guid id(i_getId());
17112
17113 alock.release();
17114
17115 uninit();
17116 HRESULT rc = initFromSettings(pParent, strConfigFile, &id, com::Utf8Str());
17117
17118 alock.acquire();
17119 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
17120 return rc;
17121}
17122#endif
17123
17124/* This isn't handled entirely by the wrapper generator yet. */
17125#ifdef VBOX_WITH_XPCOM
17126NS_DECL_CLASSINFO(SessionMachine)
17127NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
17128
17129NS_DECL_CLASSINFO(SnapshotMachine)
17130NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
17131#endif
Note: See TracBrowser for help on using the repository browser.

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