VirtualBox

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

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

Main/IMachine::attachDevice: Attempt at fixing r150710 regression. Looks like fHotplug was incorrectly set just to make USB media attachments always hotpluggable, with the result that attaching an MSD while the VM was powered off caused media to be in a locked state when it shouldn't and prevent the machine from powering up. bugref:8211

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 601.7 KB
Line 
1/* $Id: MachineImpl.cpp 98101 2023-01-17 10:59:59Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2022 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.rc()))
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.rc())) return mediumCaller.rc();
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 mData->mSession.mLockedMedia.Unlock();
4016 alock.release();
4017 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4018 mData->mSession.mLockedMedia.Lock();
4019 alock.acquire();
4020 }
4021 alock.release();
4022
4023 if (SUCCEEDED(rc))
4024 {
4025 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4026 /* Remove lock list in case of error. */
4027 if (FAILED(rc))
4028 {
4029 mData->mSession.mLockedMedia.Unlock();
4030 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4031 mData->mSession.mLockedMedia.Lock();
4032 }
4033 }
4034 }
4035
4036 return S_OK;
4037 }
4038
4039 /* bus/channel/device differ; we need a new attachment object,
4040 * but don't try to associate it again */
4041 associate = false;
4042 break;
4043 }
4044 }
4045
4046 /* go further only if the attachment is to be indirect */
4047 if (!fIndirect)
4048 break;
4049
4050 /* perform the so called smart attachment logic for indirect
4051 * attachments. Note that smart attachment is only applicable to base
4052 * hard disks. */
4053
4054 if (medium->i_getParent().isNull())
4055 {
4056 /* first, investigate the backup copy of the current hard disk
4057 * attachments to make it possible to re-attach existing diffs to
4058 * another device slot w/o losing their contents */
4059 if (mMediumAttachments.isBackedUp())
4060 {
4061 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4062
4063 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4064 uint32_t foundLevel = 0;
4065
4066 for (MediumAttachmentList::const_iterator
4067 it = oldAtts.begin();
4068 it != oldAtts.end();
4069 ++it)
4070 {
4071 uint32_t level = 0;
4072 MediumAttachment *pAttach = *it;
4073 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4074 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4075 if (pMedium.isNull())
4076 continue;
4077
4078 if (pMedium->i_getBase(&level) == medium)
4079 {
4080 /* skip the hard disk if its currently attached (we
4081 * cannot attach the same hard disk twice) */
4082 if (i_findAttachment(*mMediumAttachments.data(),
4083 pMedium))
4084 continue;
4085
4086 /* matched device, channel and bus (i.e. attached to the
4087 * same place) will win and immediately stop the search;
4088 * otherwise the attachment that has the youngest
4089 * descendant of medium will be used
4090 */
4091 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4092 {
4093 /* the simplest case: restore the whole attachment
4094 * and return, nothing else to do */
4095 mMediumAttachments->push_back(*it);
4096
4097 /* Reattach the medium to the VM. */
4098 if (fHotplug || fSilent)
4099 {
4100 mediumLock.release();
4101 treeLock.release();
4102 alock.release();
4103
4104 MediumLockList *pMediumLockList(new MediumLockList());
4105
4106 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4107 medium /* pToLockWrite */,
4108 false /* fMediumLockWriteAll */,
4109 NULL,
4110 *pMediumLockList);
4111 alock.acquire();
4112 if (FAILED(rc))
4113 delete pMediumLockList;
4114 else
4115 {
4116 mData->mSession.mLockedMedia.Unlock();
4117 alock.release();
4118 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4119 mData->mSession.mLockedMedia.Lock();
4120 alock.acquire();
4121 }
4122 alock.release();
4123
4124 if (SUCCEEDED(rc))
4125 {
4126 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4127 /* Remove lock list in case of error. */
4128 if (FAILED(rc))
4129 {
4130 mData->mSession.mLockedMedia.Unlock();
4131 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4132 mData->mSession.mLockedMedia.Lock();
4133 }
4134 }
4135 }
4136
4137 return S_OK;
4138 }
4139 else if ( foundIt == oldAtts.end()
4140 || level > foundLevel /* prefer younger */
4141 )
4142 {
4143 foundIt = it;
4144 foundLevel = level;
4145 }
4146 }
4147 }
4148
4149 if (foundIt != oldAtts.end())
4150 {
4151 /* use the previously attached hard disk */
4152 medium = (*foundIt)->i_getMedium();
4153 mediumCaller.attach(medium);
4154 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4155 mediumLock.attach(medium);
4156 /* not implicit, doesn't require association with this VM */
4157 fIndirect = false;
4158 associate = false;
4159 /* go right to the MediumAttachment creation */
4160 break;
4161 }
4162 }
4163
4164 /* must give up the medium lock and medium tree lock as below we
4165 * go over snapshots, which needs a lock with higher lock order. */
4166 mediumLock.release();
4167 treeLock.release();
4168
4169 /* then, search through snapshots for the best diff in the given
4170 * hard disk's chain to base the new diff on */
4171
4172 ComObjPtr<Medium> base;
4173 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4174 while (snap)
4175 {
4176 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4177
4178 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4179
4180 MediumAttachment *pAttachFound = NULL;
4181 uint32_t foundLevel = 0;
4182
4183 for (MediumAttachmentList::const_iterator
4184 it = snapAtts.begin();
4185 it != snapAtts.end();
4186 ++it)
4187 {
4188 MediumAttachment *pAttach = *it;
4189 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4190 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4191 if (pMedium.isNull())
4192 continue;
4193
4194 uint32_t level = 0;
4195 if (pMedium->i_getBase(&level) == medium)
4196 {
4197 /* matched device, channel and bus (i.e. attached to the
4198 * same place) will win and immediately stop the search;
4199 * otherwise the attachment that has the youngest
4200 * descendant of medium will be used
4201 */
4202 if ( pAttach->i_getDevice() == aDevice
4203 && pAttach->i_getPort() == aControllerPort
4204 && pAttach->i_getControllerName() == aName
4205 )
4206 {
4207 pAttachFound = pAttach;
4208 break;
4209 }
4210 else if ( !pAttachFound
4211 || level > foundLevel /* prefer younger */
4212 )
4213 {
4214 pAttachFound = pAttach;
4215 foundLevel = level;
4216 }
4217 }
4218 }
4219
4220 if (pAttachFound)
4221 {
4222 base = pAttachFound->i_getMedium();
4223 break;
4224 }
4225
4226 snap = snap->i_getParent();
4227 }
4228
4229 /* re-lock medium tree and the medium, as we need it below */
4230 treeLock.acquire();
4231 mediumLock.acquire();
4232
4233 /* found a suitable diff, use it as a base */
4234 if (!base.isNull())
4235 {
4236 medium = base;
4237 mediumCaller.attach(medium);
4238 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4239 mediumLock.attach(medium);
4240 }
4241 }
4242
4243 Utf8Str strFullSnapshotFolder;
4244 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4245
4246 ComObjPtr<Medium> diff;
4247 diff.createObject();
4248 // store this diff in the same registry as the parent
4249 Guid uuidRegistryParent;
4250 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4251 {
4252 // parent image has no registry: this can happen if we're attaching a new immutable
4253 // image that has not yet been attached (medium then points to the base and we're
4254 // creating the diff image for the immutable, and the parent is not yet registered);
4255 // put the parent in the machine registry then
4256 mediumLock.release();
4257 treeLock.release();
4258 alock.release();
4259 i_addMediumToRegistry(medium);
4260 alock.acquire();
4261 treeLock.acquire();
4262 mediumLock.acquire();
4263 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4264 }
4265 rc = diff->init(mParent,
4266 medium->i_getPreferredDiffFormat(),
4267 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4268 uuidRegistryParent,
4269 DeviceType_HardDisk);
4270 if (FAILED(rc)) return rc;
4271
4272 /* Apply the normal locking logic to the entire chain. */
4273 MediumLockList *pMediumLockList(new MediumLockList());
4274 mediumLock.release();
4275 treeLock.release();
4276 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4277 diff /* pToLockWrite */,
4278 false /* fMediumLockWriteAll */,
4279 medium,
4280 *pMediumLockList);
4281 treeLock.acquire();
4282 mediumLock.acquire();
4283 if (SUCCEEDED(rc))
4284 {
4285 mediumLock.release();
4286 treeLock.release();
4287 rc = pMediumLockList->Lock();
4288 treeLock.acquire();
4289 mediumLock.acquire();
4290 if (FAILED(rc))
4291 setError(rc,
4292 tr("Could not lock medium when creating diff '%s'"),
4293 diff->i_getLocationFull().c_str());
4294 else
4295 {
4296 /* will release the lock before the potentially lengthy
4297 * operation, so protect with the special state */
4298 MachineState_T oldState = mData->mMachineState;
4299 i_setMachineState(MachineState_SettingUp);
4300
4301 mediumLock.release();
4302 treeLock.release();
4303 alock.release();
4304
4305 rc = medium->i_createDiffStorage(diff,
4306 medium->i_getPreferredDiffVariant(),
4307 pMediumLockList,
4308 NULL /* aProgress */,
4309 true /* aWait */,
4310 false /* aNotify */);
4311
4312 alock.acquire();
4313 treeLock.acquire();
4314 mediumLock.acquire();
4315
4316 i_setMachineState(oldState);
4317 }
4318 }
4319
4320 /* Unlock the media and free the associated memory. */
4321 delete pMediumLockList;
4322
4323 if (FAILED(rc)) return rc;
4324
4325 /* use the created diff for the actual attachment */
4326 medium = diff;
4327 mediumCaller.attach(medium);
4328 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4329 mediumLock.attach(medium);
4330 }
4331 while (0);
4332
4333 ComObjPtr<MediumAttachment> attachment;
4334 attachment.createObject();
4335 rc = attachment->init(this,
4336 medium,
4337 aName,
4338 aControllerPort,
4339 aDevice,
4340 aType,
4341 fIndirect,
4342 false /* fPassthrough */,
4343 false /* fTempEject */,
4344 false /* fNonRotational */,
4345 false /* fDiscard */,
4346 fHotplug || ctrlType == StorageControllerType_USB /* fHotPluggable */,
4347 Utf8Str::Empty);
4348 if (FAILED(rc)) return rc;
4349
4350 if (associate && !medium.isNull())
4351 {
4352 // as the last step, associate the medium to the VM
4353 rc = medium->i_addBackReference(mData->mUuid);
4354 // here we can fail because of Deleting, or being in process of creating a Diff
4355 if (FAILED(rc)) return rc;
4356
4357 mediumLock.release();
4358 treeLock.release();
4359 alock.release();
4360 i_addMediumToRegistry(medium);
4361 alock.acquire();
4362 treeLock.acquire();
4363 mediumLock.acquire();
4364 }
4365
4366 /* success: finally remember the attachment */
4367 i_setModified(IsModified_Storage);
4368 mMediumAttachments.backup();
4369 mMediumAttachments->push_back(attachment);
4370
4371 mediumLock.release();
4372 treeLock.release();
4373 alock.release();
4374
4375 if (fHotplug || fSilent)
4376 {
4377 if (!medium.isNull())
4378 {
4379 MediumLockList *pMediumLockList(new MediumLockList());
4380
4381 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4382 medium /* pToLockWrite */,
4383 false /* fMediumLockWriteAll */,
4384 NULL,
4385 *pMediumLockList);
4386 alock.acquire();
4387 if (FAILED(rc))
4388 delete pMediumLockList;
4389 else
4390 {
4391 mData->mSession.mLockedMedia.Unlock();
4392 alock.release();
4393 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4394 mData->mSession.mLockedMedia.Lock();
4395 alock.acquire();
4396 }
4397 alock.release();
4398 }
4399
4400 if (SUCCEEDED(rc))
4401 {
4402 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4403 /* Remove lock list in case of error. */
4404 if (FAILED(rc))
4405 {
4406 mData->mSession.mLockedMedia.Unlock();
4407 mData->mSession.mLockedMedia.Remove(attachment);
4408 mData->mSession.mLockedMedia.Lock();
4409 }
4410 }
4411 }
4412
4413 /* Save modified registries, but skip this machine as it's the caller's
4414 * job to save its settings like all other settings changes. */
4415 mParent->i_unmarkRegistryModified(i_getId());
4416 mParent->i_saveModifiedRegistries();
4417
4418 if (SUCCEEDED(rc))
4419 {
4420 if (fIndirect && medium != aM)
4421 mParent->i_onMediumConfigChanged(medium);
4422 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4423 }
4424
4425 return rc;
4426}
4427
4428HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4429 LONG aDevice)
4430{
4431 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4432 aName.c_str(), aControllerPort, aDevice));
4433
4434 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4435
4436 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4437 if (FAILED(rc)) return rc;
4438
4439 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4440
4441 /* Check for an existing controller. */
4442 ComObjPtr<StorageController> ctl;
4443 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4444 if (FAILED(rc)) return rc;
4445
4446 StorageControllerType_T ctrlType;
4447 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4448 if (FAILED(rc))
4449 return setError(E_FAIL,
4450 tr("Could not get type of controller '%s'"),
4451 aName.c_str());
4452
4453 bool fSilent = false;
4454 Utf8Str strReconfig;
4455
4456 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4457 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4458 if ( mData->mMachineState == MachineState_Paused
4459 && strReconfig == "1")
4460 fSilent = true;
4461
4462 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4463 bool fHotplug = false;
4464 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4465 fHotplug = true;
4466
4467 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4468 return setError(VBOX_E_INVALID_VM_STATE,
4469 tr("Controller '%s' does not support hot-plugging"),
4470 aName.c_str());
4471
4472 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4473 aName,
4474 aControllerPort,
4475 aDevice);
4476 if (!pAttach)
4477 return setError(VBOX_E_OBJECT_NOT_FOUND,
4478 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4479 aDevice, aControllerPort, aName.c_str());
4480
4481 if (fHotplug && !pAttach->i_getHotPluggable())
4482 return setError(VBOX_E_NOT_SUPPORTED,
4483 tr("The device slot %d on port %d of controller '%s' does not support hot-plugging"),
4484 aDevice, aControllerPort, aName.c_str());
4485
4486 /*
4487 * The VM has to detach the device before we delete any implicit diffs.
4488 * If this fails we can roll back without loosing data.
4489 */
4490 if (fHotplug || fSilent)
4491 {
4492 alock.release();
4493 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4494 alock.acquire();
4495 }
4496 if (FAILED(rc)) return rc;
4497
4498 /* If we are here everything went well and we can delete the implicit now. */
4499 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4500
4501 alock.release();
4502
4503 /* Save modified registries, but skip this machine as it's the caller's
4504 * job to save its settings like all other settings changes. */
4505 mParent->i_unmarkRegistryModified(i_getId());
4506 mParent->i_saveModifiedRegistries();
4507
4508 if (SUCCEEDED(rc))
4509 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4510
4511 return rc;
4512}
4513
4514HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4515 LONG aDevice, BOOL aPassthrough)
4516{
4517 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4518 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4519
4520 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4521
4522 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4523 if (FAILED(rc)) return rc;
4524
4525 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4526
4527 /* Check for an existing controller. */
4528 ComObjPtr<StorageController> ctl;
4529 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4530 if (FAILED(rc)) return rc;
4531
4532 StorageControllerType_T ctrlType;
4533 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4534 if (FAILED(rc))
4535 return setError(E_FAIL,
4536 tr("Could not get type of controller '%s'"),
4537 aName.c_str());
4538
4539 bool fSilent = false;
4540 Utf8Str strReconfig;
4541
4542 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4543 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4544 if ( mData->mMachineState == MachineState_Paused
4545 && strReconfig == "1")
4546 fSilent = true;
4547
4548 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4549 bool fHotplug = false;
4550 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4551 fHotplug = true;
4552
4553 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4554 return setError(VBOX_E_INVALID_VM_STATE,
4555 tr("Controller '%s' does not support hot-plugging which is required to change the passthrough setting while the VM is running"),
4556 aName.c_str());
4557
4558 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4559 aName,
4560 aControllerPort,
4561 aDevice);
4562 if (!pAttach)
4563 return setError(VBOX_E_OBJECT_NOT_FOUND,
4564 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4565 aDevice, aControllerPort, aName.c_str());
4566
4567
4568 i_setModified(IsModified_Storage);
4569 mMediumAttachments.backup();
4570
4571 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4572
4573 if (pAttach->i_getType() != DeviceType_DVD)
4574 return setError(E_INVALIDARG,
4575 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4576 aDevice, aControllerPort, aName.c_str());
4577
4578 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4579
4580 pAttach->i_updatePassthrough(!!aPassthrough);
4581
4582 attLock.release();
4583 alock.release();
4584 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4585 if (SUCCEEDED(rc) && fValueChanged)
4586 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4587
4588 return rc;
4589}
4590
4591HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4592 LONG aDevice, BOOL aTemporaryEject)
4593{
4594
4595 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4596 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4597
4598 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4599
4600 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4601 if (FAILED(rc)) return rc;
4602
4603 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4604 aName,
4605 aControllerPort,
4606 aDevice);
4607 if (!pAttach)
4608 return setError(VBOX_E_OBJECT_NOT_FOUND,
4609 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4610 aDevice, aControllerPort, aName.c_str());
4611
4612
4613 i_setModified(IsModified_Storage);
4614 mMediumAttachments.backup();
4615
4616 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4617
4618 if (pAttach->i_getType() != DeviceType_DVD)
4619 return setError(E_INVALIDARG,
4620 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4621 aDevice, aControllerPort, aName.c_str());
4622 pAttach->i_updateTempEject(!!aTemporaryEject);
4623
4624 return S_OK;
4625}
4626
4627HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4628 LONG aDevice, BOOL aNonRotational)
4629{
4630
4631 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4632 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4633
4634 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4635
4636 HRESULT rc = i_checkStateDependency(MutableStateDep);
4637 if (FAILED(rc)) return rc;
4638
4639 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4640
4641 if (Global::IsOnlineOrTransient(mData->mMachineState))
4642 return setError(VBOX_E_INVALID_VM_STATE,
4643 tr("Invalid machine state: %s"),
4644 Global::stringifyMachineState(mData->mMachineState));
4645
4646 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4647 aName,
4648 aControllerPort,
4649 aDevice);
4650 if (!pAttach)
4651 return setError(VBOX_E_OBJECT_NOT_FOUND,
4652 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4653 aDevice, aControllerPort, aName.c_str());
4654
4655
4656 i_setModified(IsModified_Storage);
4657 mMediumAttachments.backup();
4658
4659 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4660
4661 if (pAttach->i_getType() != DeviceType_HardDisk)
4662 return setError(E_INVALIDARG,
4663 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"),
4664 aDevice, aControllerPort, aName.c_str());
4665 pAttach->i_updateNonRotational(!!aNonRotational);
4666
4667 return S_OK;
4668}
4669
4670HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4671 LONG aDevice, BOOL aDiscard)
4672{
4673
4674 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4675 aName.c_str(), aControllerPort, aDevice, aDiscard));
4676
4677 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4678
4679 HRESULT rc = i_checkStateDependency(MutableStateDep);
4680 if (FAILED(rc)) return rc;
4681
4682 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4683
4684 if (Global::IsOnlineOrTransient(mData->mMachineState))
4685 return setError(VBOX_E_INVALID_VM_STATE,
4686 tr("Invalid machine state: %s"),
4687 Global::stringifyMachineState(mData->mMachineState));
4688
4689 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4690 aName,
4691 aControllerPort,
4692 aDevice);
4693 if (!pAttach)
4694 return setError(VBOX_E_OBJECT_NOT_FOUND,
4695 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4696 aDevice, aControllerPort, aName.c_str());
4697
4698
4699 i_setModified(IsModified_Storage);
4700 mMediumAttachments.backup();
4701
4702 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4703
4704 if (pAttach->i_getType() != DeviceType_HardDisk)
4705 return setError(E_INVALIDARG,
4706 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"),
4707 aDevice, aControllerPort, aName.c_str());
4708 pAttach->i_updateDiscard(!!aDiscard);
4709
4710 return S_OK;
4711}
4712
4713HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4714 LONG aDevice, BOOL aHotPluggable)
4715{
4716 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4717 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4718
4719 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4720
4721 HRESULT rc = i_checkStateDependency(MutableStateDep);
4722 if (FAILED(rc)) return rc;
4723
4724 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4725
4726 if (Global::IsOnlineOrTransient(mData->mMachineState))
4727 return setError(VBOX_E_INVALID_VM_STATE,
4728 tr("Invalid machine state: %s"),
4729 Global::stringifyMachineState(mData->mMachineState));
4730
4731 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4732 aName,
4733 aControllerPort,
4734 aDevice);
4735 if (!pAttach)
4736 return setError(VBOX_E_OBJECT_NOT_FOUND,
4737 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4738 aDevice, aControllerPort, aName.c_str());
4739
4740 /* Check for an existing controller. */
4741 ComObjPtr<StorageController> ctl;
4742 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4743 if (FAILED(rc)) return rc;
4744
4745 StorageControllerType_T ctrlType;
4746 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4747 if (FAILED(rc))
4748 return setError(E_FAIL,
4749 tr("Could not get type of controller '%s'"),
4750 aName.c_str());
4751
4752 if (!i_isControllerHotplugCapable(ctrlType))
4753 return setError(VBOX_E_NOT_SUPPORTED,
4754 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4755 aName.c_str());
4756
4757 /* silently ignore attempts to modify the hot-plug status of USB devices */
4758 if (ctrlType == StorageControllerType_USB)
4759 return S_OK;
4760
4761 i_setModified(IsModified_Storage);
4762 mMediumAttachments.backup();
4763
4764 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4765
4766 if (pAttach->i_getType() == DeviceType_Floppy)
4767 return setError(E_INVALIDARG,
4768 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"),
4769 aDevice, aControllerPort, aName.c_str());
4770 pAttach->i_updateHotPluggable(!!aHotPluggable);
4771
4772 return S_OK;
4773}
4774
4775HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4776 LONG aDevice)
4777{
4778 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4779 aName.c_str(), aControllerPort, aDevice));
4780
4781 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4782}
4783
4784HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4785 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4786{
4787 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4788 aName.c_str(), aControllerPort, aDevice));
4789
4790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4791
4792 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4793 if (FAILED(rc)) return rc;
4794
4795 if (Global::IsOnlineOrTransient(mData->mMachineState))
4796 return setError(VBOX_E_INVALID_VM_STATE,
4797 tr("Invalid machine state: %s"),
4798 Global::stringifyMachineState(mData->mMachineState));
4799
4800 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4801 aName,
4802 aControllerPort,
4803 aDevice);
4804 if (!pAttach)
4805 return setError(VBOX_E_OBJECT_NOT_FOUND,
4806 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4807 aDevice, aControllerPort, aName.c_str());
4808
4809
4810 i_setModified(IsModified_Storage);
4811 mMediumAttachments.backup();
4812
4813 IBandwidthGroup *iB = aBandwidthGroup;
4814 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4815 if (aBandwidthGroup && group.isNull())
4816 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4817
4818 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4819
4820 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4821 if (strBandwidthGroupOld.isNotEmpty())
4822 {
4823 /* Get the bandwidth group object and release it - this must not fail. */
4824 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4825 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4826 Assert(SUCCEEDED(rc));
4827
4828 pBandwidthGroupOld->i_release();
4829 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4830 }
4831
4832 if (!group.isNull())
4833 {
4834 group->i_reference();
4835 pAttach->i_updateBandwidthGroup(group->i_getName());
4836 }
4837
4838 return S_OK;
4839}
4840
4841HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4842 LONG aControllerPort,
4843 LONG aDevice,
4844 DeviceType_T aType)
4845{
4846 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4847 aName.c_str(), aControllerPort, aDevice, aType));
4848
4849 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4850}
4851
4852
4853HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4854 LONG aControllerPort,
4855 LONG aDevice,
4856 BOOL aForce)
4857{
4858 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4859 aName.c_str(), aControllerPort, aForce));
4860
4861 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4862}
4863
4864HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4865 LONG aControllerPort,
4866 LONG aDevice,
4867 const ComPtr<IMedium> &aMedium,
4868 BOOL aForce)
4869{
4870 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4871 aName.c_str(), aControllerPort, aDevice, aForce));
4872
4873 // request the host lock first, since might be calling Host methods for getting host drives;
4874 // next, protect the media tree all the while we're in here, as well as our member variables
4875 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4876 this->lockHandle(),
4877 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4878
4879 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4880 if (FAILED(hrc)) return hrc;
4881
4882 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4883 aName,
4884 aControllerPort,
4885 aDevice);
4886 if (pAttach.isNull())
4887 return setError(VBOX_E_OBJECT_NOT_FOUND,
4888 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4889 aDevice, aControllerPort, aName.c_str());
4890
4891 /* Remember previously mounted medium. The medium before taking the
4892 * backup is not necessarily the same thing. */
4893 ComObjPtr<Medium> oldmedium;
4894 oldmedium = pAttach->i_getMedium();
4895
4896 IMedium *iM = aMedium;
4897 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4898 if (aMedium && pMedium.isNull())
4899 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4900
4901 /* Check if potential medium is already mounted */
4902 if (pMedium == oldmedium)
4903 return S_OK;
4904
4905 AutoCaller mediumCaller(pMedium);
4906 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4907
4908 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4909 if (pMedium)
4910 {
4911 DeviceType_T mediumType = pAttach->i_getType();
4912 switch (mediumType)
4913 {
4914 case DeviceType_DVD:
4915 case DeviceType_Floppy:
4916 break;
4917
4918 default:
4919 return setError(VBOX_E_INVALID_OBJECT_STATE,
4920 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4921 aControllerPort,
4922 aDevice,
4923 aName.c_str());
4924 }
4925 }
4926
4927 i_setModified(IsModified_Storage);
4928 mMediumAttachments.backup();
4929
4930 {
4931 // The backup operation makes the pAttach reference point to the
4932 // old settings. Re-get the correct reference.
4933 pAttach = i_findAttachment(*mMediumAttachments.data(),
4934 aName,
4935 aControllerPort,
4936 aDevice);
4937 if (!oldmedium.isNull())
4938 oldmedium->i_removeBackReference(mData->mUuid);
4939 if (!pMedium.isNull())
4940 {
4941 pMedium->i_addBackReference(mData->mUuid);
4942
4943 mediumLock.release();
4944 multiLock.release();
4945 i_addMediumToRegistry(pMedium);
4946 multiLock.acquire();
4947 mediumLock.acquire();
4948 }
4949
4950 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4951 pAttach->i_updateMedium(pMedium);
4952 }
4953
4954 i_setModified(IsModified_Storage);
4955
4956 mediumLock.release();
4957 multiLock.release();
4958 HRESULT rc = i_onMediumChange(pAttach, aForce);
4959 multiLock.acquire();
4960 mediumLock.acquire();
4961
4962 /* On error roll back this change only. */
4963 if (FAILED(rc))
4964 {
4965 if (!pMedium.isNull())
4966 pMedium->i_removeBackReference(mData->mUuid);
4967 pAttach = i_findAttachment(*mMediumAttachments.data(),
4968 aName,
4969 aControllerPort,
4970 aDevice);
4971 /* If the attachment is gone in the meantime, bail out. */
4972 if (pAttach.isNull())
4973 return rc;
4974 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4975 if (!oldmedium.isNull())
4976 oldmedium->i_addBackReference(mData->mUuid);
4977 pAttach->i_updateMedium(oldmedium);
4978 }
4979
4980 mediumLock.release();
4981 multiLock.release();
4982
4983 /* Save modified registries, but skip this machine as it's the caller's
4984 * job to save its settings like all other settings changes. */
4985 mParent->i_unmarkRegistryModified(i_getId());
4986 mParent->i_saveModifiedRegistries();
4987
4988 return rc;
4989}
4990HRESULT Machine::getMedium(const com::Utf8Str &aName,
4991 LONG aControllerPort,
4992 LONG aDevice,
4993 ComPtr<IMedium> &aMedium)
4994{
4995 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4996 aName.c_str(), aControllerPort, aDevice));
4997
4998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4999
5000 aMedium = NULL;
5001
5002 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5003 aName,
5004 aControllerPort,
5005 aDevice);
5006 if (pAttach.isNull())
5007 return setError(VBOX_E_OBJECT_NOT_FOUND,
5008 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5009 aDevice, aControllerPort, aName.c_str());
5010
5011 aMedium = pAttach->i_getMedium();
5012
5013 return S_OK;
5014}
5015
5016HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
5017{
5018 if (aSlot < RT_ELEMENTS(mSerialPorts))
5019 {
5020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5021 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5022 return S_OK;
5023 }
5024 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
5025}
5026
5027HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
5028{
5029 if (aSlot < RT_ELEMENTS(mParallelPorts))
5030 {
5031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5032 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5033 return S_OK;
5034 }
5035 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
5036}
5037
5038
5039HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
5040{
5041 /* Do not assert if slot is out of range, just return the advertised
5042 status. testdriver/vbox.py triggers this in logVmInfo. */
5043 if (aSlot >= mNetworkAdapters.size())
5044 return setError(E_INVALIDARG,
5045 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
5046 aSlot, mNetworkAdapters.size());
5047
5048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5049
5050 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
5051
5052 return S_OK;
5053}
5054
5055HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
5056{
5057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5058
5059 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
5060 size_t i = 0;
5061 for (settings::StringsMap::const_iterator
5062 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5063 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5064 ++it, ++i)
5065 aKeys[i] = it->first;
5066
5067 return S_OK;
5068}
5069
5070 /**
5071 * @note Locks this object for reading.
5072 */
5073HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
5074 com::Utf8Str &aValue)
5075{
5076 /* start with nothing found */
5077 aValue = "";
5078
5079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5080
5081 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5082 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5083 // found:
5084 aValue = it->second; // source is a Utf8Str
5085
5086 /* return the result to caller (may be empty) */
5087 return S_OK;
5088}
5089
5090 /**
5091 * @note Locks mParent for writing + this object for writing.
5092 */
5093HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5094{
5095 /* Because control characters in aKey have caused problems in the settings
5096 * they are rejected unless the key should be deleted. */
5097 if (!aValue.isEmpty())
5098 {
5099 for (size_t i = 0; i < aKey.length(); ++i)
5100 {
5101 char ch = aKey[i];
5102 if (RTLocCIsCntrl(ch))
5103 return E_INVALIDARG;
5104 }
5105 }
5106
5107 Utf8Str strOldValue; // empty
5108
5109 // locking note: we only hold the read lock briefly to look up the old value,
5110 // then release it and call the onExtraCanChange callbacks. There is a small
5111 // chance of a race insofar as the callback might be called twice if two callers
5112 // change the same key at the same time, but that's a much better solution
5113 // than the deadlock we had here before. The actual changing of the extradata
5114 // is then performed under the write lock and race-free.
5115
5116 // look up the old value first; if nothing has changed then we need not do anything
5117 {
5118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5119
5120 // For snapshots don't even think about allowing changes, extradata
5121 // is global for a machine, so there is nothing snapshot specific.
5122 if (i_isSnapshotMachine())
5123 return setError(VBOX_E_INVALID_VM_STATE,
5124 tr("Cannot set extradata for a snapshot"));
5125
5126 // check if the right IMachine instance is used
5127 if (mData->mRegistered && !i_isSessionMachine())
5128 return setError(VBOX_E_INVALID_VM_STATE,
5129 tr("Cannot set extradata for an immutable machine"));
5130
5131 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5132 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5133 strOldValue = it->second;
5134 }
5135
5136 bool fChanged;
5137 if ((fChanged = (strOldValue != aValue)))
5138 {
5139 // ask for permission from all listeners outside the locks;
5140 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5141 // lock to copy the list of callbacks to invoke
5142 Bstr bstrError;
5143 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
5144 {
5145 const char *sep = bstrError.isEmpty() ? "" : ": ";
5146 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
5147 return setError(E_ACCESSDENIED,
5148 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5149 aKey.c_str(),
5150 aValue.c_str(),
5151 sep,
5152 bstrError.raw());
5153 }
5154
5155 // data is changing and change not vetoed: then write it out under the lock
5156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5157
5158 if (aValue.isEmpty())
5159 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5160 else
5161 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5162 // creates a new key if needed
5163
5164 bool fNeedsGlobalSaveSettings = false;
5165 // This saving of settings is tricky: there is no "old state" for the
5166 // extradata items at all (unlike all other settings), so the old/new
5167 // settings comparison would give a wrong result!
5168 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
5169
5170 if (fNeedsGlobalSaveSettings)
5171 {
5172 // save the global settings; for that we should hold only the VirtualBox lock
5173 alock.release();
5174 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5175 mParent->i_saveSettings();
5176 }
5177 }
5178
5179 // fire notification outside the lock
5180 if (fChanged)
5181 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
5182
5183 return S_OK;
5184}
5185
5186HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5187{
5188 aProgress = NULL;
5189 NOREF(aSettingsFilePath);
5190 ReturnComNotImplemented();
5191}
5192
5193HRESULT Machine::saveSettings()
5194{
5195 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5196
5197 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5198 if (FAILED(rc)) return rc;
5199
5200 /* the settings file path may never be null */
5201 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5202
5203 /* save all VM data excluding snapshots */
5204 bool fNeedsGlobalSaveSettings = false;
5205 rc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
5206 mlock.release();
5207
5208 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5209 {
5210 // save the global settings; for that we should hold only the VirtualBox lock
5211 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5212 rc = mParent->i_saveSettings();
5213 }
5214
5215 return rc;
5216}
5217
5218
5219HRESULT Machine::discardSettings()
5220{
5221 /*
5222 * We need to take the machine list lock here as well as the machine one
5223 * or we'll get into trouble should any media stuff require rolling back.
5224 *
5225 * Details:
5226 *
5227 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5228 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5229 * 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]
5230 * 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
5231 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5232 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5233 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5234 * 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
5235 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5236 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5237 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5238 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5239 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5240 * 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]
5241 * 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] (*)
5242 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5243 * 0:005> k
5244 * # Child-SP RetAddr Call Site
5245 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5246 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5247 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5248 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5249 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5250 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5251 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5252 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5253 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5254 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5255 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5256 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5257 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5258 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5259 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5260 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5261 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5262 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5263 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5264 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5265 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5266 *
5267 */
5268 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5269 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5270
5271 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5272 if (FAILED(rc)) return rc;
5273
5274 /*
5275 * during this rollback, the session will be notified if data has
5276 * been actually changed
5277 */
5278 i_rollback(true /* aNotify */);
5279
5280 return S_OK;
5281}
5282
5283/** @note Locks objects! */
5284HRESULT Machine::unregister(AutoCaller &autoCaller,
5285 CleanupMode_T aCleanupMode,
5286 std::vector<ComPtr<IMedium> > &aMedia)
5287{
5288 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5289
5290 Guid id(i_getId());
5291
5292 if (mData->mSession.mState != SessionState_Unlocked)
5293 return setError(VBOX_E_INVALID_OBJECT_STATE,
5294 tr("Cannot unregister the machine '%s' while it is locked"),
5295 mUserData->s.strName.c_str());
5296
5297 // wait for state dependents to drop to zero
5298 i_ensureNoStateDependencies(alock);
5299
5300 if (!mData->mAccessible)
5301 {
5302 // inaccessible machines can only be unregistered; uninitialize ourselves
5303 // here because currently there may be no unregistered that are inaccessible
5304 // (this state combination is not supported). Note releasing the caller and
5305 // leaving the lock before calling uninit()
5306 alock.release();
5307 autoCaller.release();
5308
5309 uninit();
5310
5311 mParent->i_unregisterMachine(this, CleanupMode_UnregisterOnly, id);
5312 // calls VirtualBox::i_saveSettings()
5313
5314 return S_OK;
5315 }
5316
5317 HRESULT rc = S_OK;
5318 mData->llFilesToDelete.clear();
5319
5320 if (!mSSData->strStateFilePath.isEmpty())
5321 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5322
5323 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
5324 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
5325 mData->llFilesToDelete.push_back(strNVRAMFile);
5326
5327 // This list collects the medium objects from all medium attachments
5328 // which we will detach from the machine and its snapshots, in a specific
5329 // order which allows for closing all media without getting "media in use"
5330 // errors, simply by going through the list from the front to the back:
5331 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5332 // and must be closed before the parent media from the snapshots, or closing the parents
5333 // will fail because they still have children);
5334 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5335 // the root ("first") snapshot of the machine.
5336 MediaList llMedia;
5337
5338 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5339 && mMediumAttachments->size()
5340 )
5341 {
5342 // we have media attachments: detach them all and add the Medium objects to our list
5343 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5344 }
5345
5346 if (mData->mFirstSnapshot)
5347 {
5348 // add the media from the medium attachments of the snapshots to
5349 // llMedia as well, after the "main" machine media;
5350 // Snapshot::uninitAll() calls Machine::detachAllMedia() for each
5351 // snapshot machine, depth first.
5352
5353 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5354 MachineState_T oldState = mData->mMachineState;
5355 mData->mMachineState = MachineState_DeletingSnapshot;
5356
5357 // make a copy of the first snapshot reference so the refcount does not
5358 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
5359 // (would hang due to the AutoCaller voodoo)
5360 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5361
5362 // GO!
5363 pFirstSnapshot->i_uninitAll(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5364
5365 mData->mMachineState = oldState;
5366 }
5367
5368 if (FAILED(rc))
5369 {
5370 i_rollbackMedia();
5371 return rc;
5372 }
5373
5374 // commit all the media changes made above
5375 i_commitMedia();
5376
5377 mData->mRegistered = false;
5378
5379 // machine lock no longer needed
5380 alock.release();
5381
5382 /* Make sure that the settings of the current VM are not saved, because
5383 * they are rather crippled at this point to meet the cleanup expectations
5384 * and there's no point destroying the VM config on disk just because. */
5385 mParent->i_unmarkRegistryModified(id);
5386
5387 // return media to caller
5388 aMedia.resize(llMedia.size());
5389 size_t i = 0;
5390 for (MediaList::const_iterator
5391 it = llMedia.begin();
5392 it != llMedia.end();
5393 ++it, ++i)
5394 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5395
5396 mParent->i_unregisterMachine(this, aCleanupMode, id);
5397 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5398
5399 return S_OK;
5400}
5401
5402/**
5403 * Task record for deleting a machine config.
5404 */
5405class Machine::DeleteConfigTask
5406 : public Machine::Task
5407{
5408public:
5409 DeleteConfigTask(Machine *m,
5410 Progress *p,
5411 const Utf8Str &t,
5412 const RTCList<ComPtr<IMedium> > &llMediums,
5413 const StringsList &llFilesToDelete)
5414 : Task(m, p, t),
5415 m_llMediums(llMediums),
5416 m_llFilesToDelete(llFilesToDelete)
5417 {}
5418
5419private:
5420 void handler()
5421 {
5422 try
5423 {
5424 m_pMachine->i_deleteConfigHandler(*this);
5425 }
5426 catch (...)
5427 {
5428 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5429 }
5430 }
5431
5432 RTCList<ComPtr<IMedium> > m_llMediums;
5433 StringsList m_llFilesToDelete;
5434
5435 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5436};
5437
5438/**
5439 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5440 * SessionMachine::taskHandler().
5441 *
5442 * @note Locks this object for writing.
5443 *
5444 * @param task
5445 * @return
5446 */
5447void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5448{
5449 LogFlowThisFuncEnter();
5450
5451 AutoCaller autoCaller(this);
5452 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5453 if (FAILED(autoCaller.rc()))
5454 {
5455 /* we might have been uninitialized because the session was accidentally
5456 * closed by the client, so don't assert */
5457 HRESULT rc = setError(E_FAIL,
5458 tr("The session has been accidentally closed"));
5459 task.m_pProgress->i_notifyComplete(rc);
5460 LogFlowThisFuncLeave();
5461 return;
5462 }
5463
5464 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5465
5466 HRESULT rc = S_OK;
5467
5468 try
5469 {
5470 ULONG uLogHistoryCount = 3;
5471 ComPtr<ISystemProperties> systemProperties;
5472 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5473 if (FAILED(rc)) throw rc;
5474
5475 if (!systemProperties.isNull())
5476 {
5477 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5478 if (FAILED(rc)) throw rc;
5479 }
5480
5481 MachineState_T oldState = mData->mMachineState;
5482 i_setMachineState(MachineState_SettingUp);
5483 alock.release();
5484 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5485 {
5486 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5487 {
5488 AutoCaller mac(pMedium);
5489 if (FAILED(mac.rc())) throw mac.rc();
5490 Utf8Str strLocation = pMedium->i_getLocationFull();
5491 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5492 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5493 if (FAILED(rc)) throw rc;
5494 }
5495 if (pMedium->i_isMediumFormatFile())
5496 {
5497 ComPtr<IProgress> pProgress2;
5498 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5499 if (FAILED(rc)) throw rc;
5500 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5501 if (FAILED(rc)) throw rc;
5502 }
5503
5504 /* Close the medium, deliberately without checking the return
5505 * code, and without leaving any trace in the error info, as
5506 * a failure here is a very minor issue, which shouldn't happen
5507 * as above we even managed to delete the medium. */
5508 {
5509 ErrorInfoKeeper eik;
5510 pMedium->Close();
5511 }
5512 }
5513 i_setMachineState(oldState);
5514 alock.acquire();
5515
5516 // delete the files pushed on the task list by Machine::Delete()
5517 // (this includes saved states of the machine and snapshots and
5518 // medium storage files from the IMedium list passed in, and the
5519 // machine XML file)
5520 for (StringsList::const_iterator
5521 it = task.m_llFilesToDelete.begin();
5522 it != task.m_llFilesToDelete.end();
5523 ++it)
5524 {
5525 const Utf8Str &strFile = *it;
5526 LogFunc(("Deleting file %s\n", strFile.c_str()));
5527 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5528 if (FAILED(rc)) throw rc;
5529 i_deleteFile(strFile);
5530 }
5531
5532 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5533 if (FAILED(rc)) throw rc;
5534
5535 /* delete the settings only when the file actually exists */
5536 if (mData->pMachineConfigFile->fileExists())
5537 {
5538 /* Delete any backup or uncommitted XML files. Ignore failures.
5539 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5540 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5541 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5542 i_deleteFile(otherXml, true /* fIgnoreFailures */);
5543 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5544 i_deleteFile(otherXml, true /* fIgnoreFailures */);
5545
5546 /* delete the Logs folder, nothing important should be left
5547 * there (we don't check for errors because the user might have
5548 * some private files there that we don't want to delete) */
5549 Utf8Str logFolder;
5550 getLogFolder(logFolder);
5551 Assert(logFolder.length());
5552 if (RTDirExists(logFolder.c_str()))
5553 {
5554 /* Delete all VBox.log[.N] files from the Logs folder
5555 * (this must be in sync with the rotation logic in
5556 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5557 * files that may have been created by the GUI. */
5558 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5559 i_deleteFile(log, true /* fIgnoreFailures */);
5560 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5561 i_deleteFile(log, true /* fIgnoreFailures */);
5562 for (ULONG i = uLogHistoryCount; i > 0; i--)
5563 {
5564 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5565 i_deleteFile(log, true /* fIgnoreFailures */);
5566 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5567 i_deleteFile(log, true /* fIgnoreFailures */);
5568 }
5569 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5570 i_deleteFile(log, true /* fIgnoreFailures */);
5571#if defined(RT_OS_WINDOWS)
5572 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5573 i_deleteFile(log, true /* fIgnoreFailures */);
5574 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5575 i_deleteFile(log, true /* fIgnoreFailures */);
5576#endif
5577
5578 RTDirRemove(logFolder.c_str());
5579 }
5580
5581 /* delete the Snapshots folder, nothing important should be left
5582 * there (we don't check for errors because the user might have
5583 * some private files there that we don't want to delete) */
5584 Utf8Str strFullSnapshotFolder;
5585 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5586 Assert(!strFullSnapshotFolder.isEmpty());
5587 if (RTDirExists(strFullSnapshotFolder.c_str()))
5588 RTDirRemove(strFullSnapshotFolder.c_str());
5589
5590 // delete the directory that contains the settings file, but only
5591 // if it matches the VM name
5592 Utf8Str settingsDir;
5593 if (i_isInOwnDir(&settingsDir))
5594 RTDirRemove(settingsDir.c_str());
5595 }
5596
5597 alock.release();
5598
5599 mParent->i_saveModifiedRegistries();
5600 }
5601 catch (HRESULT aRC) { rc = aRC; }
5602
5603 task.m_pProgress->i_notifyComplete(rc);
5604
5605 LogFlowThisFuncLeave();
5606}
5607
5608HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5609{
5610 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5611
5612 HRESULT rc = i_checkStateDependency(MutableStateDep);
5613 if (FAILED(rc)) return rc;
5614
5615 if (mData->mRegistered)
5616 return setError(VBOX_E_INVALID_VM_STATE,
5617 tr("Cannot delete settings of a registered machine"));
5618
5619 // collect files to delete
5620 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5621 // machine config file
5622 if (mData->pMachineConfigFile->fileExists())
5623 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5624 // backup of machine config file
5625 Utf8Str strTmp(mData->m_strConfigFileFull);
5626 strTmp.append("-prev");
5627 if (RTFileExists(strTmp.c_str()))
5628 llFilesToDelete.push_back(strTmp);
5629
5630 RTCList<ComPtr<IMedium> > llMediums;
5631 for (size_t i = 0; i < aMedia.size(); ++i)
5632 {
5633 IMedium *pIMedium(aMedia[i]);
5634 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5635 if (pMedium.isNull())
5636 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
5637 SafeArray<BSTR> ids;
5638 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5639 if (FAILED(rc)) return rc;
5640 /* At this point the medium should not have any back references
5641 * anymore. If it has it is attached to another VM and *must* not
5642 * deleted. */
5643 if (ids.size() < 1)
5644 llMediums.append(pMedium);
5645 }
5646
5647 ComObjPtr<Progress> pProgress;
5648 pProgress.createObject();
5649 rc = pProgress->init(i_getVirtualBox(),
5650 static_cast<IMachine*>(this) /* aInitiator */,
5651 tr("Deleting files"),
5652 true /* fCancellable */,
5653 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5654 tr("Collecting file inventory"));
5655 if (FAILED(rc))
5656 return rc;
5657
5658 /* create and start the task on a separate thread (note that it will not
5659 * start working until we release alock) */
5660 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5661 rc = pTask->createThread();
5662 pTask = NULL;
5663 if (FAILED(rc))
5664 return rc;
5665
5666 pProgress.queryInterfaceTo(aProgress.asOutParam());
5667
5668 LogFlowFuncLeave();
5669
5670 return S_OK;
5671}
5672
5673HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5674{
5675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5676
5677 ComObjPtr<Snapshot> pSnapshot;
5678 HRESULT rc;
5679
5680 if (aNameOrId.isEmpty())
5681 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5682 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5683 else
5684 {
5685 Guid uuid(aNameOrId);
5686 if (uuid.isValid())
5687 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5688 else
5689 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5690 }
5691 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5692
5693 return rc;
5694}
5695
5696HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5697 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5698{
5699 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5700
5701 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5702 if (FAILED(rc)) return rc;
5703
5704 ComObjPtr<SharedFolder> sharedFolder;
5705 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5706 if (SUCCEEDED(rc))
5707 return setError(VBOX_E_OBJECT_IN_USE,
5708 tr("Shared folder named '%s' already exists"),
5709 aName.c_str());
5710
5711 sharedFolder.createObject();
5712 rc = sharedFolder->init(i_getMachine(),
5713 aName,
5714 aHostPath,
5715 !!aWritable,
5716 !!aAutomount,
5717 aAutoMountPoint,
5718 true /* fFailOnError */);
5719 if (FAILED(rc)) return rc;
5720
5721 i_setModified(IsModified_SharedFolders);
5722 mHWData.backup();
5723 mHWData->mSharedFolders.push_back(sharedFolder);
5724
5725 /* inform the direct session if any */
5726 alock.release();
5727 i_onSharedFolderChange();
5728
5729 return S_OK;
5730}
5731
5732HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5733{
5734 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5735
5736 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5737 if (FAILED(rc)) return rc;
5738
5739 ComObjPtr<SharedFolder> sharedFolder;
5740 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5741 if (FAILED(rc)) return rc;
5742
5743 i_setModified(IsModified_SharedFolders);
5744 mHWData.backup();
5745 mHWData->mSharedFolders.remove(sharedFolder);
5746
5747 /* inform the direct session if any */
5748 alock.release();
5749 i_onSharedFolderChange();
5750
5751 return S_OK;
5752}
5753
5754HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5755{
5756 /* start with No */
5757 *aCanShow = FALSE;
5758
5759 ComPtr<IInternalSessionControl> directControl;
5760 {
5761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5762
5763 if (mData->mSession.mState != SessionState_Locked)
5764 return setError(VBOX_E_INVALID_VM_STATE,
5765 tr("Machine is not locked for session (session state: %s)"),
5766 Global::stringifySessionState(mData->mSession.mState));
5767
5768 if (mData->mSession.mLockType == LockType_VM)
5769 directControl = mData->mSession.mDirectControl;
5770 }
5771
5772 /* ignore calls made after #OnSessionEnd() is called */
5773 if (!directControl)
5774 return S_OK;
5775
5776 LONG64 dummy;
5777 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5778}
5779
5780HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5781{
5782 ComPtr<IInternalSessionControl> directControl;
5783 {
5784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5785
5786 if (mData->mSession.mState != SessionState_Locked)
5787 return setError(E_FAIL,
5788 tr("Machine is not locked for session (session state: %s)"),
5789 Global::stringifySessionState(mData->mSession.mState));
5790
5791 if (mData->mSession.mLockType == LockType_VM)
5792 directControl = mData->mSession.mDirectControl;
5793 }
5794
5795 /* ignore calls made after #OnSessionEnd() is called */
5796 if (!directControl)
5797 return S_OK;
5798
5799 BOOL dummy;
5800 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5801}
5802
5803#ifdef VBOX_WITH_GUEST_PROPS
5804/**
5805 * Look up a guest property in VBoxSVC's internal structures.
5806 */
5807HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5808 com::Utf8Str &aValue,
5809 LONG64 *aTimestamp,
5810 com::Utf8Str &aFlags) const
5811{
5812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5813
5814 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5815 if (it != mHWData->mGuestProperties.end())
5816 {
5817 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5818 aValue = it->second.strValue;
5819 *aTimestamp = it->second.mTimestamp;
5820 GuestPropWriteFlags(it->second.mFlags, szFlags);
5821 aFlags = Utf8Str(szFlags);
5822 }
5823
5824 return S_OK;
5825}
5826
5827/**
5828 * Query the VM that a guest property belongs to for the property.
5829 * @returns E_ACCESSDENIED if the VM process is not available or not
5830 * currently handling queries and the lookup should then be done in
5831 * VBoxSVC.
5832 */
5833HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5834 com::Utf8Str &aValue,
5835 LONG64 *aTimestamp,
5836 com::Utf8Str &aFlags) const
5837{
5838 HRESULT rc = S_OK;
5839 Bstr bstrValue;
5840 Bstr bstrFlags;
5841
5842 ComPtr<IInternalSessionControl> directControl;
5843 {
5844 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5845 if (mData->mSession.mLockType == LockType_VM)
5846 directControl = mData->mSession.mDirectControl;
5847 }
5848
5849 /* ignore calls made after #OnSessionEnd() is called */
5850 if (!directControl)
5851 rc = E_ACCESSDENIED;
5852 else
5853 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5854 0 /* accessMode */,
5855 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5856
5857 aValue = bstrValue;
5858 aFlags = bstrFlags;
5859
5860 return rc;
5861}
5862#endif // VBOX_WITH_GUEST_PROPS
5863
5864HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5865 com::Utf8Str &aValue,
5866 LONG64 *aTimestamp,
5867 com::Utf8Str &aFlags)
5868{
5869#ifndef VBOX_WITH_GUEST_PROPS
5870 ReturnComNotImplemented();
5871#else // VBOX_WITH_GUEST_PROPS
5872
5873 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5874
5875 if (rc == E_ACCESSDENIED)
5876 /* The VM is not running or the service is not (yet) accessible */
5877 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5878 return rc;
5879#endif // VBOX_WITH_GUEST_PROPS
5880}
5881
5882HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5883{
5884 LONG64 dummyTimestamp;
5885 com::Utf8Str dummyFlags;
5886 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5887 return rc;
5888
5889}
5890HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5891{
5892 com::Utf8Str dummyFlags;
5893 com::Utf8Str dummyValue;
5894 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5895 return rc;
5896}
5897
5898#ifdef VBOX_WITH_GUEST_PROPS
5899/**
5900 * Set a guest property in VBoxSVC's internal structures.
5901 */
5902HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5903 const com::Utf8Str &aFlags, bool fDelete)
5904{
5905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5906 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5907 if (FAILED(rc)) return rc;
5908
5909 try
5910 {
5911 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5912 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5913 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5914
5915 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
5916 return setError(E_INVALIDARG, tr("Properties with TRANSIENT or TRANSRESET flag cannot be set or modified if VM is not running"));
5917
5918 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5919 if (it == mHWData->mGuestProperties.end())
5920 {
5921 if (!fDelete)
5922 {
5923 i_setModified(IsModified_MachineData);
5924 mHWData.backupEx();
5925
5926 RTTIMESPEC time;
5927 HWData::GuestProperty prop;
5928 prop.strValue = aValue;
5929 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5930 prop.mFlags = fFlags;
5931 mHWData->mGuestProperties[aName] = prop;
5932 }
5933 }
5934 else
5935 {
5936 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5937 {
5938 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5939 }
5940 else
5941 {
5942 i_setModified(IsModified_MachineData);
5943 mHWData.backupEx();
5944
5945 /* The backupEx() operation invalidates our iterator,
5946 * so get a new one. */
5947 it = mHWData->mGuestProperties.find(aName);
5948 Assert(it != mHWData->mGuestProperties.end());
5949
5950 if (!fDelete)
5951 {
5952 RTTIMESPEC time;
5953 it->second.strValue = aValue;
5954 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5955 it->second.mFlags = fFlags;
5956 }
5957 else
5958 mHWData->mGuestProperties.erase(it);
5959 }
5960 }
5961
5962 if (SUCCEEDED(rc))
5963 {
5964 alock.release();
5965
5966 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fDelete);
5967 }
5968 }
5969 catch (std::bad_alloc &)
5970 {
5971 rc = E_OUTOFMEMORY;
5972 }
5973
5974 return rc;
5975}
5976
5977/**
5978 * Set a property on the VM that that property belongs to.
5979 * @returns E_ACCESSDENIED if the VM process is not available or not
5980 * currently handling queries and the setting should then be done in
5981 * VBoxSVC.
5982 */
5983HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5984 const com::Utf8Str &aFlags, bool fDelete)
5985{
5986 HRESULT rc;
5987
5988 try
5989 {
5990 ComPtr<IInternalSessionControl> directControl;
5991 {
5992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5993 if (mData->mSession.mLockType == LockType_VM)
5994 directControl = mData->mSession.mDirectControl;
5995 }
5996
5997 Bstr dummy1; /* will not be changed (setter) */
5998 Bstr dummy2; /* will not be changed (setter) */
5999 LONG64 dummy64;
6000 if (!directControl)
6001 rc = E_ACCESSDENIED;
6002 else
6003 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6004 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
6005 fDelete ? 2 : 1 /* accessMode */,
6006 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
6007 }
6008 catch (std::bad_alloc &)
6009 {
6010 rc = E_OUTOFMEMORY;
6011 }
6012
6013 return rc;
6014}
6015#endif // VBOX_WITH_GUEST_PROPS
6016
6017HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
6018 const com::Utf8Str &aFlags)
6019{
6020#ifndef VBOX_WITH_GUEST_PROPS
6021 ReturnComNotImplemented();
6022#else // VBOX_WITH_GUEST_PROPS
6023
6024 int vrc = GuestPropValidateName(aProperty.c_str(), aProperty.length() + 1 /* '\0' */);
6025 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
6026
6027 vrc = GuestPropValidateValue(aValue.c_str(), aValue.length() + 1 /* '\0' */);
6028 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
6029
6030 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
6031 if (rc == E_ACCESSDENIED)
6032 /* The VM is not running or the service is not (yet) accessible */
6033 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
6034 return rc;
6035#endif // VBOX_WITH_GUEST_PROPS
6036}
6037
6038HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
6039{
6040 return setGuestProperty(aProperty, aValue, "");
6041}
6042
6043HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
6044{
6045#ifndef VBOX_WITH_GUEST_PROPS
6046 ReturnComNotImplemented();
6047#else // VBOX_WITH_GUEST_PROPS
6048 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
6049 if (rc == E_ACCESSDENIED)
6050 /* The VM is not running or the service is not (yet) accessible */
6051 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
6052 return rc;
6053#endif // VBOX_WITH_GUEST_PROPS
6054}
6055
6056#ifdef VBOX_WITH_GUEST_PROPS
6057/**
6058 * Enumerate the guest properties in VBoxSVC's internal structures.
6059 */
6060HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
6061 std::vector<com::Utf8Str> &aNames,
6062 std::vector<com::Utf8Str> &aValues,
6063 std::vector<LONG64> &aTimestamps,
6064 std::vector<com::Utf8Str> &aFlags)
6065{
6066 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6067 Utf8Str strPatterns(aPatterns);
6068
6069 /*
6070 * Look for matching patterns and build up a list.
6071 */
6072 HWData::GuestPropertyMap propMap;
6073 for (HWData::GuestPropertyMap::const_iterator
6074 it = mHWData->mGuestProperties.begin();
6075 it != mHWData->mGuestProperties.end();
6076 ++it)
6077 {
6078 if ( strPatterns.isEmpty()
6079 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6080 RTSTR_MAX,
6081 it->first.c_str(),
6082 RTSTR_MAX,
6083 NULL)
6084 )
6085 propMap.insert(*it);
6086 }
6087
6088 alock.release();
6089
6090 /*
6091 * And build up the arrays for returning the property information.
6092 */
6093 size_t cEntries = propMap.size();
6094
6095 aNames.resize(cEntries);
6096 aValues.resize(cEntries);
6097 aTimestamps.resize(cEntries);
6098 aFlags.resize(cEntries);
6099
6100 size_t i = 0;
6101 for (HWData::GuestPropertyMap::const_iterator
6102 it = propMap.begin();
6103 it != propMap.end();
6104 ++it, ++i)
6105 {
6106 aNames[i] = it->first;
6107 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
6108 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
6109
6110 aValues[i] = it->second.strValue;
6111 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
6112 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
6113
6114 aTimestamps[i] = it->second.mTimestamp;
6115
6116 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6117 GuestPropWriteFlags(it->second.mFlags, szFlags);
6118 aFlags[i] = szFlags;
6119 }
6120
6121 return S_OK;
6122}
6123
6124/**
6125 * Enumerate the properties managed by a VM.
6126 * @returns E_ACCESSDENIED if the VM process is not available or not
6127 * currently handling queries and the setting should then be done in
6128 * VBoxSVC.
6129 */
6130HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6131 std::vector<com::Utf8Str> &aNames,
6132 std::vector<com::Utf8Str> &aValues,
6133 std::vector<LONG64> &aTimestamps,
6134 std::vector<com::Utf8Str> &aFlags)
6135{
6136 HRESULT rc;
6137 ComPtr<IInternalSessionControl> directControl;
6138 {
6139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6140 if (mData->mSession.mLockType == LockType_VM)
6141 directControl = mData->mSession.mDirectControl;
6142 }
6143
6144 com::SafeArray<BSTR> bNames;
6145 com::SafeArray<BSTR> bValues;
6146 com::SafeArray<LONG64> bTimestamps;
6147 com::SafeArray<BSTR> bFlags;
6148
6149 if (!directControl)
6150 rc = E_ACCESSDENIED;
6151 else
6152 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6153 ComSafeArrayAsOutParam(bNames),
6154 ComSafeArrayAsOutParam(bValues),
6155 ComSafeArrayAsOutParam(bTimestamps),
6156 ComSafeArrayAsOutParam(bFlags));
6157 size_t i;
6158 aNames.resize(bNames.size());
6159 for (i = 0; i < bNames.size(); ++i)
6160 aNames[i] = Utf8Str(bNames[i]);
6161 aValues.resize(bValues.size());
6162 for (i = 0; i < bValues.size(); ++i)
6163 aValues[i] = Utf8Str(bValues[i]);
6164 aTimestamps.resize(bTimestamps.size());
6165 for (i = 0; i < bTimestamps.size(); ++i)
6166 aTimestamps[i] = bTimestamps[i];
6167 aFlags.resize(bFlags.size());
6168 for (i = 0; i < bFlags.size(); ++i)
6169 aFlags[i] = Utf8Str(bFlags[i]);
6170
6171 return rc;
6172}
6173#endif // VBOX_WITH_GUEST_PROPS
6174HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6175 std::vector<com::Utf8Str> &aNames,
6176 std::vector<com::Utf8Str> &aValues,
6177 std::vector<LONG64> &aTimestamps,
6178 std::vector<com::Utf8Str> &aFlags)
6179{
6180#ifndef VBOX_WITH_GUEST_PROPS
6181 ReturnComNotImplemented();
6182#else // VBOX_WITH_GUEST_PROPS
6183
6184 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6185
6186 if (rc == E_ACCESSDENIED)
6187 /* The VM is not running or the service is not (yet) accessible */
6188 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6189 return rc;
6190#endif // VBOX_WITH_GUEST_PROPS
6191}
6192
6193HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6194 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6195{
6196 MediumAttachmentList atts;
6197
6198 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6199 if (FAILED(rc)) return rc;
6200
6201 aMediumAttachments.resize(atts.size());
6202 size_t i = 0;
6203 for (MediumAttachmentList::const_iterator
6204 it = atts.begin();
6205 it != atts.end();
6206 ++it, ++i)
6207 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6208
6209 return S_OK;
6210}
6211
6212HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6213 LONG aControllerPort,
6214 LONG aDevice,
6215 ComPtr<IMediumAttachment> &aAttachment)
6216{
6217 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6218 aName.c_str(), aControllerPort, aDevice));
6219
6220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6221
6222 aAttachment = NULL;
6223
6224 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6225 aName,
6226 aControllerPort,
6227 aDevice);
6228 if (pAttach.isNull())
6229 return setError(VBOX_E_OBJECT_NOT_FOUND,
6230 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6231 aDevice, aControllerPort, aName.c_str());
6232
6233 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6234
6235 return S_OK;
6236}
6237
6238
6239HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6240 StorageBus_T aConnectionType,
6241 ComPtr<IStorageController> &aController)
6242{
6243 if ( (aConnectionType <= StorageBus_Null)
6244 || (aConnectionType > StorageBus_VirtioSCSI))
6245 return setError(E_INVALIDARG,
6246 tr("Invalid connection type: %d"),
6247 aConnectionType);
6248
6249 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6250
6251 HRESULT rc = i_checkStateDependency(MutableStateDep);
6252 if (FAILED(rc)) return rc;
6253
6254 /* try to find one with the name first. */
6255 ComObjPtr<StorageController> ctrl;
6256
6257 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6258 if (SUCCEEDED(rc))
6259 return setError(VBOX_E_OBJECT_IN_USE,
6260 tr("Storage controller named '%s' already exists"),
6261 aName.c_str());
6262
6263 ctrl.createObject();
6264
6265 /* get a new instance number for the storage controller */
6266 ULONG ulInstance = 0;
6267 bool fBootable = true;
6268 for (StorageControllerList::const_iterator
6269 it = mStorageControllers->begin();
6270 it != mStorageControllers->end();
6271 ++it)
6272 {
6273 if ((*it)->i_getStorageBus() == aConnectionType)
6274 {
6275 ULONG ulCurInst = (*it)->i_getInstance();
6276
6277 if (ulCurInst >= ulInstance)
6278 ulInstance = ulCurInst + 1;
6279
6280 /* Only one controller of each type can be marked as bootable. */
6281 if ((*it)->i_getBootable())
6282 fBootable = false;
6283 }
6284 }
6285
6286 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6287 if (FAILED(rc)) return rc;
6288
6289 i_setModified(IsModified_Storage);
6290 mStorageControllers.backup();
6291 mStorageControllers->push_back(ctrl);
6292
6293 ctrl.queryInterfaceTo(aController.asOutParam());
6294
6295 /* inform the direct session if any */
6296 alock.release();
6297 i_onStorageControllerChange(i_getId(), aName);
6298
6299 return S_OK;
6300}
6301
6302HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6303 ComPtr<IStorageController> &aStorageController)
6304{
6305 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6306
6307 ComObjPtr<StorageController> ctrl;
6308
6309 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6310 if (SUCCEEDED(rc))
6311 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6312
6313 return rc;
6314}
6315
6316HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6317 ULONG aInstance,
6318 ComPtr<IStorageController> &aStorageController)
6319{
6320 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6321
6322 for (StorageControllerList::const_iterator
6323 it = mStorageControllers->begin();
6324 it != mStorageControllers->end();
6325 ++it)
6326 {
6327 if ( (*it)->i_getStorageBus() == aConnectionType
6328 && (*it)->i_getInstance() == aInstance)
6329 {
6330 (*it).queryInterfaceTo(aStorageController.asOutParam());
6331 return S_OK;
6332 }
6333 }
6334
6335 return setError(VBOX_E_OBJECT_NOT_FOUND,
6336 tr("Could not find a storage controller with instance number '%lu'"),
6337 aInstance);
6338}
6339
6340HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6341{
6342 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6343
6344 HRESULT rc = i_checkStateDependency(MutableStateDep);
6345 if (FAILED(rc)) return rc;
6346
6347 ComObjPtr<StorageController> ctrl;
6348
6349 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6350 if (SUCCEEDED(rc))
6351 {
6352 /* Ensure that only one controller of each type is marked as bootable. */
6353 if (aBootable == TRUE)
6354 {
6355 for (StorageControllerList::const_iterator
6356 it = mStorageControllers->begin();
6357 it != mStorageControllers->end();
6358 ++it)
6359 {
6360 ComObjPtr<StorageController> aCtrl = (*it);
6361
6362 if ( (aCtrl->i_getName() != aName)
6363 && aCtrl->i_getBootable() == TRUE
6364 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6365 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6366 {
6367 aCtrl->i_setBootable(FALSE);
6368 break;
6369 }
6370 }
6371 }
6372
6373 if (SUCCEEDED(rc))
6374 {
6375 ctrl->i_setBootable(aBootable);
6376 i_setModified(IsModified_Storage);
6377 }
6378 }
6379
6380 if (SUCCEEDED(rc))
6381 {
6382 /* inform the direct session if any */
6383 alock.release();
6384 i_onStorageControllerChange(i_getId(), aName);
6385 }
6386
6387 return rc;
6388}
6389
6390HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6391{
6392 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6393
6394 HRESULT rc = i_checkStateDependency(MutableStateDep);
6395 if (FAILED(rc)) return rc;
6396
6397 ComObjPtr<StorageController> ctrl;
6398 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6399 if (FAILED(rc)) return rc;
6400
6401 MediumAttachmentList llDetachedAttachments;
6402 {
6403 /* find all attached devices to the appropriate storage controller and detach them all */
6404 // make a temporary list because detachDevice invalidates iterators into
6405 // mMediumAttachments
6406 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6407
6408 for (MediumAttachmentList::const_iterator
6409 it = llAttachments2.begin();
6410 it != llAttachments2.end();
6411 ++it)
6412 {
6413 MediumAttachment *pAttachTemp = *it;
6414
6415 AutoCaller localAutoCaller(pAttachTemp);
6416 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6417
6418 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6419
6420 if (pAttachTemp->i_getControllerName() == aName)
6421 {
6422 llDetachedAttachments.push_back(pAttachTemp);
6423 rc = i_detachDevice(pAttachTemp, alock, NULL);
6424 if (FAILED(rc)) return rc;
6425 }
6426 }
6427 }
6428
6429 /* send event about detached devices before removing parent controller */
6430 for (MediumAttachmentList::const_iterator
6431 it = llDetachedAttachments.begin();
6432 it != llDetachedAttachments.end();
6433 ++it)
6434 {
6435 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6436 }
6437
6438 /* We can remove it now. */
6439 i_setModified(IsModified_Storage);
6440 mStorageControllers.backup();
6441
6442 ctrl->i_unshare();
6443
6444 mStorageControllers->remove(ctrl);
6445
6446 /* inform the direct session if any */
6447 alock.release();
6448 i_onStorageControllerChange(i_getId(), aName);
6449
6450 return S_OK;
6451}
6452
6453HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6454 ComPtr<IUSBController> &aController)
6455{
6456 if ( (aType <= USBControllerType_Null)
6457 || (aType >= USBControllerType_Last))
6458 return setError(E_INVALIDARG,
6459 tr("Invalid USB controller type: %d"),
6460 aType);
6461
6462 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6463
6464 HRESULT rc = i_checkStateDependency(MutableStateDep);
6465 if (FAILED(rc)) return rc;
6466
6467 /* try to find one with the same type first. */
6468 ComObjPtr<USBController> ctrl;
6469
6470 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6471 if (SUCCEEDED(rc))
6472 return setError(VBOX_E_OBJECT_IN_USE,
6473 tr("USB controller named '%s' already exists"),
6474 aName.c_str());
6475
6476 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6477 ULONG maxInstances;
6478 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6479 if (FAILED(rc))
6480 return rc;
6481
6482 ULONG cInstances = i_getUSBControllerCountByType(aType);
6483 if (cInstances >= maxInstances)
6484 return setError(E_INVALIDARG,
6485 tr("Too many USB controllers of this type"));
6486
6487 ctrl.createObject();
6488
6489 rc = ctrl->init(this, aName, aType);
6490 if (FAILED(rc)) return rc;
6491
6492 i_setModified(IsModified_USB);
6493 mUSBControllers.backup();
6494 mUSBControllers->push_back(ctrl);
6495
6496 ctrl.queryInterfaceTo(aController.asOutParam());
6497
6498 /* inform the direct session if any */
6499 alock.release();
6500 i_onUSBControllerChange();
6501
6502 return S_OK;
6503}
6504
6505HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6506{
6507 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6508
6509 ComObjPtr<USBController> ctrl;
6510
6511 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6512 if (SUCCEEDED(rc))
6513 ctrl.queryInterfaceTo(aController.asOutParam());
6514
6515 return rc;
6516}
6517
6518HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6519 ULONG *aControllers)
6520{
6521 if ( (aType <= USBControllerType_Null)
6522 || (aType >= USBControllerType_Last))
6523 return setError(E_INVALIDARG,
6524 tr("Invalid USB controller type: %d"),
6525 aType);
6526
6527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6528
6529 ComObjPtr<USBController> ctrl;
6530
6531 *aControllers = i_getUSBControllerCountByType(aType);
6532
6533 return S_OK;
6534}
6535
6536HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6537{
6538
6539 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6540
6541 HRESULT rc = i_checkStateDependency(MutableStateDep);
6542 if (FAILED(rc)) return rc;
6543
6544 ComObjPtr<USBController> ctrl;
6545 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6546 if (FAILED(rc)) return rc;
6547
6548 i_setModified(IsModified_USB);
6549 mUSBControllers.backup();
6550
6551 ctrl->i_unshare();
6552
6553 mUSBControllers->remove(ctrl);
6554
6555 /* inform the direct session if any */
6556 alock.release();
6557 i_onUSBControllerChange();
6558
6559 return S_OK;
6560}
6561
6562HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6563 ULONG *aOriginX,
6564 ULONG *aOriginY,
6565 ULONG *aWidth,
6566 ULONG *aHeight,
6567 BOOL *aEnabled)
6568{
6569 uint32_t u32OriginX= 0;
6570 uint32_t u32OriginY= 0;
6571 uint32_t u32Width = 0;
6572 uint32_t u32Height = 0;
6573 uint16_t u16Flags = 0;
6574
6575#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6576 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6577#else
6578 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6579#endif
6580 int vrc = readSavedGuestScreenInfo(SavedStateStream, mSSData->strStateFilePath, aScreenId,
6581 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6582 if (RT_FAILURE(vrc))
6583 {
6584#ifdef RT_OS_WINDOWS
6585 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6586 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6587 * So just assign fEnable to TRUE again.
6588 * The right fix would be to change GUI API wrappers to make sure that parameters
6589 * are changed only if API succeeds.
6590 */
6591 *aEnabled = TRUE;
6592#endif
6593 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6594 tr("Saved guest size is not available (%Rrc)"),
6595 vrc);
6596 }
6597
6598 *aOriginX = u32OriginX;
6599 *aOriginY = u32OriginY;
6600 *aWidth = u32Width;
6601 *aHeight = u32Height;
6602 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6603
6604 return S_OK;
6605}
6606
6607HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6608 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6609{
6610 if (aScreenId != 0)
6611 return E_NOTIMPL;
6612
6613 if ( aBitmapFormat != BitmapFormat_BGR0
6614 && aBitmapFormat != BitmapFormat_BGRA
6615 && aBitmapFormat != BitmapFormat_RGBA
6616 && aBitmapFormat != BitmapFormat_PNG)
6617 return setError(E_NOTIMPL,
6618 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6619
6620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6621
6622 uint8_t *pu8Data = NULL;
6623 uint32_t cbData = 0;
6624 uint32_t u32Width = 0;
6625 uint32_t u32Height = 0;
6626
6627#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6628 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6629#else
6630 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6631#endif
6632 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 0 /* u32Type */,
6633 &pu8Data, &cbData, &u32Width, &u32Height);
6634 if (RT_FAILURE(vrc))
6635 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6636 tr("Saved thumbnail data is not available (%Rrc)"),
6637 vrc);
6638
6639 HRESULT hr = S_OK;
6640
6641 *aWidth = u32Width;
6642 *aHeight = u32Height;
6643
6644 if (cbData > 0)
6645 {
6646 /* Convert pixels to the format expected by the API caller. */
6647 if (aBitmapFormat == BitmapFormat_BGR0)
6648 {
6649 /* [0] B, [1] G, [2] R, [3] 0. */
6650 aData.resize(cbData);
6651 memcpy(&aData.front(), pu8Data, cbData);
6652 }
6653 else if (aBitmapFormat == BitmapFormat_BGRA)
6654 {
6655 /* [0] B, [1] G, [2] R, [3] A. */
6656 aData.resize(cbData);
6657 for (uint32_t i = 0; i < cbData; i += 4)
6658 {
6659 aData[i] = pu8Data[i];
6660 aData[i + 1] = pu8Data[i + 1];
6661 aData[i + 2] = pu8Data[i + 2];
6662 aData[i + 3] = 0xff;
6663 }
6664 }
6665 else if (aBitmapFormat == BitmapFormat_RGBA)
6666 {
6667 /* [0] R, [1] G, [2] B, [3] A. */
6668 aData.resize(cbData);
6669 for (uint32_t i = 0; i < cbData; i += 4)
6670 {
6671 aData[i] = pu8Data[i + 2];
6672 aData[i + 1] = pu8Data[i + 1];
6673 aData[i + 2] = pu8Data[i];
6674 aData[i + 3] = 0xff;
6675 }
6676 }
6677 else if (aBitmapFormat == BitmapFormat_PNG)
6678 {
6679 uint8_t *pu8PNG = NULL;
6680 uint32_t cbPNG = 0;
6681 uint32_t cxPNG = 0;
6682 uint32_t cyPNG = 0;
6683
6684 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6685
6686 if (RT_SUCCESS(vrc))
6687 {
6688 aData.resize(cbPNG);
6689 if (cbPNG)
6690 memcpy(&aData.front(), pu8PNG, cbPNG);
6691 }
6692 else
6693 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6694 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6695 vrc);
6696
6697 RTMemFree(pu8PNG);
6698 }
6699 }
6700
6701 freeSavedDisplayScreenshot(pu8Data);
6702
6703 return hr;
6704}
6705
6706HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6707 ULONG *aWidth,
6708 ULONG *aHeight,
6709 std::vector<BitmapFormat_T> &aBitmapFormats)
6710{
6711 if (aScreenId != 0)
6712 return E_NOTIMPL;
6713
6714 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6715
6716 uint8_t *pu8Data = NULL;
6717 uint32_t cbData = 0;
6718 uint32_t u32Width = 0;
6719 uint32_t u32Height = 0;
6720
6721#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6722 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6723#else
6724 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6725#endif
6726 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6727 &pu8Data, &cbData, &u32Width, &u32Height);
6728
6729 if (RT_FAILURE(vrc))
6730 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6731 tr("Saved screenshot data is not available (%Rrc)"),
6732 vrc);
6733
6734 *aWidth = u32Width;
6735 *aHeight = u32Height;
6736 aBitmapFormats.resize(1);
6737 aBitmapFormats[0] = BitmapFormat_PNG;
6738
6739 freeSavedDisplayScreenshot(pu8Data);
6740
6741 return S_OK;
6742}
6743
6744HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6745 BitmapFormat_T aBitmapFormat,
6746 ULONG *aWidth,
6747 ULONG *aHeight,
6748 std::vector<BYTE> &aData)
6749{
6750 if (aScreenId != 0)
6751 return E_NOTIMPL;
6752
6753 if (aBitmapFormat != BitmapFormat_PNG)
6754 return E_NOTIMPL;
6755
6756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6757
6758 uint8_t *pu8Data = NULL;
6759 uint32_t cbData = 0;
6760 uint32_t u32Width = 0;
6761 uint32_t u32Height = 0;
6762
6763#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6764 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6765#else
6766 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6767#endif
6768 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6769 &pu8Data, &cbData, &u32Width, &u32Height);
6770
6771 if (RT_FAILURE(vrc))
6772 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6773 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6774 vrc);
6775
6776 *aWidth = u32Width;
6777 *aHeight = u32Height;
6778
6779 aData.resize(cbData);
6780 if (cbData)
6781 memcpy(&aData.front(), pu8Data, cbData);
6782
6783 freeSavedDisplayScreenshot(pu8Data);
6784
6785 return S_OK;
6786}
6787
6788HRESULT Machine::hotPlugCPU(ULONG aCpu)
6789{
6790 HRESULT rc = S_OK;
6791 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6792
6793 if (!mHWData->mCPUHotPlugEnabled)
6794 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6795
6796 if (aCpu >= mHWData->mCPUCount)
6797 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6798
6799 if (mHWData->mCPUAttached[aCpu])
6800 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6801
6802 rc = i_checkStateDependency(MutableOrRunningStateDep);
6803 if (FAILED(rc)) return rc;
6804
6805 alock.release();
6806 rc = i_onCPUChange(aCpu, false);
6807 alock.acquire();
6808 if (FAILED(rc)) return rc;
6809
6810 i_setModified(IsModified_MachineData);
6811 mHWData.backup();
6812 mHWData->mCPUAttached[aCpu] = true;
6813
6814 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6815 if (Global::IsOnline(mData->mMachineState))
6816 i_saveSettings(NULL, alock);
6817
6818 return S_OK;
6819}
6820
6821HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6822{
6823 HRESULT rc = S_OK;
6824
6825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6826
6827 if (!mHWData->mCPUHotPlugEnabled)
6828 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6829
6830 if (aCpu >= SchemaDefs::MaxCPUCount)
6831 return setError(E_INVALIDARG,
6832 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6833 SchemaDefs::MaxCPUCount);
6834
6835 if (!mHWData->mCPUAttached[aCpu])
6836 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6837
6838 /* CPU 0 can't be detached */
6839 if (aCpu == 0)
6840 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6841
6842 rc = i_checkStateDependency(MutableOrRunningStateDep);
6843 if (FAILED(rc)) return rc;
6844
6845 alock.release();
6846 rc = i_onCPUChange(aCpu, true);
6847 alock.acquire();
6848 if (FAILED(rc)) return rc;
6849
6850 i_setModified(IsModified_MachineData);
6851 mHWData.backup();
6852 mHWData->mCPUAttached[aCpu] = false;
6853
6854 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6855 if (Global::IsOnline(mData->mMachineState))
6856 i_saveSettings(NULL, alock);
6857
6858 return S_OK;
6859}
6860
6861HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6862{
6863 *aAttached = false;
6864
6865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6866
6867 /* If hotplug is enabled the CPU is always enabled. */
6868 if (!mHWData->mCPUHotPlugEnabled)
6869 {
6870 if (aCpu < mHWData->mCPUCount)
6871 *aAttached = true;
6872 }
6873 else
6874 {
6875 if (aCpu < SchemaDefs::MaxCPUCount)
6876 *aAttached = mHWData->mCPUAttached[aCpu];
6877 }
6878
6879 return S_OK;
6880}
6881
6882HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6883{
6884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6885
6886 Utf8Str log = i_getLogFilename(aIdx);
6887 if (!RTFileExists(log.c_str()))
6888 log.setNull();
6889 aFilename = log;
6890
6891 return S_OK;
6892}
6893
6894HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6895{
6896 if (aSize < 0)
6897 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6898
6899 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6900
6901 HRESULT rc = S_OK;
6902 Utf8Str log = i_getLogFilename(aIdx);
6903
6904 /* do not unnecessarily hold the lock while doing something which does
6905 * not need the lock and potentially takes a long time. */
6906 alock.release();
6907
6908 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6909 * keeps the SOAP reply size under 1M for the webservice (we're using
6910 * base64 encoded strings for binary data for years now, avoiding the
6911 * expansion of each byte array element to approx. 25 bytes of XML. */
6912 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6913 aData.resize(cbData);
6914
6915 int vrc = VINF_SUCCESS;
6916 RTVFSIOSTREAM hVfsIosLog = NIL_RTVFSIOSTREAM;
6917
6918#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6919 if (mData->mstrLogKeyId.isNotEmpty() && mData->mstrLogKeyStore.isNotEmpty())
6920 {
6921 PCVBOXCRYPTOIF pCryptoIf = NULL;
6922 rc = i_getVirtualBox()->i_retainCryptoIf(&pCryptoIf);
6923 if (SUCCEEDED(rc))
6924 {
6925 alock.acquire();
6926
6927 SecretKey *pKey = NULL;
6928 vrc = mData->mpKeyStore->retainSecretKey(mData->mstrLogKeyId, &pKey);
6929 alock.release();
6930
6931 if (RT_SUCCESS(vrc))
6932 {
6933 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6934 if (RT_SUCCESS(vrc))
6935 {
6936 RTVFSIOSTREAM hVfsIosLogDec = NIL_RTVFSIOSTREAM;
6937 vrc = pCryptoIf->pfnCryptoIoStrmFromVfsIoStrmDecrypt(hVfsIosLog, mData->mstrLogKeyStore.c_str(),
6938 (const char *)pKey->getKeyBuffer(), &hVfsIosLogDec);
6939 if (RT_SUCCESS(vrc))
6940 {
6941 RTVfsIoStrmRelease(hVfsIosLog);
6942 hVfsIosLog = hVfsIosLogDec;
6943 }
6944 }
6945
6946 pKey->release();
6947 }
6948
6949 i_getVirtualBox()->i_releaseCryptoIf(pCryptoIf);
6950 }
6951 }
6952 else
6953 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6954#else
6955 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6956#endif
6957 if (RT_SUCCESS(vrc))
6958 {
6959 vrc = RTVfsIoStrmReadAt(hVfsIosLog, aOffset,
6960 cbData ? &aData.front() : NULL, cbData,
6961 true /*fBlocking*/, &cbData);
6962 if (RT_SUCCESS(vrc))
6963 aData.resize(cbData);
6964 else
6965 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6966 tr("Could not read log file '%s' (%Rrc)"),
6967 log.c_str(), vrc);
6968
6969 RTVfsIoStrmRelease(hVfsIosLog);
6970 }
6971 else
6972 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6973 tr("Could not open log file '%s' (%Rrc)"),
6974 log.c_str(), vrc);
6975
6976 if (FAILED(rc))
6977 aData.resize(0);
6978
6979 return rc;
6980}
6981
6982
6983/**
6984 * Currently this method doesn't attach device to the running VM,
6985 * just makes sure it's plugged on next VM start.
6986 */
6987HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6988{
6989 // lock scope
6990 {
6991 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6992
6993 HRESULT rc = i_checkStateDependency(MutableStateDep);
6994 if (FAILED(rc)) return rc;
6995
6996 ChipsetType_T aChipset = ChipsetType_PIIX3;
6997 COMGETTER(ChipsetType)(&aChipset);
6998
6999 if (aChipset != ChipsetType_ICH9)
7000 {
7001 return setError(E_INVALIDARG,
7002 tr("Host PCI attachment only supported with ICH9 chipset"));
7003 }
7004
7005 // check if device with this host PCI address already attached
7006 for (HWData::PCIDeviceAssignmentList::const_iterator
7007 it = mHWData->mPCIDeviceAssignments.begin();
7008 it != mHWData->mPCIDeviceAssignments.end();
7009 ++it)
7010 {
7011 LONG iHostAddress = -1;
7012 ComPtr<PCIDeviceAttachment> pAttach;
7013 pAttach = *it;
7014 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7015 if (iHostAddress == aHostAddress)
7016 return setError(E_INVALIDARG,
7017 tr("Device with host PCI address already attached to this VM"));
7018 }
7019
7020 ComObjPtr<PCIDeviceAttachment> pda;
7021 char name[32];
7022
7023 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
7024 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
7025 pda.createObject();
7026 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
7027 i_setModified(IsModified_MachineData);
7028 mHWData.backup();
7029 mHWData->mPCIDeviceAssignments.push_back(pda);
7030 }
7031
7032 return S_OK;
7033}
7034
7035/**
7036 * Currently this method doesn't detach device from the running VM,
7037 * just makes sure it's not plugged on next VM start.
7038 */
7039HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
7040{
7041 ComObjPtr<PCIDeviceAttachment> pAttach;
7042 bool fRemoved = false;
7043 HRESULT rc;
7044
7045 // lock scope
7046 {
7047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7048
7049 rc = i_checkStateDependency(MutableStateDep);
7050 if (FAILED(rc)) return rc;
7051
7052 for (HWData::PCIDeviceAssignmentList::const_iterator
7053 it = mHWData->mPCIDeviceAssignments.begin();
7054 it != mHWData->mPCIDeviceAssignments.end();
7055 ++it)
7056 {
7057 LONG iHostAddress = -1;
7058 pAttach = *it;
7059 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7060 if (iHostAddress != -1 && iHostAddress == aHostAddress)
7061 {
7062 i_setModified(IsModified_MachineData);
7063 mHWData.backup();
7064 mHWData->mPCIDeviceAssignments.remove(pAttach);
7065 fRemoved = true;
7066 break;
7067 }
7068 }
7069 }
7070
7071
7072 /* Fire event outside of the lock */
7073 if (fRemoved)
7074 {
7075 Assert(!pAttach.isNull());
7076 ComPtr<IEventSource> es;
7077 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7078 Assert(SUCCEEDED(rc));
7079 Bstr mid;
7080 rc = this->COMGETTER(Id)(mid.asOutParam());
7081 Assert(SUCCEEDED(rc));
7082 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7083 }
7084
7085 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7086 tr("No host PCI device %08x attached"),
7087 aHostAddress
7088 );
7089}
7090
7091HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
7092{
7093 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7094
7095 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
7096 size_t i = 0;
7097 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
7098 it = mHWData->mPCIDeviceAssignments.begin();
7099 it != mHWData->mPCIDeviceAssignments.end();
7100 ++it, ++i)
7101 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
7102
7103 return S_OK;
7104}
7105
7106HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
7107{
7108 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
7109
7110 return S_OK;
7111}
7112
7113HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
7114{
7115 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7116
7117 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
7118
7119 return S_OK;
7120}
7121
7122HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
7123{
7124 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7125 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7126 if (SUCCEEDED(hrc))
7127 {
7128 hrc = mHWData.backupEx();
7129 if (SUCCEEDED(hrc))
7130 {
7131 i_setModified(IsModified_MachineData);
7132 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
7133 }
7134 }
7135 return hrc;
7136}
7137
7138HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
7139{
7140 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7141 aTracingConfig = mHWData->mDebugging.strTracingConfig;
7142 return S_OK;
7143}
7144
7145HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
7146{
7147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7148 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7149 if (SUCCEEDED(hrc))
7150 {
7151 hrc = mHWData.backupEx();
7152 if (SUCCEEDED(hrc))
7153 {
7154 mHWData->mDebugging.strTracingConfig = aTracingConfig;
7155 if (SUCCEEDED(hrc))
7156 i_setModified(IsModified_MachineData);
7157 }
7158 }
7159 return hrc;
7160}
7161
7162HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
7163{
7164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7165
7166 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
7167
7168 return S_OK;
7169}
7170
7171HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
7172{
7173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7174 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7175 if (SUCCEEDED(hrc))
7176 {
7177 hrc = mHWData.backupEx();
7178 if (SUCCEEDED(hrc))
7179 {
7180 i_setModified(IsModified_MachineData);
7181 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
7182 }
7183 }
7184 return hrc;
7185}
7186
7187HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7188{
7189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7190
7191 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7192
7193 return S_OK;
7194}
7195
7196HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7197{
7198 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7199
7200 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7201 if ( SUCCEEDED(hrc)
7202 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7203 {
7204 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7205 int vrc;
7206
7207 if (aAutostartEnabled)
7208 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7209 else
7210 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7211
7212 if (RT_SUCCESS(vrc))
7213 {
7214 hrc = mHWData.backupEx();
7215 if (SUCCEEDED(hrc))
7216 {
7217 i_setModified(IsModified_MachineData);
7218 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7219 }
7220 }
7221 else if (vrc == VERR_NOT_SUPPORTED)
7222 hrc = setError(VBOX_E_NOT_SUPPORTED,
7223 tr("The VM autostart feature is not supported on this platform"));
7224 else if (vrc == VERR_PATH_NOT_FOUND)
7225 hrc = setError(E_FAIL,
7226 tr("The path to the autostart database is not set"));
7227 else
7228 hrc = setError(E_UNEXPECTED,
7229 aAutostartEnabled ?
7230 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
7231 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
7232 mUserData->s.strName.c_str(), vrc);
7233 }
7234 return hrc;
7235}
7236
7237HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7238{
7239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7240
7241 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7242
7243 return S_OK;
7244}
7245
7246HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7247{
7248 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7249 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7250 if (SUCCEEDED(hrc))
7251 {
7252 hrc = mHWData.backupEx();
7253 if (SUCCEEDED(hrc))
7254 {
7255 i_setModified(IsModified_MachineData);
7256 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7257 }
7258 }
7259 return hrc;
7260}
7261
7262HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7263{
7264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7265
7266 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7267
7268 return S_OK;
7269}
7270
7271HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7272{
7273 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7274 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7275 if ( SUCCEEDED(hrc)
7276 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7277 {
7278 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7279 int vrc;
7280
7281 if (aAutostopType != AutostopType_Disabled)
7282 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7283 else
7284 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7285
7286 if (RT_SUCCESS(vrc))
7287 {
7288 hrc = mHWData.backupEx();
7289 if (SUCCEEDED(hrc))
7290 {
7291 i_setModified(IsModified_MachineData);
7292 mHWData->mAutostart.enmAutostopType = aAutostopType;
7293 }
7294 }
7295 else if (vrc == VERR_NOT_SUPPORTED)
7296 hrc = setError(VBOX_E_NOT_SUPPORTED,
7297 tr("The VM autostop feature is not supported on this platform"));
7298 else if (vrc == VERR_PATH_NOT_FOUND)
7299 hrc = setError(E_FAIL,
7300 tr("The path to the autostart database is not set"));
7301 else
7302 hrc = setError(E_UNEXPECTED,
7303 aAutostopType != AutostopType_Disabled ?
7304 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
7305 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
7306 mUserData->s.strName.c_str(), vrc);
7307 }
7308 return hrc;
7309}
7310
7311HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7312{
7313 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7314
7315 aDefaultFrontend = mHWData->mDefaultFrontend;
7316
7317 return S_OK;
7318}
7319
7320HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7321{
7322 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7323 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7324 if (SUCCEEDED(hrc))
7325 {
7326 hrc = mHWData.backupEx();
7327 if (SUCCEEDED(hrc))
7328 {
7329 i_setModified(IsModified_MachineData);
7330 mHWData->mDefaultFrontend = aDefaultFrontend;
7331 }
7332 }
7333 return hrc;
7334}
7335
7336HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7337{
7338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7339 size_t cbIcon = mUserData->s.ovIcon.size();
7340 aIcon.resize(cbIcon);
7341 if (cbIcon)
7342 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7343 return S_OK;
7344}
7345
7346HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7347{
7348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7349 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7350 if (SUCCEEDED(hrc))
7351 {
7352 i_setModified(IsModified_MachineData);
7353 mUserData.backup();
7354 size_t cbIcon = aIcon.size();
7355 mUserData->s.ovIcon.resize(cbIcon);
7356 if (cbIcon)
7357 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7358 }
7359 return hrc;
7360}
7361
7362HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7363{
7364#ifdef VBOX_WITH_USB
7365 *aUSBProxyAvailable = true;
7366#else
7367 *aUSBProxyAvailable = false;
7368#endif
7369 return S_OK;
7370}
7371
7372HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
7373{
7374 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7375
7376 *aVMProcessPriority = mUserData->s.enmVMPriority;
7377
7378 return S_OK;
7379}
7380
7381HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
7382{
7383 RT_NOREF(aVMProcessPriority);
7384 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7385 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7386 if (SUCCEEDED(hrc))
7387 {
7388 hrc = mUserData.backupEx();
7389 if (SUCCEEDED(hrc))
7390 {
7391 i_setModified(IsModified_MachineData);
7392 mUserData->s.enmVMPriority = aVMProcessPriority;
7393 }
7394 }
7395 alock.release();
7396 if (SUCCEEDED(hrc))
7397 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
7398 return hrc;
7399}
7400
7401HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7402 ComPtr<IProgress> &aProgress)
7403{
7404 ComObjPtr<Progress> pP;
7405 Progress *ppP = pP;
7406 IProgress *iP = static_cast<IProgress *>(ppP);
7407 IProgress **pProgress = &iP;
7408
7409 IMachine *pTarget = aTarget;
7410
7411 /* Convert the options. */
7412 RTCList<CloneOptions_T> optList;
7413 if (aOptions.size())
7414 for (size_t i = 0; i < aOptions.size(); ++i)
7415 optList.append(aOptions[i]);
7416
7417 if (optList.contains(CloneOptions_Link))
7418 {
7419 if (!i_isSnapshotMachine())
7420 return setError(E_INVALIDARG,
7421 tr("Linked clone can only be created from a snapshot"));
7422 if (aMode != CloneMode_MachineState)
7423 return setError(E_INVALIDARG,
7424 tr("Linked clone can only be created for a single machine state"));
7425 }
7426 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7427
7428 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7429
7430 HRESULT rc = pWorker->start(pProgress);
7431
7432 pP = static_cast<Progress *>(*pProgress);
7433 pP.queryInterfaceTo(aProgress.asOutParam());
7434
7435 return rc;
7436
7437}
7438
7439HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7440 const com::Utf8Str &aType,
7441 ComPtr<IProgress> &aProgress)
7442{
7443 LogFlowThisFuncEnter();
7444
7445 ComObjPtr<Progress> ptrProgress;
7446 HRESULT hrc = ptrProgress.createObject();
7447 if (SUCCEEDED(hrc))
7448 {
7449 com::Utf8Str strDefaultPath;
7450 if (aTargetPath.isEmpty())
7451 i_calculateFullPath(".", strDefaultPath);
7452
7453 /* Initialize our worker task */
7454 MachineMoveVM *pTask = NULL;
7455 try
7456 {
7457 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7458 }
7459 catch (std::bad_alloc &)
7460 {
7461 return E_OUTOFMEMORY;
7462 }
7463
7464 hrc = pTask->init();//no exceptions are thrown
7465
7466 if (SUCCEEDED(hrc))
7467 {
7468 hrc = pTask->createThread();
7469 pTask = NULL; /* Consumed by createThread(). */
7470 if (SUCCEEDED(hrc))
7471 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7472 else
7473 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7474 }
7475 else
7476 delete pTask;
7477 }
7478
7479 LogFlowThisFuncLeave();
7480 return hrc;
7481
7482}
7483
7484HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7485{
7486 NOREF(aProgress);
7487 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7488
7489 // This check should always fail.
7490 HRESULT rc = i_checkStateDependency(MutableStateDep);
7491 if (FAILED(rc)) return rc;
7492
7493 AssertFailedReturn(E_NOTIMPL);
7494}
7495
7496HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7497{
7498 NOREF(aSavedStateFile);
7499 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7500
7501 // This check should always fail.
7502 HRESULT rc = i_checkStateDependency(MutableStateDep);
7503 if (FAILED(rc)) return rc;
7504
7505 AssertFailedReturn(E_NOTIMPL);
7506}
7507
7508HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7509{
7510 NOREF(aFRemoveFile);
7511 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7512
7513 // This check should always fail.
7514 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7515 if (FAILED(rc)) return rc;
7516
7517 AssertFailedReturn(E_NOTIMPL);
7518}
7519
7520// public methods for internal purposes
7521/////////////////////////////////////////////////////////////////////////////
7522
7523/**
7524 * Adds the given IsModified_* flag to the dirty flags of the machine.
7525 * This must be called either during i_loadSettings or under the machine write lock.
7526 * @param fl Flag
7527 * @param fAllowStateModification If state modifications are allowed.
7528 */
7529void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7530{
7531 mData->flModifications |= fl;
7532 if (fAllowStateModification && i_isStateModificationAllowed())
7533 mData->mCurrentStateModified = true;
7534}
7535
7536/**
7537 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7538 * care of the write locking.
7539 *
7540 * @param fModification The flag to add.
7541 * @param fAllowStateModification If state modifications are allowed.
7542 */
7543void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7544{
7545 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7546 i_setModified(fModification, fAllowStateModification);
7547}
7548
7549/**
7550 * Saves the registry entry of this machine to the given configuration node.
7551 *
7552 * @param data Machine registry data.
7553 *
7554 * @note locks this object for reading.
7555 */
7556HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7557{
7558 AutoLimitedCaller autoCaller(this);
7559 AssertComRCReturnRC(autoCaller.rc());
7560
7561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7562
7563 data.uuid = mData->mUuid;
7564 data.strSettingsFile = mData->m_strConfigFile;
7565
7566 return S_OK;
7567}
7568
7569/**
7570 * Calculates the absolute path of the given path taking the directory of the
7571 * machine settings file as the current directory.
7572 *
7573 * @param strPath Path to calculate the absolute path for.
7574 * @param aResult Where to put the result (used only on success, can be the
7575 * same Utf8Str instance as passed in @a aPath).
7576 * @return IPRT result.
7577 *
7578 * @note Locks this object for reading.
7579 */
7580int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7581{
7582 AutoCaller autoCaller(this);
7583 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7584
7585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7586
7587 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7588
7589 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7590
7591 strSettingsDir.stripFilename();
7592 char szFolder[RTPATH_MAX];
7593 size_t cbFolder = sizeof(szFolder);
7594 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7595 if (RT_SUCCESS(vrc))
7596 aResult = szFolder;
7597
7598 return vrc;
7599}
7600
7601/**
7602 * Copies strSource to strTarget, making it relative to the machine folder
7603 * if it is a subdirectory thereof, or simply copying it otherwise.
7604 *
7605 * @param strSource Path to evaluate and copy.
7606 * @param strTarget Buffer to receive target path.
7607 *
7608 * @note Locks this object for reading.
7609 */
7610void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7611 Utf8Str &strTarget)
7612{
7613 AutoCaller autoCaller(this);
7614 AssertComRCReturn(autoCaller.rc(), (void)0);
7615
7616 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7617
7618 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7619 // use strTarget as a temporary buffer to hold the machine settings dir
7620 strTarget = mData->m_strConfigFileFull;
7621 strTarget.stripFilename();
7622 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7623 {
7624 // is relative: then append what's left
7625 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7626 // for empty paths (only possible for subdirs) use "." to avoid
7627 // triggering default settings for not present config attributes.
7628 if (strTarget.isEmpty())
7629 strTarget = ".";
7630 }
7631 else
7632 // is not relative: then overwrite
7633 strTarget = strSource;
7634}
7635
7636/**
7637 * Returns the full path to the machine's log folder in the
7638 * \a aLogFolder argument.
7639 */
7640void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7641{
7642 AutoCaller autoCaller(this);
7643 AssertComRCReturnVoid(autoCaller.rc());
7644
7645 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7646
7647 char szTmp[RTPATH_MAX];
7648 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7649 if (RT_SUCCESS(vrc))
7650 {
7651 if (szTmp[0] && !mUserData.isNull())
7652 {
7653 char szTmp2[RTPATH_MAX];
7654 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7655 if (RT_SUCCESS(vrc))
7656 aLogFolder.printf("%s%c%s",
7657 szTmp2,
7658 RTPATH_DELIMITER,
7659 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7660 }
7661 else
7662 vrc = VERR_PATH_IS_RELATIVE;
7663 }
7664
7665 if (RT_FAILURE(vrc))
7666 {
7667 // fallback if VBOX_USER_LOGHOME is not set or invalid
7668 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7669 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7670 aLogFolder.append(RTPATH_DELIMITER);
7671 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7672 }
7673}
7674
7675/**
7676 * Returns the full path to the machine's log file for an given index.
7677 */
7678Utf8Str Machine::i_getLogFilename(ULONG idx)
7679{
7680 Utf8Str logFolder;
7681 getLogFolder(logFolder);
7682 Assert(logFolder.length());
7683
7684 Utf8Str log;
7685 if (idx == 0)
7686 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7687#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7688 else if (idx == 1)
7689 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7690 else
7691 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7692#else
7693 else
7694 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7695#endif
7696 return log;
7697}
7698
7699/**
7700 * Returns the full path to the machine's hardened log file.
7701 */
7702Utf8Str Machine::i_getHardeningLogFilename(void)
7703{
7704 Utf8Str strFilename;
7705 getLogFolder(strFilename);
7706 Assert(strFilename.length());
7707 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7708 return strFilename;
7709}
7710
7711/**
7712 * Returns the default NVRAM filename based on the location of the VM config.
7713 * Note that this is a relative path.
7714 */
7715Utf8Str Machine::i_getDefaultNVRAMFilename()
7716{
7717 AutoCaller autoCaller(this);
7718 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7719
7720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7721
7722 if (i_isSnapshotMachine())
7723 return Utf8Str::Empty;
7724
7725 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7726 strNVRAMFilePath.stripPath();
7727 strNVRAMFilePath.stripSuffix();
7728 strNVRAMFilePath += ".nvram";
7729
7730 return strNVRAMFilePath;
7731}
7732
7733/**
7734 * Returns the NVRAM filename for a new snapshot. This intentionally works
7735 * similarly to the saved state file naming. Note that this is usually
7736 * a relative path, unless the snapshot folder is absolute.
7737 */
7738Utf8Str Machine::i_getSnapshotNVRAMFilename()
7739{
7740 AutoCaller autoCaller(this);
7741 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7742
7743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7744
7745 RTTIMESPEC ts;
7746 RTTimeNow(&ts);
7747 RTTIME time;
7748 RTTimeExplode(&time, &ts);
7749
7750 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7751 strNVRAMFilePath += RTPATH_DELIMITER;
7752 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7753 time.i32Year, time.u8Month, time.u8MonthDay,
7754 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7755
7756 return strNVRAMFilePath;
7757}
7758
7759/**
7760 * Returns the version of the settings file.
7761 */
7762SettingsVersion_T Machine::i_getSettingsVersion(void)
7763{
7764 AutoCaller autoCaller(this);
7765 AssertComRCReturn(autoCaller.rc(), SettingsVersion_Null);
7766
7767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7768
7769 return mData->pMachineConfigFile->getSettingsVersion();
7770}
7771
7772/**
7773 * Composes a unique saved state filename based on the current system time. The filename is
7774 * granular to the second so this will work so long as no more than one snapshot is taken on
7775 * a machine per second.
7776 *
7777 * Before version 4.1, we used this formula for saved state files:
7778 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7779 * which no longer works because saved state files can now be shared between the saved state of the
7780 * "saved" machine and an online snapshot, and the following would cause problems:
7781 * 1) save machine
7782 * 2) create online snapshot from that machine state --> reusing saved state file
7783 * 3) save machine again --> filename would be reused, breaking the online snapshot
7784 *
7785 * So instead we now use a timestamp.
7786 *
7787 * @param strStateFilePath
7788 */
7789
7790void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7791{
7792 AutoCaller autoCaller(this);
7793 AssertComRCReturnVoid(autoCaller.rc());
7794
7795 {
7796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7797 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7798 }
7799
7800 RTTIMESPEC ts;
7801 RTTimeNow(&ts);
7802 RTTIME time;
7803 RTTimeExplode(&time, &ts);
7804
7805 strStateFilePath += RTPATH_DELIMITER;
7806 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7807 time.i32Year, time.u8Month, time.u8MonthDay,
7808 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7809}
7810
7811/**
7812 * Returns whether at least one USB controller is present for the VM.
7813 */
7814bool Machine::i_isUSBControllerPresent()
7815{
7816 AutoCaller autoCaller(this);
7817 AssertComRCReturn(autoCaller.rc(), false);
7818
7819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7820
7821 return (mUSBControllers->size() > 0);
7822}
7823
7824
7825/**
7826 * @note Locks this object for writing, calls the client process
7827 * (inside the lock).
7828 */
7829HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7830 const Utf8Str &strFrontend,
7831 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7832 ProgressProxy *aProgress)
7833{
7834 LogFlowThisFuncEnter();
7835
7836 AssertReturn(aControl, E_FAIL);
7837 AssertReturn(aProgress, E_FAIL);
7838 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7839
7840 AutoCaller autoCaller(this);
7841 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7842
7843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7844
7845 if (!mData->mRegistered)
7846 return setError(E_UNEXPECTED,
7847 tr("The machine '%s' is not registered"),
7848 mUserData->s.strName.c_str());
7849
7850 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7851
7852 /* The process started when launching a VM with separate UI/VM processes is always
7853 * the UI process, i.e. needs special handling as it won't claim the session. */
7854 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7855
7856 if (fSeparate)
7857 {
7858 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7859 return setError(VBOX_E_INVALID_OBJECT_STATE,
7860 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7861 mUserData->s.strName.c_str());
7862 }
7863 else
7864 {
7865 if ( mData->mSession.mState == SessionState_Locked
7866 || mData->mSession.mState == SessionState_Spawning
7867 || mData->mSession.mState == SessionState_Unlocking)
7868 return setError(VBOX_E_INVALID_OBJECT_STATE,
7869 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7870 mUserData->s.strName.c_str());
7871
7872 /* may not be busy */
7873 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7874 }
7875
7876 /* Hardening logging */
7877#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7878 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7879 {
7880 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7881 int vrc2;
7882 /* ignore rc */ i_deleteFile(strHardeningLogFile, false /* fIgnoreFailures */, tr("hardening log file"), &vrc2);
7883 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7884 {
7885 Utf8Str strStartupLogDir = strHardeningLogFile;
7886 strStartupLogDir.stripFilename();
7887 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7888 file without stripping the file. */
7889 }
7890 strSupHardeningLogArg.append(strHardeningLogFile);
7891
7892 /* Remove legacy log filename to avoid confusion. */
7893 Utf8Str strOldStartupLogFile;
7894 getLogFolder(strOldStartupLogFile);
7895 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7896 i_deleteFile(strOldStartupLogFile, true /* fIgnoreFailures */);
7897 }
7898#else
7899 Utf8Str strSupHardeningLogArg;
7900#endif
7901
7902 Utf8Str strAppOverride;
7903#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7904 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7905#endif
7906
7907 bool fUseVBoxSDS = false;
7908 Utf8Str strCanonicalName;
7909 if (false)
7910 { }
7911#ifdef VBOX_WITH_QTGUI
7912 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7913 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7914 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7915 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7916 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7917 {
7918 strCanonicalName = "GUI/Qt";
7919 fUseVBoxSDS = true;
7920 }
7921#endif
7922#ifdef VBOX_WITH_VBOXSDL
7923 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7924 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7925 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7926 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7927 {
7928 strCanonicalName = "GUI/SDL";
7929 fUseVBoxSDS = true;
7930 }
7931#endif
7932#ifdef VBOX_WITH_HEADLESS
7933 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7934 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7935 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7936 {
7937 strCanonicalName = "headless";
7938 }
7939#endif
7940 else
7941 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7942
7943 Utf8Str idStr = mData->mUuid.toString();
7944 Utf8Str const &strMachineName = mUserData->s.strName;
7945 RTPROCESS pid = NIL_RTPROCESS;
7946
7947#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7948 RT_NOREF(fUseVBoxSDS);
7949#else
7950 DWORD idCallerSession = ~(DWORD)0;
7951 if (fUseVBoxSDS)
7952 {
7953 /*
7954 * The VBoxSDS should be used for process launching the VM with
7955 * GUI only if the caller and the VBoxSDS are in different Windows
7956 * sessions and the caller in the interactive one.
7957 */
7958 fUseVBoxSDS = false;
7959
7960 /* Get windows session of the current process. The process token used
7961 due to several reasons:
7962 1. The token is absent for the current thread except someone set it
7963 for us.
7964 2. Needs to get the id of the session where the process is started.
7965 We only need to do this once, though. */
7966 static DWORD s_idCurrentSession = ~(DWORD)0;
7967 DWORD idCurrentSession = s_idCurrentSession;
7968 if (idCurrentSession == ~(DWORD)0)
7969 {
7970 HANDLE hCurrentProcessToken = NULL;
7971 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7972 {
7973 DWORD cbIgn = 0;
7974 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7975 s_idCurrentSession = idCurrentSession;
7976 else
7977 {
7978 idCurrentSession = ~(DWORD)0;
7979 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7980 }
7981 CloseHandle(hCurrentProcessToken);
7982 }
7983 else
7984 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7985 }
7986
7987 /* get the caller's session */
7988 HRESULT hrc = CoImpersonateClient();
7989 if (SUCCEEDED(hrc))
7990 {
7991 HANDLE hCallerThreadToken;
7992 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7993 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7994 &hCallerThreadToken))
7995 {
7996 SetLastError(NO_ERROR);
7997 DWORD cbIgn = 0;
7998 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7999 {
8000 /* Only need to use SDS if the session ID differs: */
8001 if (idCurrentSession != idCallerSession)
8002 {
8003 fUseVBoxSDS = false;
8004
8005 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
8006 DWORD cbTokenGroups = 0;
8007 PTOKEN_GROUPS pTokenGroups = NULL;
8008 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
8009 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
8010 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
8011 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
8012 {
8013 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
8014 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
8015 PSID pInteractiveSid = NULL;
8016 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
8017 {
8018 /* Iterate over the groups looking for the interactive SID: */
8019 fUseVBoxSDS = false;
8020 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
8021 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
8022 {
8023 fUseVBoxSDS = true;
8024 break;
8025 }
8026 FreeSid(pInteractiveSid);
8027 }
8028 }
8029 else
8030 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
8031 RTMemTmpFree(pTokenGroups);
8032 }
8033 }
8034 else
8035 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
8036 CloseHandle(hCallerThreadToken);
8037 }
8038 else
8039 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
8040 CoRevertToSelf();
8041 }
8042 else
8043 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
8044 }
8045 if (fUseVBoxSDS)
8046 {
8047 /* connect to VBoxSDS */
8048 ComPtr<IVirtualBoxSDS> pVBoxSDS;
8049 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
8050 if (FAILED(rc))
8051 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
8052 strMachineName.c_str());
8053
8054 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
8055 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
8056 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
8057 service to access the files. */
8058 rc = CoSetProxyBlanket(pVBoxSDS,
8059 RPC_C_AUTHN_DEFAULT,
8060 RPC_C_AUTHZ_DEFAULT,
8061 COLE_DEFAULT_PRINCIPAL,
8062 RPC_C_AUTHN_LEVEL_DEFAULT,
8063 RPC_C_IMP_LEVEL_IMPERSONATE,
8064 NULL,
8065 EOAC_DEFAULT);
8066 if (FAILED(rc))
8067 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
8068
8069 size_t const cEnvVars = aEnvironmentChanges.size();
8070 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
8071 for (size_t i = 0; i < cEnvVars; i++)
8072 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
8073
8074 ULONG uPid = 0;
8075 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
8076 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
8077 idCallerSession, &uPid);
8078 if (FAILED(rc))
8079 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
8080 pid = (RTPROCESS)uPid;
8081 }
8082 else
8083#endif /* VBOX_WITH_SDS && RT_OS_WINDOWS */
8084 {
8085 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
8086 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
8087 if (RT_FAILURE(vrc))
8088 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
8089 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
8090 }
8091
8092 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
8093 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
8094
8095 if (!fSeparate)
8096 {
8097 /*
8098 * Note that we don't release the lock here before calling the client,
8099 * because it doesn't need to call us back if called with a NULL argument.
8100 * Releasing the lock here is dangerous because we didn't prepare the
8101 * launch data yet, but the client we've just started may happen to be
8102 * too fast and call LockMachine() that will fail (because of PID, etc.),
8103 * so that the Machine will never get out of the Spawning session state.
8104 */
8105
8106 /* inform the session that it will be a remote one */
8107 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8108#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8109 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8110#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8111 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8112#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8113 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8114
8115 if (FAILED(rc))
8116 {
8117 /* restore the session state */
8118 mData->mSession.mState = SessionState_Unlocked;
8119 alock.release();
8120 mParent->i_addProcessToReap(pid);
8121 /* The failure may occur w/o any error info (from RPC), so provide one */
8122 return setError(VBOX_E_VM_ERROR,
8123 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8124 }
8125
8126 /* attach launch data to the machine */
8127 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8128 mData->mSession.mRemoteControls.push_back(aControl);
8129 mData->mSession.mProgress = aProgress;
8130 mData->mSession.mPID = pid;
8131 mData->mSession.mState = SessionState_Spawning;
8132 Assert(strCanonicalName.isNotEmpty());
8133 mData->mSession.mName = strCanonicalName;
8134 }
8135 else
8136 {
8137 /* For separate UI process we declare the launch as completed instantly, as the
8138 * actual headless VM start may or may not come. No point in remembering anything
8139 * yet, as what matters for us is when the headless VM gets started. */
8140 aProgress->i_notifyComplete(S_OK);
8141 }
8142
8143 alock.release();
8144 mParent->i_addProcessToReap(pid);
8145
8146 LogFlowThisFuncLeave();
8147 return S_OK;
8148}
8149
8150/**
8151 * Returns @c true if the given session machine instance has an open direct
8152 * session (and optionally also for direct sessions which are closing) and
8153 * returns the session control machine instance if so.
8154 *
8155 * Note that when the method returns @c false, the arguments remain unchanged.
8156 *
8157 * @param aMachine Session machine object.
8158 * @param aControl Direct session control object (optional).
8159 * @param aRequireVM If true then only allow VM sessions.
8160 * @param aAllowClosing If true then additionally a session which is currently
8161 * being closed will also be allowed.
8162 *
8163 * @note locks this object for reading.
8164 */
8165bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8166 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8167 bool aRequireVM /*= false*/,
8168 bool aAllowClosing /*= false*/)
8169{
8170 AutoLimitedCaller autoCaller(this);
8171 AssertComRCReturn(autoCaller.rc(), false);
8172
8173 /* just return false for inaccessible machines */
8174 if (getObjectState().getState() != ObjectState::Ready)
8175 return false;
8176
8177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8178
8179 if ( ( mData->mSession.mState == SessionState_Locked
8180 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
8181 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8182 )
8183 {
8184 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8185
8186 aMachine = mData->mSession.mMachine;
8187
8188 if (aControl != NULL)
8189 *aControl = mData->mSession.mDirectControl;
8190
8191 return true;
8192 }
8193
8194 return false;
8195}
8196
8197/**
8198 * Returns @c true if the given machine has an spawning direct session.
8199 *
8200 * @note locks this object for reading.
8201 */
8202bool Machine::i_isSessionSpawning()
8203{
8204 AutoLimitedCaller autoCaller(this);
8205 AssertComRCReturn(autoCaller.rc(), false);
8206
8207 /* just return false for inaccessible machines */
8208 if (getObjectState().getState() != ObjectState::Ready)
8209 return false;
8210
8211 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8212
8213 if (mData->mSession.mState == SessionState_Spawning)
8214 return true;
8215
8216 return false;
8217}
8218
8219/**
8220 * Called from the client watcher thread to check for unexpected client process
8221 * death during Session_Spawning state (e.g. before it successfully opened a
8222 * direct session).
8223 *
8224 * On Win32 and on OS/2, this method is called only when we've got the
8225 * direct client's process termination notification, so it always returns @c
8226 * true.
8227 *
8228 * On other platforms, this method returns @c true if the client process is
8229 * terminated and @c false if it's still alive.
8230 *
8231 * @note Locks this object for writing.
8232 */
8233bool Machine::i_checkForSpawnFailure()
8234{
8235 AutoCaller autoCaller(this);
8236 if (!autoCaller.isOk())
8237 {
8238 /* nothing to do */
8239 LogFlowThisFunc(("Already uninitialized!\n"));
8240 return true;
8241 }
8242
8243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8244
8245 if (mData->mSession.mState != SessionState_Spawning)
8246 {
8247 /* nothing to do */
8248 LogFlowThisFunc(("Not spawning any more!\n"));
8249 return true;
8250 }
8251
8252 HRESULT rc = S_OK;
8253
8254 /* PID not yet initialized, skip check. */
8255 if (mData->mSession.mPID == NIL_RTPROCESS)
8256 return false;
8257
8258 RTPROCSTATUS status;
8259 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8260
8261 if (vrc != VERR_PROCESS_RUNNING)
8262 {
8263 Utf8Str strExtraInfo;
8264
8265#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8266 /* If the startup logfile exists and is of non-zero length, tell the
8267 user to look there for more details to encourage them to attach it
8268 when reporting startup issues. */
8269 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8270 uint64_t cbStartupLogFile = 0;
8271 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
8272 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8273 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
8274#endif
8275
8276 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8277 rc = setError(E_FAIL,
8278 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8279 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8280 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8281 rc = setError(E_FAIL,
8282 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8283 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8284 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8285 rc = setError(E_FAIL,
8286 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8287 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8288 else
8289 rc = setErrorBoth(E_FAIL, vrc,
8290 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8291 i_getName().c_str(), vrc, strExtraInfo.c_str());
8292 }
8293
8294 if (FAILED(rc))
8295 {
8296 /* Close the remote session, remove the remote control from the list
8297 * and reset session state to Closed (@note keep the code in sync with
8298 * the relevant part in LockMachine()). */
8299
8300 Assert(mData->mSession.mRemoteControls.size() == 1);
8301 if (mData->mSession.mRemoteControls.size() == 1)
8302 {
8303 ErrorInfoKeeper eik;
8304 mData->mSession.mRemoteControls.front()->Uninitialize();
8305 }
8306
8307 mData->mSession.mRemoteControls.clear();
8308 mData->mSession.mState = SessionState_Unlocked;
8309
8310 /* finalize the progress after setting the state */
8311 if (!mData->mSession.mProgress.isNull())
8312 {
8313 mData->mSession.mProgress->notifyComplete(rc);
8314 mData->mSession.mProgress.setNull();
8315 }
8316
8317 mData->mSession.mPID = NIL_RTPROCESS;
8318
8319 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
8320 return true;
8321 }
8322
8323 return false;
8324}
8325
8326/**
8327 * Checks whether the machine can be registered. If so, commits and saves
8328 * all settings.
8329 *
8330 * @note Must be called from mParent's write lock. Locks this object and
8331 * children for writing.
8332 */
8333HRESULT Machine::i_prepareRegister()
8334{
8335 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8336
8337 AutoLimitedCaller autoCaller(this);
8338 AssertComRCReturnRC(autoCaller.rc());
8339
8340 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8341
8342 /* wait for state dependents to drop to zero */
8343 i_ensureNoStateDependencies(alock);
8344
8345 if (!mData->mAccessible)
8346 return setError(VBOX_E_INVALID_OBJECT_STATE,
8347 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8348 mUserData->s.strName.c_str(),
8349 mData->mUuid.toString().c_str());
8350
8351 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8352
8353 if (mData->mRegistered)
8354 return setError(VBOX_E_INVALID_OBJECT_STATE,
8355 tr("The machine '%s' with UUID {%s} is already registered"),
8356 mUserData->s.strName.c_str(),
8357 mData->mUuid.toString().c_str());
8358
8359 HRESULT rc = S_OK;
8360
8361 // Ensure the settings are saved. If we are going to be registered and
8362 // no config file exists yet, create it by calling i_saveSettings() too.
8363 if ( (mData->flModifications)
8364 || (!mData->pMachineConfigFile->fileExists())
8365 )
8366 {
8367 rc = i_saveSettings(NULL, alock);
8368 // no need to check whether VirtualBox.xml needs saving too since
8369 // we can't have a machine XML file rename pending
8370 if (FAILED(rc)) return rc;
8371 }
8372
8373 /* more config checking goes here */
8374
8375 if (SUCCEEDED(rc))
8376 {
8377 /* we may have had implicit modifications we want to fix on success */
8378 i_commit();
8379
8380 mData->mRegistered = true;
8381 }
8382 else
8383 {
8384 /* we may have had implicit modifications we want to cancel on failure*/
8385 i_rollback(false /* aNotify */);
8386 }
8387
8388 return rc;
8389}
8390
8391/**
8392 * Increases the number of objects dependent on the machine state or on the
8393 * registered state. Guarantees that these two states will not change at least
8394 * until #i_releaseStateDependency() is called.
8395 *
8396 * Depending on the @a aDepType value, additional state checks may be made.
8397 * These checks will set extended error info on failure. See
8398 * #i_checkStateDependency() for more info.
8399 *
8400 * If this method returns a failure, the dependency is not added and the caller
8401 * is not allowed to rely on any particular machine state or registration state
8402 * value and may return the failed result code to the upper level.
8403 *
8404 * @param aDepType Dependency type to add.
8405 * @param aState Current machine state (NULL if not interested).
8406 * @param aRegistered Current registered state (NULL if not interested).
8407 *
8408 * @note Locks this object for writing.
8409 */
8410HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8411 MachineState_T *aState /* = NULL */,
8412 BOOL *aRegistered /* = NULL */)
8413{
8414 AutoCaller autoCaller(this);
8415 AssertComRCReturnRC(autoCaller.rc());
8416
8417 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8418
8419 HRESULT rc = i_checkStateDependency(aDepType);
8420 if (FAILED(rc)) return rc;
8421
8422 {
8423 if (mData->mMachineStateChangePending != 0)
8424 {
8425 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8426 * drop to zero so don't add more. It may make sense to wait a bit
8427 * and retry before reporting an error (since the pending state
8428 * transition should be really quick) but let's just assert for
8429 * now to see if it ever happens on practice. */
8430
8431 AssertFailed();
8432
8433 return setError(E_ACCESSDENIED,
8434 tr("Machine state change is in progress. Please retry the operation later."));
8435 }
8436
8437 ++mData->mMachineStateDeps;
8438 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8439 }
8440
8441 if (aState)
8442 *aState = mData->mMachineState;
8443 if (aRegistered)
8444 *aRegistered = mData->mRegistered;
8445
8446 return S_OK;
8447}
8448
8449/**
8450 * Decreases the number of objects dependent on the machine state.
8451 * Must always complete the #i_addStateDependency() call after the state
8452 * dependency is no more necessary.
8453 */
8454void Machine::i_releaseStateDependency()
8455{
8456 AutoCaller autoCaller(this);
8457 AssertComRCReturnVoid(autoCaller.rc());
8458
8459 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8460
8461 /* releaseStateDependency() w/o addStateDependency()? */
8462 AssertReturnVoid(mData->mMachineStateDeps != 0);
8463 -- mData->mMachineStateDeps;
8464
8465 if (mData->mMachineStateDeps == 0)
8466 {
8467 /* inform i_ensureNoStateDependencies() that there are no more deps */
8468 if (mData->mMachineStateChangePending != 0)
8469 {
8470 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8471 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8472 }
8473 }
8474}
8475
8476Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8477{
8478 /* start with nothing found */
8479 Utf8Str strResult("");
8480
8481 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8482
8483 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8484 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8485 // found:
8486 strResult = it->second; // source is a Utf8Str
8487
8488 return strResult;
8489}
8490
8491// protected methods
8492/////////////////////////////////////////////////////////////////////////////
8493
8494/**
8495 * Performs machine state checks based on the @a aDepType value. If a check
8496 * fails, this method will set extended error info, otherwise it will return
8497 * S_OK. It is supposed, that on failure, the caller will immediately return
8498 * the return value of this method to the upper level.
8499 *
8500 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8501 *
8502 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8503 * current state of this machine object allows to change settings of the
8504 * machine (i.e. the machine is not registered, or registered but not running
8505 * and not saved). It is useful to call this method from Machine setters
8506 * before performing any change.
8507 *
8508 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8509 * as for MutableStateDep except that if the machine is saved, S_OK is also
8510 * returned. This is useful in setters which allow changing machine
8511 * properties when it is in the saved state.
8512 *
8513 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8514 * if the current state of this machine object allows to change runtime
8515 * changeable settings of the machine (i.e. the machine is not registered, or
8516 * registered but either running or not running and not saved). It is useful
8517 * to call this method from Machine setters before performing any changes to
8518 * runtime changeable settings.
8519 *
8520 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8521 * the same as for MutableOrRunningStateDep except that if the machine is
8522 * saved, S_OK is also returned. This is useful in setters which allow
8523 * changing runtime and saved state changeable machine properties.
8524 *
8525 * @param aDepType Dependency type to check.
8526 *
8527 * @note Non Machine based classes should use #i_addStateDependency() and
8528 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8529 * template.
8530 *
8531 * @note This method must be called from under this object's read or write
8532 * lock.
8533 */
8534HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8535{
8536 switch (aDepType)
8537 {
8538 case AnyStateDep:
8539 {
8540 break;
8541 }
8542 case MutableStateDep:
8543 {
8544 if ( mData->mRegistered
8545 && ( !i_isSessionMachine()
8546 || ( mData->mMachineState != MachineState_Aborted
8547 && mData->mMachineState != MachineState_Teleported
8548 && mData->mMachineState != MachineState_PoweredOff
8549 )
8550 )
8551 )
8552 return setError(VBOX_E_INVALID_VM_STATE,
8553 tr("The machine is not mutable (state is %s)"),
8554 Global::stringifyMachineState(mData->mMachineState));
8555 break;
8556 }
8557 case MutableOrSavedStateDep:
8558 {
8559 if ( mData->mRegistered
8560 && ( !i_isSessionMachine()
8561 || ( mData->mMachineState != MachineState_Aborted
8562 && mData->mMachineState != MachineState_Teleported
8563 && mData->mMachineState != MachineState_Saved
8564 && mData->mMachineState != MachineState_AbortedSaved
8565 && mData->mMachineState != MachineState_PoweredOff
8566 )
8567 )
8568 )
8569 return setError(VBOX_E_INVALID_VM_STATE,
8570 tr("The machine is not mutable or saved (state is %s)"),
8571 Global::stringifyMachineState(mData->mMachineState));
8572 break;
8573 }
8574 case MutableOrRunningStateDep:
8575 {
8576 if ( mData->mRegistered
8577 && ( !i_isSessionMachine()
8578 || ( mData->mMachineState != MachineState_Aborted
8579 && mData->mMachineState != MachineState_Teleported
8580 && mData->mMachineState != MachineState_PoweredOff
8581 && !Global::IsOnline(mData->mMachineState)
8582 )
8583 )
8584 )
8585 return setError(VBOX_E_INVALID_VM_STATE,
8586 tr("The machine is not mutable or running (state is %s)"),
8587 Global::stringifyMachineState(mData->mMachineState));
8588 break;
8589 }
8590 case MutableOrSavedOrRunningStateDep:
8591 {
8592 if ( mData->mRegistered
8593 && ( !i_isSessionMachine()
8594 || ( mData->mMachineState != MachineState_Aborted
8595 && mData->mMachineState != MachineState_Teleported
8596 && mData->mMachineState != MachineState_Saved
8597 && mData->mMachineState != MachineState_AbortedSaved
8598 && mData->mMachineState != MachineState_PoweredOff
8599 && !Global::IsOnline(mData->mMachineState)
8600 )
8601 )
8602 )
8603 return setError(VBOX_E_INVALID_VM_STATE,
8604 tr("The machine is not mutable, saved or running (state is %s)"),
8605 Global::stringifyMachineState(mData->mMachineState));
8606 break;
8607 }
8608 }
8609
8610 return S_OK;
8611}
8612
8613/**
8614 * Helper to initialize all associated child objects and allocate data
8615 * structures.
8616 *
8617 * This method must be called as a part of the object's initialization procedure
8618 * (usually done in the #init() method).
8619 *
8620 * @note Must be called only from #init() or from #i_registeredInit().
8621 */
8622HRESULT Machine::initDataAndChildObjects()
8623{
8624 AutoCaller autoCaller(this);
8625 AssertComRCReturnRC(autoCaller.rc());
8626 AssertReturn( getObjectState().getState() == ObjectState::InInit
8627 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8628
8629 AssertReturn(!mData->mAccessible, E_FAIL);
8630
8631 /* allocate data structures */
8632 mSSData.allocate();
8633 mUserData.allocate();
8634 mHWData.allocate();
8635 mMediumAttachments.allocate();
8636 mStorageControllers.allocate();
8637 mUSBControllers.allocate();
8638
8639 /* initialize mOSTypeId */
8640 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8641
8642/** @todo r=bird: init() methods never fails, right? Why don't we make them
8643 * return void then! */
8644
8645 /* create associated BIOS settings object */
8646 unconst(mBIOSSettings).createObject();
8647 mBIOSSettings->init(this);
8648
8649 /* create associated recording settings object */
8650 unconst(mRecordingSettings).createObject();
8651 mRecordingSettings->init(this);
8652
8653 /* create associated trusted platform module object */
8654 unconst(mTrustedPlatformModule).createObject();
8655 mTrustedPlatformModule->init(this);
8656
8657 /* create associated NVRAM store object */
8658 unconst(mNvramStore).createObject();
8659 mNvramStore->init(this);
8660
8661 /* create the graphics adapter object (always present) */
8662 unconst(mGraphicsAdapter).createObject();
8663 mGraphicsAdapter->init(this);
8664
8665 /* create an associated VRDE object (default is disabled) */
8666 unconst(mVRDEServer).createObject();
8667 mVRDEServer->init(this);
8668
8669 /* create associated serial port objects */
8670 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8671 {
8672 unconst(mSerialPorts[slot]).createObject();
8673 mSerialPorts[slot]->init(this, slot);
8674 }
8675
8676 /* create associated parallel port objects */
8677 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8678 {
8679 unconst(mParallelPorts[slot]).createObject();
8680 mParallelPorts[slot]->init(this, slot);
8681 }
8682
8683 /* create the audio settings object */
8684 unconst(mAudioSettings).createObject();
8685 mAudioSettings->init(this);
8686
8687 /* create the USB device filters object (always present) */
8688 unconst(mUSBDeviceFilters).createObject();
8689 mUSBDeviceFilters->init(this);
8690
8691 /* create associated network adapter objects */
8692 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8693 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8694 {
8695 unconst(mNetworkAdapters[slot]).createObject();
8696 mNetworkAdapters[slot]->init(this, slot);
8697 }
8698
8699 /* create the bandwidth control */
8700 unconst(mBandwidthControl).createObject();
8701 mBandwidthControl->init(this);
8702
8703 /* create the guest debug control object */
8704 unconst(mGuestDebugControl).createObject();
8705 mGuestDebugControl->init(this);
8706
8707 return S_OK;
8708}
8709
8710/**
8711 * Helper to uninitialize all associated child objects and to free all data
8712 * structures.
8713 *
8714 * This method must be called as a part of the object's uninitialization
8715 * procedure (usually done in the #uninit() method).
8716 *
8717 * @note Must be called only from #uninit() or from #i_registeredInit().
8718 */
8719void Machine::uninitDataAndChildObjects()
8720{
8721 AutoCaller autoCaller(this);
8722 AssertComRCReturnVoid(autoCaller.rc());
8723 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8724 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8725 || getObjectState().getState() == ObjectState::InUninit
8726 || getObjectState().getState() == ObjectState::Limited);
8727
8728 /* tell all our other child objects we've been uninitialized */
8729 if (mGuestDebugControl)
8730 {
8731 mGuestDebugControl->uninit();
8732 unconst(mGuestDebugControl).setNull();
8733 }
8734
8735 if (mBandwidthControl)
8736 {
8737 mBandwidthControl->uninit();
8738 unconst(mBandwidthControl).setNull();
8739 }
8740
8741 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8742 {
8743 if (mNetworkAdapters[slot])
8744 {
8745 mNetworkAdapters[slot]->uninit();
8746 unconst(mNetworkAdapters[slot]).setNull();
8747 }
8748 }
8749
8750 if (mUSBDeviceFilters)
8751 {
8752 mUSBDeviceFilters->uninit();
8753 unconst(mUSBDeviceFilters).setNull();
8754 }
8755
8756 if (mAudioSettings)
8757 {
8758 mAudioSettings->uninit();
8759 unconst(mAudioSettings).setNull();
8760 }
8761
8762 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8763 {
8764 if (mParallelPorts[slot])
8765 {
8766 mParallelPorts[slot]->uninit();
8767 unconst(mParallelPorts[slot]).setNull();
8768 }
8769 }
8770
8771 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8772 {
8773 if (mSerialPorts[slot])
8774 {
8775 mSerialPorts[slot]->uninit();
8776 unconst(mSerialPorts[slot]).setNull();
8777 }
8778 }
8779
8780 if (mVRDEServer)
8781 {
8782 mVRDEServer->uninit();
8783 unconst(mVRDEServer).setNull();
8784 }
8785
8786 if (mGraphicsAdapter)
8787 {
8788 mGraphicsAdapter->uninit();
8789 unconst(mGraphicsAdapter).setNull();
8790 }
8791
8792 if (mBIOSSettings)
8793 {
8794 mBIOSSettings->uninit();
8795 unconst(mBIOSSettings).setNull();
8796 }
8797
8798 if (mRecordingSettings)
8799 {
8800 mRecordingSettings->uninit();
8801 unconst(mRecordingSettings).setNull();
8802 }
8803
8804 if (mTrustedPlatformModule)
8805 {
8806 mTrustedPlatformModule->uninit();
8807 unconst(mTrustedPlatformModule).setNull();
8808 }
8809
8810 if (mNvramStore)
8811 {
8812 mNvramStore->uninit();
8813 unconst(mNvramStore).setNull();
8814 }
8815
8816 /* Deassociate media (only when a real Machine or a SnapshotMachine
8817 * instance is uninitialized; SessionMachine instances refer to real
8818 * Machine media). This is necessary for a clean re-initialization of
8819 * the VM after successfully re-checking the accessibility state. Note
8820 * that in case of normal Machine or SnapshotMachine uninitialization (as
8821 * a result of unregistering or deleting the snapshot), outdated media
8822 * attachments will already be uninitialized and deleted, so this
8823 * code will not affect them. */
8824 if ( !mMediumAttachments.isNull()
8825 && !i_isSessionMachine()
8826 )
8827 {
8828 for (MediumAttachmentList::const_iterator
8829 it = mMediumAttachments->begin();
8830 it != mMediumAttachments->end();
8831 ++it)
8832 {
8833 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8834 if (pMedium.isNull())
8835 continue;
8836 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8837 AssertComRC(rc);
8838 }
8839 }
8840
8841 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8842 {
8843 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children)
8844 if (mData->mFirstSnapshot)
8845 {
8846 // Snapshots tree is protected by machine write lock.
8847 // Otherwise we assert in Snapshot::uninit()
8848 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8849 mData->mFirstSnapshot->uninit();
8850 mData->mFirstSnapshot.setNull();
8851 }
8852
8853 mData->mCurrentSnapshot.setNull();
8854 }
8855
8856 /* free data structures (the essential mData structure is not freed here
8857 * since it may be still in use) */
8858 mMediumAttachments.free();
8859 mStorageControllers.free();
8860 mUSBControllers.free();
8861 mHWData.free();
8862 mUserData.free();
8863 mSSData.free();
8864}
8865
8866/**
8867 * Returns a pointer to the Machine object for this machine that acts like a
8868 * parent for complex machine data objects such as shared folders, etc.
8869 *
8870 * For primary Machine objects and for SnapshotMachine objects, returns this
8871 * object's pointer itself. For SessionMachine objects, returns the peer
8872 * (primary) machine pointer.
8873 */
8874Machine *Machine::i_getMachine()
8875{
8876 if (i_isSessionMachine())
8877 return (Machine*)mPeer;
8878 return this;
8879}
8880
8881/**
8882 * Makes sure that there are no machine state dependents. If necessary, waits
8883 * for the number of dependents to drop to zero.
8884 *
8885 * Make sure this method is called from under this object's write lock to
8886 * guarantee that no new dependents may be added when this method returns
8887 * control to the caller.
8888 *
8889 * @note Receives a lock to this object for writing. The lock will be released
8890 * while waiting (if necessary).
8891 *
8892 * @warning To be used only in methods that change the machine state!
8893 */
8894void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8895{
8896 AssertReturnVoid(isWriteLockOnCurrentThread());
8897
8898 /* Wait for all state dependents if necessary */
8899 if (mData->mMachineStateDeps != 0)
8900 {
8901 /* lazy semaphore creation */
8902 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8903 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8904
8905 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8906 mData->mMachineStateDeps));
8907
8908 ++mData->mMachineStateChangePending;
8909
8910 /* reset the semaphore before waiting, the last dependent will signal
8911 * it */
8912 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8913
8914 alock.release();
8915
8916 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8917
8918 alock.acquire();
8919
8920 -- mData->mMachineStateChangePending;
8921 }
8922}
8923
8924/**
8925 * Changes the machine state and informs callbacks.
8926 *
8927 * This method is not intended to fail so it either returns S_OK or asserts (and
8928 * returns a failure).
8929 *
8930 * @note Locks this object for writing.
8931 */
8932HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8933{
8934 LogFlowThisFuncEnter();
8935 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8936 Assert(aMachineState != MachineState_Null);
8937
8938 AutoCaller autoCaller(this);
8939 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8940
8941 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8942
8943 /* wait for state dependents to drop to zero */
8944 i_ensureNoStateDependencies(alock);
8945
8946 MachineState_T const enmOldState = mData->mMachineState;
8947 if (enmOldState != aMachineState)
8948 {
8949 mData->mMachineState = aMachineState;
8950 RTTimeNow(&mData->mLastStateChange);
8951
8952#ifdef VBOX_WITH_DTRACE_R3_MAIN
8953 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8954#endif
8955 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8956 }
8957
8958 LogFlowThisFuncLeave();
8959 return S_OK;
8960}
8961
8962/**
8963 * Searches for a shared folder with the given logical name
8964 * in the collection of shared folders.
8965 *
8966 * @param aName logical name of the shared folder
8967 * @param aSharedFolder where to return the found object
8968 * @param aSetError whether to set the error info if the folder is
8969 * not found
8970 * @return
8971 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8972 *
8973 * @note
8974 * must be called from under the object's lock!
8975 */
8976HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8977 ComObjPtr<SharedFolder> &aSharedFolder,
8978 bool aSetError /* = false */)
8979{
8980 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8981 for (HWData::SharedFolderList::const_iterator
8982 it = mHWData->mSharedFolders.begin();
8983 it != mHWData->mSharedFolders.end();
8984 ++it)
8985 {
8986 SharedFolder *pSF = *it;
8987 AutoCaller autoCaller(pSF);
8988 if (pSF->i_getName() == aName)
8989 {
8990 aSharedFolder = pSF;
8991 rc = S_OK;
8992 break;
8993 }
8994 }
8995
8996 if (aSetError && FAILED(rc))
8997 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8998
8999 return rc;
9000}
9001
9002/**
9003 * Initializes all machine instance data from the given settings structures
9004 * from XML. The exception is the machine UUID which needs special handling
9005 * depending on the caller's use case, so the caller needs to set that herself.
9006 *
9007 * This gets called in several contexts during machine initialization:
9008 *
9009 * -- When machine XML exists on disk already and needs to be loaded into memory,
9010 * for example, from #i_registeredInit() to load all registered machines on
9011 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
9012 * attached to the machine should be part of some media registry already.
9013 *
9014 * -- During OVF import, when a machine config has been constructed from an
9015 * OVF file. In this case, puuidRegistry is set to the machine UUID to
9016 * ensure that the media listed as attachments in the config (which have
9017 * been imported from the OVF) receive the correct registry ID.
9018 *
9019 * -- During VM cloning.
9020 *
9021 * @param config Machine settings from XML.
9022 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
9023 * for each attached medium in the config.
9024 * @return
9025 */
9026HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
9027 const Guid *puuidRegistry)
9028{
9029 // copy name, description, OS type, teleporter, UTC etc.
9030 mUserData->s = config.machineUserData;
9031
9032 // look up the object by Id to check it is valid
9033 ComObjPtr<GuestOSType> pGuestOSType;
9034 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9035 if (!pGuestOSType.isNull())
9036 mUserData->s.strOsType = pGuestOSType->i_id();
9037
9038#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9039 // stateFile encryption (optional)
9040 mSSData->strStateKeyId = config.strStateKeyId;
9041 mSSData->strStateKeyStore = config.strStateKeyStore;
9042 mData->mstrLogKeyId = config.strLogKeyId;
9043 mData->mstrLogKeyStore = config.strLogKeyStore;
9044#endif
9045
9046 // stateFile (optional)
9047 if (config.strStateFile.isEmpty())
9048 mSSData->strStateFilePath.setNull();
9049 else
9050 {
9051 Utf8Str stateFilePathFull(config.strStateFile);
9052 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
9053 if (RT_FAILURE(vrc))
9054 return setErrorBoth(E_FAIL, vrc,
9055 tr("Invalid saved state file path '%s' (%Rrc)"),
9056 config.strStateFile.c_str(),
9057 vrc);
9058 mSSData->strStateFilePath = stateFilePathFull;
9059 }
9060
9061 // snapshot folder needs special processing so set it again
9062 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
9063 if (FAILED(rc)) return rc;
9064
9065 /* Copy the extra data items (config may or may not be the same as
9066 * mData->pMachineConfigFile) if necessary. When loading the XML files
9067 * from disk they are the same, but not for OVF import. */
9068 if (mData->pMachineConfigFile != &config)
9069 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
9070
9071 /* currentStateModified (optional, default is true) */
9072 mData->mCurrentStateModified = config.fCurrentStateModified;
9073
9074 mData->mLastStateChange = config.timeLastStateChange;
9075
9076 /*
9077 * note: all mUserData members must be assigned prior this point because
9078 * we need to commit changes in order to let mUserData be shared by all
9079 * snapshot machine instances.
9080 */
9081 mUserData.commitCopy();
9082
9083 // machine registry, if present (must be loaded before snapshots)
9084 if (config.canHaveOwnMediaRegistry())
9085 {
9086 // determine machine folder
9087 Utf8Str strMachineFolder = i_getSettingsFileFull();
9088 strMachineFolder.stripFilename();
9089 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
9090 config.mediaRegistry,
9091 strMachineFolder);
9092 if (FAILED(rc)) return rc;
9093 }
9094
9095 /* Snapshot node (optional) */
9096 size_t cRootSnapshots;
9097 if ((cRootSnapshots = config.llFirstSnapshot.size()))
9098 {
9099 // there must be only one root snapshot
9100 Assert(cRootSnapshots == 1);
9101 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9102
9103 rc = i_loadSnapshot(snap,
9104 config.uuidCurrentSnapshot);
9105 if (FAILED(rc)) return rc;
9106 }
9107
9108 // hardware data
9109 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart,
9110 config.recordingSettings);
9111 if (FAILED(rc)) return rc;
9112
9113 /*
9114 * NOTE: the assignment below must be the last thing to do,
9115 * otherwise it will be not possible to change the settings
9116 * somewhere in the code above because all setters will be
9117 * blocked by i_checkStateDependency(MutableStateDep).
9118 */
9119
9120 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
9121 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
9122 {
9123 /* no need to use i_setMachineState() during init() */
9124 mData->mMachineState = MachineState_AbortedSaved;
9125 }
9126 else if (config.fAborted)
9127 {
9128 mSSData->strStateFilePath.setNull();
9129
9130 /* no need to use i_setMachineState() during init() */
9131 mData->mMachineState = MachineState_Aborted;
9132 }
9133 else if (!mSSData->strStateFilePath.isEmpty())
9134 {
9135 /* no need to use i_setMachineState() during init() */
9136 mData->mMachineState = MachineState_Saved;
9137 }
9138
9139 // after loading settings, we are no longer different from the XML on disk
9140 mData->flModifications = 0;
9141
9142 return S_OK;
9143}
9144
9145/**
9146 * Loads all snapshots starting from the given settings.
9147 *
9148 * @param data snapshot settings.
9149 * @param aCurSnapshotId Current snapshot ID from the settings file.
9150 */
9151HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
9152 const Guid &aCurSnapshotId)
9153{
9154 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
9155 AssertReturn(!i_isSessionMachine(), E_FAIL);
9156
9157 HRESULT rc = S_OK;
9158
9159 std::list<const settings::Snapshot *> llSettingsTodo;
9160 llSettingsTodo.push_back(&data);
9161 std::list<Snapshot *> llParentsTodo;
9162 llParentsTodo.push_back(NULL);
9163
9164 while (llSettingsTodo.size() > 0)
9165 {
9166 const settings::Snapshot *current = llSettingsTodo.front();
9167 llSettingsTodo.pop_front();
9168 Snapshot *pParent = llParentsTodo.front();
9169 llParentsTodo.pop_front();
9170
9171 Utf8Str strStateFile;
9172 if (!current->strStateFile.isEmpty())
9173 {
9174 /* optional */
9175 strStateFile = current->strStateFile;
9176 int vrc = i_calculateFullPath(strStateFile, strStateFile);
9177 if (RT_FAILURE(vrc))
9178 {
9179 setErrorBoth(E_FAIL, vrc,
9180 tr("Invalid saved state file path '%s' (%Rrc)"),
9181 strStateFile.c_str(), vrc);
9182 }
9183 }
9184
9185 /* create a snapshot machine object */
9186 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9187 pSnapshotMachine.createObject();
9188 rc = pSnapshotMachine->initFromSettings(this,
9189 current->hardware,
9190 &current->debugging,
9191 &current->autostart,
9192 current->recordingSettings,
9193 current->uuid.ref(),
9194 strStateFile);
9195 if (FAILED(rc)) break;
9196
9197 /* create a snapshot object */
9198 ComObjPtr<Snapshot> pSnapshot;
9199 pSnapshot.createObject();
9200 /* initialize the snapshot */
9201 rc = pSnapshot->init(mParent, // VirtualBox object
9202 current->uuid,
9203 current->strName,
9204 current->strDescription,
9205 current->timestamp,
9206 pSnapshotMachine,
9207 pParent);
9208 if (FAILED(rc)) break;
9209
9210 /* memorize the first snapshot if necessary */
9211 if (!mData->mFirstSnapshot)
9212 {
9213 Assert(pParent == NULL);
9214 mData->mFirstSnapshot = pSnapshot;
9215 }
9216
9217 /* memorize the current snapshot when appropriate */
9218 if ( !mData->mCurrentSnapshot
9219 && pSnapshot->i_getId() == aCurSnapshotId
9220 )
9221 mData->mCurrentSnapshot = pSnapshot;
9222
9223 /* create all children */
9224 std::list<settings::Snapshot>::const_iterator itBegin = current->llChildSnapshots.begin();
9225 std::list<settings::Snapshot>::const_iterator itEnd = current->llChildSnapshots.end();
9226 for (std::list<settings::Snapshot>::const_iterator it = itBegin; it != itEnd; ++it)
9227 {
9228 llSettingsTodo.push_back(&*it);
9229 llParentsTodo.push_back(pSnapshot);
9230 }
9231 }
9232
9233 return rc;
9234}
9235
9236/**
9237 * Loads settings into mHWData.
9238 *
9239 * @param puuidRegistry Registry ID.
9240 * @param puuidSnapshot Snapshot ID
9241 * @param data Reference to the hardware settings.
9242 * @param pDbg Pointer to the debugging settings.
9243 * @param pAutostart Pointer to the autostart settings
9244 * @param recording Reference to recording settings.
9245 */
9246HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
9247 const Guid *puuidSnapshot,
9248 const settings::Hardware &data,
9249 const settings::Debugging *pDbg,
9250 const settings::Autostart *pAutostart,
9251 const settings::RecordingSettings &recording)
9252{
9253 AssertReturn(!i_isSessionMachine(), E_FAIL);
9254
9255 HRESULT rc = S_OK;
9256
9257 try
9258 {
9259 ComObjPtr<GuestOSType> pGuestOSType;
9260 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9261
9262 /* The hardware version attribute (optional). */
9263 mHWData->mHWVersion = data.strVersion;
9264 mHWData->mHardwareUUID = data.uuid;
9265
9266 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9267 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9268 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9269 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9270 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9271 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9272 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
9273 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
9274 mHWData->mPAEEnabled = data.fPAE;
9275 mHWData->mLongMode = data.enmLongMode;
9276 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9277 mHWData->mAPIC = data.fAPIC;
9278 mHWData->mX2APIC = data.fX2APIC;
9279 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9280 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9281 mHWData->mSpecCtrl = data.fSpecCtrl;
9282 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
9283 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
9284 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
9285 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
9286 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
9287 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9288 mHWData->mCPUCount = data.cCPUs;
9289 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9290 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9291 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9292 mHWData->mCpuProfile = data.strCpuProfile;
9293
9294 // cpu
9295 if (mHWData->mCPUHotPlugEnabled)
9296 {
9297 for (settings::CpuList::const_iterator
9298 it = data.llCpus.begin();
9299 it != data.llCpus.end();
9300 ++it)
9301 {
9302 const settings::Cpu &cpu = *it;
9303
9304 mHWData->mCPUAttached[cpu.ulId] = true;
9305 }
9306 }
9307
9308 // cpuid leafs
9309 for (settings::CpuIdLeafsList::const_iterator
9310 it = data.llCpuIdLeafs.begin();
9311 it != data.llCpuIdLeafs.end();
9312 ++it)
9313 {
9314 const settings::CpuIdLeaf &rLeaf= *it;
9315 if ( rLeaf.idx < UINT32_C(0x20)
9316 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9317 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9318 mHWData->mCpuIdLeafList.push_back(rLeaf);
9319 /* else: just ignore */
9320 }
9321
9322 mHWData->mMemorySize = data.ulMemorySizeMB;
9323 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9324
9325 // boot order
9326 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9327 {
9328 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9329 if (it == data.mapBootOrder.end())
9330 mHWData->mBootOrder[i] = DeviceType_Null;
9331 else
9332 mHWData->mBootOrder[i] = it->second;
9333 }
9334
9335 mHWData->mFirmwareType = data.firmwareType;
9336 mHWData->mPointingHIDType = data.pointingHIDType;
9337 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9338 mHWData->mChipsetType = data.chipsetType;
9339 mHWData->mIommuType = data.iommuType;
9340 mHWData->mParavirtProvider = data.paravirtProvider;
9341 mHWData->mParavirtDebug = data.strParavirtDebug;
9342 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9343 mHWData->mHPETEnabled = data.fHPETEnabled;
9344
9345 /* GraphicsAdapter */
9346 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
9347 if (FAILED(rc)) return rc;
9348
9349 /* VRDEServer */
9350 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9351 if (FAILED(rc)) return rc;
9352
9353 /* BIOS */
9354 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9355 if (FAILED(rc)) return rc;
9356
9357 /* Recording */
9358 rc = mRecordingSettings->i_loadSettings(recording);
9359 if (FAILED(rc)) return rc;
9360
9361 /* Trusted Platform Module */
9362 rc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
9363 if (FAILED(rc)) return rc;
9364
9365 rc = mNvramStore->i_loadSettings(data.nvramSettings);
9366 if (FAILED(rc)) return rc;
9367
9368 // Bandwidth control (must come before network adapters)
9369 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9370 if (FAILED(rc)) return rc;
9371
9372 /* USB controllers */
9373 for (settings::USBControllerList::const_iterator
9374 it = data.usbSettings.llUSBControllers.begin();
9375 it != data.usbSettings.llUSBControllers.end();
9376 ++it)
9377 {
9378 const settings::USBController &settingsCtrl = *it;
9379 ComObjPtr<USBController> newCtrl;
9380
9381 newCtrl.createObject();
9382 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9383 mUSBControllers->push_back(newCtrl);
9384 }
9385
9386 /* USB device filters */
9387 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9388 if (FAILED(rc)) return rc;
9389
9390 // network adapters (establish array size first and apply defaults, to
9391 // ensure reading the same settings as we saved, since the list skips
9392 // adapters having defaults)
9393 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9394 size_t oldCount = mNetworkAdapters.size();
9395 if (newCount > oldCount)
9396 {
9397 mNetworkAdapters.resize(newCount);
9398 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9399 {
9400 unconst(mNetworkAdapters[slot]).createObject();
9401 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9402 }
9403 }
9404 else if (newCount < oldCount)
9405 mNetworkAdapters.resize(newCount);
9406 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9407 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9408 for (settings::NetworkAdaptersList::const_iterator
9409 it = data.llNetworkAdapters.begin();
9410 it != data.llNetworkAdapters.end();
9411 ++it)
9412 {
9413 const settings::NetworkAdapter &nic = *it;
9414
9415 /* slot uniqueness is guaranteed by XML Schema */
9416 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9417 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9418 if (FAILED(rc)) return rc;
9419 }
9420
9421 // serial ports (establish defaults first, to ensure reading the same
9422 // settings as we saved, since the list skips ports having defaults)
9423 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9424 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9425 for (settings::SerialPortsList::const_iterator
9426 it = data.llSerialPorts.begin();
9427 it != data.llSerialPorts.end();
9428 ++it)
9429 {
9430 const settings::SerialPort &s = *it;
9431
9432 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9433 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9434 if (FAILED(rc)) return rc;
9435 }
9436
9437 // parallel ports (establish defaults first, to ensure reading the same
9438 // settings as we saved, since the list skips ports having defaults)
9439 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9440 mParallelPorts[i]->i_applyDefaults();
9441 for (settings::ParallelPortsList::const_iterator
9442 it = data.llParallelPorts.begin();
9443 it != data.llParallelPorts.end();
9444 ++it)
9445 {
9446 const settings::ParallelPort &p = *it;
9447
9448 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9449 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9450 if (FAILED(rc)) return rc;
9451 }
9452
9453 /* Audio settings */
9454 rc = mAudioSettings->i_loadSettings(data.audioAdapter);
9455 if (FAILED(rc)) return rc;
9456
9457 /* storage controllers */
9458 rc = i_loadStorageControllers(data.storage,
9459 puuidRegistry,
9460 puuidSnapshot);
9461 if (FAILED(rc)) return rc;
9462
9463 /* Shared folders */
9464 for (settings::SharedFoldersList::const_iterator
9465 it = data.llSharedFolders.begin();
9466 it != data.llSharedFolders.end();
9467 ++it)
9468 {
9469 const settings::SharedFolder &sf = *it;
9470
9471 ComObjPtr<SharedFolder> sharedFolder;
9472 /* Check for double entries. Not allowed! */
9473 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9474 if (SUCCEEDED(rc))
9475 return setError(VBOX_E_OBJECT_IN_USE,
9476 tr("Shared folder named '%s' already exists"),
9477 sf.strName.c_str());
9478
9479 /* Create the new shared folder. Don't break on error. This will be
9480 * reported when the machine starts. */
9481 sharedFolder.createObject();
9482 rc = sharedFolder->init(i_getMachine(),
9483 sf.strName,
9484 sf.strHostPath,
9485 RT_BOOL(sf.fWritable),
9486 RT_BOOL(sf.fAutoMount),
9487 sf.strAutoMountPoint,
9488 false /* fFailOnError */);
9489 if (FAILED(rc)) return rc;
9490 mHWData->mSharedFolders.push_back(sharedFolder);
9491 }
9492
9493 // Clipboard
9494 mHWData->mClipboardMode = data.clipboardMode;
9495 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9496
9497 // drag'n'drop
9498 mHWData->mDnDMode = data.dndMode;
9499
9500 // guest settings
9501 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9502
9503 // IO settings
9504 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9505 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9506
9507 // Host PCI devices
9508 for (settings::HostPCIDeviceAttachmentList::const_iterator
9509 it = data.pciAttachments.begin();
9510 it != data.pciAttachments.end();
9511 ++it)
9512 {
9513 const settings::HostPCIDeviceAttachment &hpda = *it;
9514 ComObjPtr<PCIDeviceAttachment> pda;
9515
9516 pda.createObject();
9517 pda->i_loadSettings(this, hpda);
9518 mHWData->mPCIDeviceAssignments.push_back(pda);
9519 }
9520
9521 /*
9522 * (The following isn't really real hardware, but it lives in HWData
9523 * for reasons of convenience.)
9524 */
9525
9526#ifdef VBOX_WITH_GUEST_PROPS
9527 /* Guest properties (optional) */
9528
9529 /* Only load transient guest properties for configs which have saved
9530 * state, because there shouldn't be any for powered off VMs. The same
9531 * logic applies for snapshots, as offline snapshots shouldn't have
9532 * any such properties. They confuse the code in various places.
9533 * Note: can't rely on the machine state, as it isn't set yet. */
9534 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9535 /* apologies for the hacky unconst() usage, but this needs hacking
9536 * actually inconsistent settings into consistency, otherwise there
9537 * will be some corner cases where the inconsistency survives
9538 * surprisingly long without getting fixed, especially for snapshots
9539 * as there are no config changes. */
9540 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9541 for (settings::GuestPropertiesList::iterator
9542 it = llGuestProperties.begin();
9543 it != llGuestProperties.end();
9544 /*nothing*/)
9545 {
9546 const settings::GuestProperty &prop = *it;
9547 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9548 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9549 if ( fSkipTransientGuestProperties
9550 && ( fFlags & GUEST_PROP_F_TRANSIENT
9551 || fFlags & GUEST_PROP_F_TRANSRESET))
9552 {
9553 it = llGuestProperties.erase(it);
9554 continue;
9555 }
9556 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9557 mHWData->mGuestProperties[prop.strName] = property;
9558 ++it;
9559 }
9560#endif /* VBOX_WITH_GUEST_PROPS defined */
9561
9562 rc = i_loadDebugging(pDbg);
9563 if (FAILED(rc))
9564 return rc;
9565
9566 mHWData->mAutostart = *pAutostart;
9567
9568 /* default frontend */
9569 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9570 }
9571 catch (std::bad_alloc &)
9572 {
9573 return E_OUTOFMEMORY;
9574 }
9575
9576 AssertComRC(rc);
9577 return rc;
9578}
9579
9580/**
9581 * Called from i_loadHardware() to load the debugging settings of the
9582 * machine.
9583 *
9584 * @param pDbg Pointer to the settings.
9585 */
9586HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9587{
9588 mHWData->mDebugging = *pDbg;
9589 /* no more processing currently required, this will probably change. */
9590
9591 HRESULT rc = mGuestDebugControl->i_loadSettings(*pDbg);
9592 if (FAILED(rc)) return rc;
9593
9594 return S_OK;
9595}
9596
9597/**
9598 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9599 *
9600 * @param data storage settings.
9601 * @param puuidRegistry media registry ID to set media to or NULL;
9602 * see Machine::i_loadMachineDataFromSettings()
9603 * @param puuidSnapshot snapshot ID
9604 * @return
9605 */
9606HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9607 const Guid *puuidRegistry,
9608 const Guid *puuidSnapshot)
9609{
9610 AssertReturn(!i_isSessionMachine(), E_FAIL);
9611
9612 HRESULT rc = S_OK;
9613
9614 for (settings::StorageControllersList::const_iterator
9615 it = data.llStorageControllers.begin();
9616 it != data.llStorageControllers.end();
9617 ++it)
9618 {
9619 const settings::StorageController &ctlData = *it;
9620
9621 ComObjPtr<StorageController> pCtl;
9622 /* Try to find one with the name first. */
9623 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9624 if (SUCCEEDED(rc))
9625 return setError(VBOX_E_OBJECT_IN_USE,
9626 tr("Storage controller named '%s' already exists"),
9627 ctlData.strName.c_str());
9628
9629 pCtl.createObject();
9630 rc = pCtl->init(this,
9631 ctlData.strName,
9632 ctlData.storageBus,
9633 ctlData.ulInstance,
9634 ctlData.fBootable);
9635 if (FAILED(rc)) return rc;
9636
9637 mStorageControllers->push_back(pCtl);
9638
9639 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9640 if (FAILED(rc)) return rc;
9641
9642 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9643 if (FAILED(rc)) return rc;
9644
9645 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9646 if (FAILED(rc)) return rc;
9647
9648 /* Load the attached devices now. */
9649 rc = i_loadStorageDevices(pCtl,
9650 ctlData,
9651 puuidRegistry,
9652 puuidSnapshot);
9653 if (FAILED(rc)) return rc;
9654 }
9655
9656 return S_OK;
9657}
9658
9659/**
9660 * Called from i_loadStorageControllers for a controller's devices.
9661 *
9662 * @param aStorageController
9663 * @param data
9664 * @param puuidRegistry media registry ID to set media to or NULL; see
9665 * Machine::i_loadMachineDataFromSettings()
9666 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9667 * @return
9668 */
9669HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9670 const settings::StorageController &data,
9671 const Guid *puuidRegistry,
9672 const Guid *puuidSnapshot)
9673{
9674 HRESULT rc = S_OK;
9675
9676 /* paranoia: detect duplicate attachments */
9677 for (settings::AttachedDevicesList::const_iterator
9678 it = data.llAttachedDevices.begin();
9679 it != data.llAttachedDevices.end();
9680 ++it)
9681 {
9682 const settings::AttachedDevice &ad = *it;
9683
9684 for (settings::AttachedDevicesList::const_iterator it2 = it;
9685 it2 != data.llAttachedDevices.end();
9686 ++it2)
9687 {
9688 if (it == it2)
9689 continue;
9690
9691 const settings::AttachedDevice &ad2 = *it2;
9692
9693 if ( ad.lPort == ad2.lPort
9694 && ad.lDevice == ad2.lDevice)
9695 {
9696 return setError(E_FAIL,
9697 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9698 aStorageController->i_getName().c_str(),
9699 ad.lPort,
9700 ad.lDevice,
9701 mUserData->s.strName.c_str());
9702 }
9703 }
9704 }
9705
9706 for (settings::AttachedDevicesList::const_iterator
9707 it = data.llAttachedDevices.begin();
9708 it != data.llAttachedDevices.end();
9709 ++it)
9710 {
9711 const settings::AttachedDevice &dev = *it;
9712 ComObjPtr<Medium> medium;
9713
9714 switch (dev.deviceType)
9715 {
9716 case DeviceType_Floppy:
9717 case DeviceType_DVD:
9718 if (dev.strHostDriveSrc.isNotEmpty())
9719 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9720 false /* fRefresh */, medium);
9721 else
9722 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9723 dev.uuid,
9724 false /* fRefresh */,
9725 false /* aSetError */,
9726 medium);
9727 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9728 // This is not an error. The host drive or UUID might have vanished, so just go
9729 // ahead without this removeable medium attachment
9730 rc = S_OK;
9731 break;
9732
9733 case DeviceType_HardDisk:
9734 {
9735 /* find a hard disk by UUID */
9736 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9737 if (FAILED(rc))
9738 {
9739 if (i_isSnapshotMachine())
9740 {
9741 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9742 // so the user knows that the bad disk is in a snapshot somewhere
9743 com::ErrorInfo info;
9744 return setError(E_FAIL,
9745 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9746 puuidSnapshot->raw(),
9747 info.getText().raw());
9748 }
9749 else
9750 return rc;
9751 }
9752
9753 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9754
9755 if (medium->i_getType() == MediumType_Immutable)
9756 {
9757 if (i_isSnapshotMachine())
9758 return setError(E_FAIL,
9759 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9760 "of the virtual machine '%s' ('%s')"),
9761 medium->i_getLocationFull().c_str(),
9762 dev.uuid.raw(),
9763 puuidSnapshot->raw(),
9764 mUserData->s.strName.c_str(),
9765 mData->m_strConfigFileFull.c_str());
9766
9767 return setError(E_FAIL,
9768 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9769 medium->i_getLocationFull().c_str(),
9770 dev.uuid.raw(),
9771 mUserData->s.strName.c_str(),
9772 mData->m_strConfigFileFull.c_str());
9773 }
9774
9775 if (medium->i_getType() == MediumType_MultiAttach)
9776 {
9777 if (i_isSnapshotMachine())
9778 return setError(E_FAIL,
9779 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9780 "of the virtual machine '%s' ('%s')"),
9781 medium->i_getLocationFull().c_str(),
9782 dev.uuid.raw(),
9783 puuidSnapshot->raw(),
9784 mUserData->s.strName.c_str(),
9785 mData->m_strConfigFileFull.c_str());
9786
9787 return setError(E_FAIL,
9788 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9789 medium->i_getLocationFull().c_str(),
9790 dev.uuid.raw(),
9791 mUserData->s.strName.c_str(),
9792 mData->m_strConfigFileFull.c_str());
9793 }
9794
9795 if ( !i_isSnapshotMachine()
9796 && medium->i_getChildren().size() != 0
9797 )
9798 return setError(E_FAIL,
9799 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9800 "because it has %d differencing child hard disks"),
9801 medium->i_getLocationFull().c_str(),
9802 dev.uuid.raw(),
9803 mUserData->s.strName.c_str(),
9804 mData->m_strConfigFileFull.c_str(),
9805 medium->i_getChildren().size());
9806
9807 if (i_findAttachment(*mMediumAttachments.data(),
9808 medium))
9809 return setError(E_FAIL,
9810 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9811 medium->i_getLocationFull().c_str(),
9812 dev.uuid.raw(),
9813 mUserData->s.strName.c_str(),
9814 mData->m_strConfigFileFull.c_str());
9815
9816 break;
9817 }
9818
9819 default:
9820 return setError(E_FAIL,
9821 tr("Controller '%s' port %u unit %u has device with unknown type (%d) - virtual machine '%s' ('%s')"),
9822 data.strName.c_str(), dev.lPort, dev.lDevice, dev.deviceType,
9823 mUserData->s.strName.c_str(), mData->m_strConfigFileFull.c_str());
9824 }
9825
9826 if (FAILED(rc))
9827 break;
9828
9829 /* Bandwidth groups are loaded at this point. */
9830 ComObjPtr<BandwidthGroup> pBwGroup;
9831
9832 if (!dev.strBwGroup.isEmpty())
9833 {
9834 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9835 if (FAILED(rc))
9836 return setError(E_FAIL,
9837 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9838 medium->i_getLocationFull().c_str(),
9839 dev.strBwGroup.c_str(),
9840 mUserData->s.strName.c_str(),
9841 mData->m_strConfigFileFull.c_str());
9842 pBwGroup->i_reference();
9843 }
9844
9845 const Utf8Str controllerName = aStorageController->i_getName();
9846 ComObjPtr<MediumAttachment> pAttachment;
9847 pAttachment.createObject();
9848 rc = pAttachment->init(this,
9849 medium,
9850 controllerName,
9851 dev.lPort,
9852 dev.lDevice,
9853 dev.deviceType,
9854 false,
9855 dev.fPassThrough,
9856 dev.fTempEject,
9857 dev.fNonRotational,
9858 dev.fDiscard,
9859 dev.fHotPluggable,
9860 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9861 if (FAILED(rc)) break;
9862
9863 /* associate the medium with this machine and snapshot */
9864 if (!medium.isNull())
9865 {
9866 AutoCaller medCaller(medium);
9867 if (FAILED(medCaller.rc())) return medCaller.rc();
9868 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9869
9870 if (i_isSnapshotMachine())
9871 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9872 else
9873 rc = medium->i_addBackReference(mData->mUuid);
9874 /* If the medium->addBackReference fails it sets an appropriate
9875 * error message, so no need to do any guesswork here. */
9876
9877 if (puuidRegistry)
9878 // caller wants registry ID to be set on all attached media (OVF import case)
9879 medium->i_addRegistry(*puuidRegistry);
9880 }
9881
9882 if (FAILED(rc))
9883 break;
9884
9885 /* back up mMediumAttachments to let registeredInit() properly rollback
9886 * on failure (= limited accessibility) */
9887 i_setModified(IsModified_Storage);
9888 mMediumAttachments.backup();
9889 mMediumAttachments->push_back(pAttachment);
9890 }
9891
9892 return rc;
9893}
9894
9895/**
9896 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9897 *
9898 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9899 * @param aSnapshot where to return the found snapshot
9900 * @param aSetError true to set extended error info on failure
9901 */
9902HRESULT Machine::i_findSnapshotById(const Guid &aId,
9903 ComObjPtr<Snapshot> &aSnapshot,
9904 bool aSetError /* = false */)
9905{
9906 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9907
9908 if (!mData->mFirstSnapshot)
9909 {
9910 if (aSetError)
9911 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9912 return E_FAIL;
9913 }
9914
9915 if (aId.isZero())
9916 aSnapshot = mData->mFirstSnapshot;
9917 else
9918 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9919
9920 if (!aSnapshot)
9921 {
9922 if (aSetError)
9923 return setError(E_FAIL,
9924 tr("Could not find a snapshot with UUID {%s}"),
9925 aId.toString().c_str());
9926 return E_FAIL;
9927 }
9928
9929 return S_OK;
9930}
9931
9932/**
9933 * Returns the snapshot with the given name or fails of no such snapshot.
9934 *
9935 * @param strName snapshot name to find
9936 * @param aSnapshot where to return the found snapshot
9937 * @param aSetError true to set extended error info on failure
9938 */
9939HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9940 ComObjPtr<Snapshot> &aSnapshot,
9941 bool aSetError /* = false */)
9942{
9943 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9944
9945 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9946
9947 if (!mData->mFirstSnapshot)
9948 {
9949 if (aSetError)
9950 return setError(VBOX_E_OBJECT_NOT_FOUND,
9951 tr("This machine does not have any snapshots"));
9952 return VBOX_E_OBJECT_NOT_FOUND;
9953 }
9954
9955 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9956
9957 if (!aSnapshot)
9958 {
9959 if (aSetError)
9960 return setError(VBOX_E_OBJECT_NOT_FOUND,
9961 tr("Could not find a snapshot named '%s'"), strName.c_str());
9962 return VBOX_E_OBJECT_NOT_FOUND;
9963 }
9964
9965 return S_OK;
9966}
9967
9968/**
9969 * Returns a storage controller object with the given name.
9970 *
9971 * @param aName storage controller name to find
9972 * @param aStorageController where to return the found storage controller
9973 * @param aSetError true to set extended error info on failure
9974 */
9975HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9976 ComObjPtr<StorageController> &aStorageController,
9977 bool aSetError /* = false */)
9978{
9979 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9980
9981 for (StorageControllerList::const_iterator
9982 it = mStorageControllers->begin();
9983 it != mStorageControllers->end();
9984 ++it)
9985 {
9986 if ((*it)->i_getName() == aName)
9987 {
9988 aStorageController = (*it);
9989 return S_OK;
9990 }
9991 }
9992
9993 if (aSetError)
9994 return setError(VBOX_E_OBJECT_NOT_FOUND,
9995 tr("Could not find a storage controller named '%s'"),
9996 aName.c_str());
9997 return VBOX_E_OBJECT_NOT_FOUND;
9998}
9999
10000/**
10001 * Returns a USB controller object with the given name.
10002 *
10003 * @param aName USB controller name to find
10004 * @param aUSBController where to return the found USB controller
10005 * @param aSetError true to set extended error info on failure
10006 */
10007HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
10008 ComObjPtr<USBController> &aUSBController,
10009 bool aSetError /* = false */)
10010{
10011 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
10012
10013 for (USBControllerList::const_iterator
10014 it = mUSBControllers->begin();
10015 it != mUSBControllers->end();
10016 ++it)
10017 {
10018 if ((*it)->i_getName() == aName)
10019 {
10020 aUSBController = (*it);
10021 return S_OK;
10022 }
10023 }
10024
10025 if (aSetError)
10026 return setError(VBOX_E_OBJECT_NOT_FOUND,
10027 tr("Could not find a storage controller named '%s'"),
10028 aName.c_str());
10029 return VBOX_E_OBJECT_NOT_FOUND;
10030}
10031
10032/**
10033 * Returns the number of USB controller instance of the given type.
10034 *
10035 * @param enmType USB controller type.
10036 */
10037ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
10038{
10039 ULONG cCtrls = 0;
10040
10041 for (USBControllerList::const_iterator
10042 it = mUSBControllers->begin();
10043 it != mUSBControllers->end();
10044 ++it)
10045 {
10046 if ((*it)->i_getControllerType() == enmType)
10047 cCtrls++;
10048 }
10049
10050 return cCtrls;
10051}
10052
10053HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
10054 MediumAttachmentList &atts)
10055{
10056 AutoCaller autoCaller(this);
10057 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10058
10059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10060
10061 for (MediumAttachmentList::const_iterator
10062 it = mMediumAttachments->begin();
10063 it != mMediumAttachments->end();
10064 ++it)
10065 {
10066 const ComObjPtr<MediumAttachment> &pAtt = *it;
10067 // should never happen, but deal with NULL pointers in the list.
10068 AssertContinue(!pAtt.isNull());
10069
10070 // getControllerName() needs caller+read lock
10071 AutoCaller autoAttCaller(pAtt);
10072 if (FAILED(autoAttCaller.rc()))
10073 {
10074 atts.clear();
10075 return autoAttCaller.rc();
10076 }
10077 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
10078
10079 if (pAtt->i_getControllerName() == aName)
10080 atts.push_back(pAtt);
10081 }
10082
10083 return S_OK;
10084}
10085
10086
10087/**
10088 * Helper for #i_saveSettings. Cares about renaming the settings directory and
10089 * file if the machine name was changed and about creating a new settings file
10090 * if this is a new machine.
10091 *
10092 * @note Must be never called directly but only from #saveSettings().
10093 */
10094HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
10095 bool *pfSettingsFileIsNew)
10096{
10097 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10098
10099 HRESULT rc = S_OK;
10100
10101 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
10102 /// @todo need to handle primary group change, too
10103
10104 /* attempt to rename the settings file if machine name is changed */
10105 if ( mUserData->s.fNameSync
10106 && mUserData.isBackedUp()
10107 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
10108 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
10109 )
10110 {
10111 bool dirRenamed = false;
10112 bool fileRenamed = false;
10113
10114 Utf8Str configFile, newConfigFile;
10115 Utf8Str configFilePrev, newConfigFilePrev;
10116 Utf8Str NVRAMFile, newNVRAMFile;
10117 Utf8Str configDir, newConfigDir;
10118
10119 do
10120 {
10121 int vrc = VINF_SUCCESS;
10122
10123 Utf8Str name = mUserData.backedUpData()->s.strName;
10124 Utf8Str newName = mUserData->s.strName;
10125 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
10126 if (group == "/")
10127 group.setNull();
10128 Utf8Str newGroup = mUserData->s.llGroups.front();
10129 if (newGroup == "/")
10130 newGroup.setNull();
10131
10132 configFile = mData->m_strConfigFileFull;
10133
10134 /* first, rename the directory if it matches the group and machine name */
10135 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
10136 /** @todo hack, make somehow use of ComposeMachineFilename */
10137 if (mUserData->s.fDirectoryIncludesUUID)
10138 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10139 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
10140 /** @todo hack, make somehow use of ComposeMachineFilename */
10141 if (mUserData->s.fDirectoryIncludesUUID)
10142 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10143 configDir = configFile;
10144 configDir.stripFilename();
10145 newConfigDir = configDir;
10146 if ( configDir.length() >= groupPlusName.length()
10147 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
10148 groupPlusName.c_str()))
10149 {
10150 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10151 Utf8Str newConfigBaseDir(newConfigDir);
10152 newConfigDir.append(newGroupPlusName);
10153 /* consistency: use \ if appropriate on the platform */
10154 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10155 /* new dir and old dir cannot be equal here because of 'if'
10156 * above and because name != newName */
10157 Assert(configDir != newConfigDir);
10158 if (!fSettingsFileIsNew)
10159 {
10160 /* perform real rename only if the machine is not new */
10161 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10162 if ( vrc == VERR_FILE_NOT_FOUND
10163 || vrc == VERR_PATH_NOT_FOUND)
10164 {
10165 /* create the parent directory, then retry renaming */
10166 Utf8Str parent(newConfigDir);
10167 parent.stripFilename();
10168 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10169 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10170 }
10171 if (RT_FAILURE(vrc))
10172 {
10173 rc = setErrorBoth(E_FAIL, vrc,
10174 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10175 configDir.c_str(),
10176 newConfigDir.c_str(),
10177 vrc);
10178 break;
10179 }
10180 /* delete subdirectories which are no longer needed */
10181 Utf8Str dir(configDir);
10182 dir.stripFilename();
10183 while (dir != newConfigBaseDir && dir != ".")
10184 {
10185 vrc = RTDirRemove(dir.c_str());
10186 if (RT_FAILURE(vrc))
10187 break;
10188 dir.stripFilename();
10189 }
10190 dirRenamed = true;
10191 }
10192 }
10193
10194 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10195
10196 /* then try to rename the settings file itself */
10197 if (newConfigFile != configFile)
10198 {
10199 /* get the path to old settings file in renamed directory */
10200 Assert(mData->m_strConfigFileFull == configFile);
10201 configFile.printf("%s%c%s",
10202 newConfigDir.c_str(),
10203 RTPATH_DELIMITER,
10204 RTPathFilename(mData->m_strConfigFileFull.c_str()));
10205 if (!fSettingsFileIsNew)
10206 {
10207 /* perform real rename only if the machine is not new */
10208 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10209 if (RT_FAILURE(vrc))
10210 {
10211 rc = setErrorBoth(E_FAIL, vrc,
10212 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10213 configFile.c_str(),
10214 newConfigFile.c_str(),
10215 vrc);
10216 break;
10217 }
10218 fileRenamed = true;
10219 configFilePrev = configFile;
10220 configFilePrev += "-prev";
10221 newConfigFilePrev = newConfigFile;
10222 newConfigFilePrev += "-prev";
10223 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10224 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
10225 if (NVRAMFile.isNotEmpty())
10226 {
10227 // in the NVRAM file path, replace the old directory with the new directory
10228 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
10229 {
10230 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
10231 NVRAMFile = newConfigDir + strNVRAMFile;
10232 }
10233 newNVRAMFile = newConfigFile;
10234 newNVRAMFile.stripSuffix();
10235 newNVRAMFile += ".nvram";
10236 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
10237 }
10238 }
10239 }
10240
10241 // update m_strConfigFileFull amd mConfigFile
10242 mData->m_strConfigFileFull = newConfigFile;
10243 // compute the relative path too
10244 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10245
10246 // store the old and new so that VirtualBox::i_saveSettings() can update
10247 // the media registry
10248 if ( mData->mRegistered
10249 && (configDir != newConfigDir || configFile != newConfigFile))
10250 {
10251 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10252
10253 if (pfNeedsGlobalSaveSettings)
10254 *pfNeedsGlobalSaveSettings = true;
10255 }
10256
10257 // in the saved state file path, replace the old directory with the new directory
10258 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10259 {
10260 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
10261 mSSData->strStateFilePath = newConfigDir + strStateFileName;
10262 }
10263 if (newNVRAMFile.isNotEmpty())
10264 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
10265
10266 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
10267 if (mData->mFirstSnapshot)
10268 {
10269 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10270 newConfigDir.c_str());
10271 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
10272 newConfigDir.c_str());
10273 }
10274 }
10275 while (0);
10276
10277 if (FAILED(rc))
10278 {
10279 /* silently try to rename everything back */
10280 if (fileRenamed)
10281 {
10282 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10283 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10284 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
10285 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
10286 }
10287 if (dirRenamed)
10288 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10289 }
10290
10291 if (FAILED(rc)) return rc;
10292 }
10293
10294 if (fSettingsFileIsNew)
10295 {
10296 /* create a virgin config file */
10297 int vrc = VINF_SUCCESS;
10298
10299 /* ensure the settings directory exists */
10300 Utf8Str path(mData->m_strConfigFileFull);
10301 path.stripFilename();
10302 if (!RTDirExists(path.c_str()))
10303 {
10304 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10305 if (RT_FAILURE(vrc))
10306 {
10307 return setErrorBoth(E_FAIL, vrc,
10308 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10309 path.c_str(),
10310 vrc);
10311 }
10312 }
10313
10314 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10315 path = mData->m_strConfigFileFull;
10316 RTFILE f = NIL_RTFILE;
10317 vrc = RTFileOpen(&f, path.c_str(),
10318 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10319 if (RT_FAILURE(vrc))
10320 return setErrorBoth(E_FAIL, vrc,
10321 tr("Could not create the settings file '%s' (%Rrc)"),
10322 path.c_str(),
10323 vrc);
10324 RTFileClose(f);
10325 }
10326 if (pfSettingsFileIsNew)
10327 *pfSettingsFileIsNew = fSettingsFileIsNew;
10328
10329 return rc;
10330}
10331
10332/**
10333 * Saves and commits machine data, user data and hardware data.
10334 *
10335 * Note that on failure, the data remains uncommitted.
10336 *
10337 * @a aFlags may combine the following flags:
10338 *
10339 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10340 * Used when saving settings after an operation that makes them 100%
10341 * correspond to the settings from the current snapshot.
10342 * - SaveS_Force: settings will be saved without doing a deep compare of the
10343 * settings structures. This is used when this is called because snapshots
10344 * have changed to avoid the overhead of the deep compare.
10345 *
10346 * @note Must be called from under this object's write lock. Locks children for
10347 * writing.
10348 *
10349 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10350 * initialized to false and that will be set to true by this function if
10351 * the caller must invoke VirtualBox::i_saveSettings() because the global
10352 * settings have changed. This will happen if a machine rename has been
10353 * saved and the global machine and media registries will therefore need
10354 * updating.
10355 * @param alock Reference to the lock for this machine object.
10356 * @param aFlags Flags.
10357 */
10358HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10359 AutoWriteLock &alock,
10360 int aFlags /*= 0*/)
10361{
10362 LogFlowThisFuncEnter();
10363
10364 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10365
10366 /* make sure child objects are unable to modify the settings while we are
10367 * saving them */
10368 i_ensureNoStateDependencies(alock);
10369
10370 AssertReturn(!i_isSnapshotMachine(),
10371 E_FAIL);
10372
10373 if (!mData->mAccessible)
10374 return setError(VBOX_E_INVALID_VM_STATE,
10375 tr("The machine is not accessible, so cannot save settings"));
10376
10377 HRESULT rc = S_OK;
10378 PCVBOXCRYPTOIF pCryptoIf = NULL;
10379 const char *pszPassword = NULL;
10380 SecretKey *pKey = NULL;
10381
10382#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10383 if (mData->mstrKeyId.isNotEmpty())
10384 {
10385 /* VM is going to be encrypted. */
10386 alock.release(); /** @todo Revise the locking. */
10387 rc = mParent->i_retainCryptoIf(&pCryptoIf);
10388 alock.acquire();
10389 if (FAILED(rc)) return rc; /* Error is set. */
10390
10391 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
10392 if (RT_SUCCESS(vrc))
10393 pszPassword = (const char *)pKey->getKeyBuffer();
10394 else
10395 {
10396 mParent->i_releaseCryptoIf(pCryptoIf);
10397 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
10398 tr("Failed to retain VM encryption password using ID '%s' with %Rrc"),
10399 mData->mstrKeyId.c_str(), vrc);
10400 }
10401 }
10402#else
10403 RT_NOREF(pKey);
10404#endif
10405
10406 bool fNeedsWrite = false;
10407 bool fSettingsFileIsNew = false;
10408
10409 /* First, prepare to save settings. It will care about renaming the
10410 * settings directory and file if the machine name was changed and about
10411 * creating a new settings file if this is a new machine. */
10412 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings,
10413 &fSettingsFileIsNew);
10414 if (FAILED(rc))
10415 {
10416#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10417 if (pCryptoIf)
10418 {
10419 alock.release(); /** @todo Revise the locking. */
10420 mParent->i_releaseCryptoIf(pCryptoIf);
10421 alock.acquire();
10422 }
10423 if (pKey)
10424 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10425#endif
10426 return rc;
10427 }
10428
10429 // keep a pointer to the current settings structures
10430 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10431 settings::MachineConfigFile *pNewConfig = NULL;
10432
10433 try
10434 {
10435 // make a fresh one to have everyone write stuff into
10436 pNewConfig = new settings::MachineConfigFile(NULL);
10437 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10438#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10439 pNewConfig->strKeyId = mData->mstrKeyId;
10440 pNewConfig->strKeyStore = mData->mstrKeyStore;
10441#endif
10442
10443 // now go and copy all the settings data from COM to the settings structures
10444 // (this calls i_saveSettings() on all the COM objects in the machine)
10445 i_copyMachineDataToSettings(*pNewConfig);
10446
10447 if (aFlags & SaveS_ResetCurStateModified)
10448 {
10449 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10450 mData->mCurrentStateModified = FALSE;
10451 fNeedsWrite = true; // always, no need to compare
10452 }
10453 else if (aFlags & SaveS_Force)
10454 {
10455 fNeedsWrite = true; // always, no need to compare
10456 }
10457 else
10458 {
10459 if (!mData->mCurrentStateModified)
10460 {
10461 // do a deep compare of the settings that we just saved with the settings
10462 // previously stored in the config file; this invokes MachineConfigFile::operator==
10463 // which does a deep compare of all the settings, which is expensive but less expensive
10464 // than writing out XML in vain
10465 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10466
10467 // could still be modified if any settings changed
10468 mData->mCurrentStateModified = fAnySettingsChanged;
10469
10470 fNeedsWrite = fAnySettingsChanged;
10471 }
10472 else
10473 fNeedsWrite = true;
10474 }
10475
10476 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10477
10478 if (fNeedsWrite)
10479 {
10480 // now spit it all out!
10481 pNewConfig->write(mData->m_strConfigFileFull, pCryptoIf, pszPassword);
10482 if (aFlags & SaveS_RemoveBackup)
10483 i_deleteFile(mData->m_strConfigFileFull + "-prev", true /* fIgnoreFailures */);
10484 }
10485
10486 mData->pMachineConfigFile = pNewConfig;
10487 delete pOldConfig;
10488 i_commit();
10489
10490 // after saving settings, we are no longer different from the XML on disk
10491 mData->flModifications = 0;
10492 }
10493 catch (HRESULT err)
10494 {
10495 // we assume that error info is set by the thrower
10496 rc = err;
10497
10498 // delete any newly created settings file
10499 if (fSettingsFileIsNew)
10500 i_deleteFile(mData->m_strConfigFileFull, true /* fIgnoreFailures */);
10501
10502 // restore old config
10503 delete pNewConfig;
10504 mData->pMachineConfigFile = pOldConfig;
10505 }
10506 catch (...)
10507 {
10508 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10509 }
10510
10511#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10512 if (pCryptoIf)
10513 {
10514 alock.release(); /** @todo Revise the locking. */
10515 mParent->i_releaseCryptoIf(pCryptoIf);
10516 alock.acquire();
10517 }
10518 if (pKey)
10519 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10520#endif
10521
10522 if (fNeedsWrite)
10523 {
10524 /* Fire the data change event, even on failure (since we've already
10525 * committed all data). This is done only for SessionMachines because
10526 * mutable Machine instances are always not registered (i.e. private
10527 * to the client process that creates them) and thus don't need to
10528 * inform callbacks. */
10529 if (i_isSessionMachine())
10530 mParent->i_onMachineDataChanged(mData->mUuid);
10531 }
10532
10533 LogFlowThisFunc(("rc=%08X\n", rc));
10534 LogFlowThisFuncLeave();
10535 return rc;
10536}
10537
10538/**
10539 * Implementation for saving the machine settings into the given
10540 * settings::MachineConfigFile instance. This copies machine extradata
10541 * from the previous machine config file in the instance data, if any.
10542 *
10543 * This gets called from two locations:
10544 *
10545 * -- Machine::i_saveSettings(), during the regular XML writing;
10546 *
10547 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10548 * exported to OVF and we write the VirtualBox proprietary XML
10549 * into a <vbox:Machine> tag.
10550 *
10551 * This routine fills all the fields in there, including snapshots, *except*
10552 * for the following:
10553 *
10554 * -- fCurrentStateModified. There is some special logic associated with that.
10555 *
10556 * The caller can then call MachineConfigFile::write() or do something else
10557 * with it.
10558 *
10559 * Caller must hold the machine lock!
10560 *
10561 * This throws XML errors and HRESULT, so the caller must have a catch block!
10562 */
10563void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10564{
10565 // deep copy extradata, being extra careful with self assignment (the STL
10566 // map assignment on Mac OS X clang based Xcode isn't checking)
10567 if (&config != mData->pMachineConfigFile)
10568 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10569
10570 config.uuid = mData->mUuid;
10571
10572 // copy name, description, OS type, teleport, UTC etc.
10573 config.machineUserData = mUserData->s;
10574
10575#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10576 config.strStateKeyId = mSSData->strStateKeyId;
10577 config.strStateKeyStore = mSSData->strStateKeyStore;
10578 config.strLogKeyId = mData->mstrLogKeyId;
10579 config.strLogKeyStore = mData->mstrLogKeyStore;
10580#endif
10581
10582 if ( mData->mMachineState == MachineState_Saved
10583 || mData->mMachineState == MachineState_AbortedSaved
10584 || mData->mMachineState == MachineState_Restoring
10585 // when doing certain snapshot operations we may or may not have
10586 // a saved state in the current state, so keep everything as is
10587 || ( ( mData->mMachineState == MachineState_Snapshotting
10588 || mData->mMachineState == MachineState_DeletingSnapshot
10589 || mData->mMachineState == MachineState_RestoringSnapshot)
10590 && (!mSSData->strStateFilePath.isEmpty())
10591 )
10592 )
10593 {
10594 Assert(!mSSData->strStateFilePath.isEmpty());
10595 /* try to make the file name relative to the settings file dir */
10596 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10597 }
10598 else
10599 {
10600 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10601 config.strStateFile.setNull();
10602 }
10603
10604 if (mData->mCurrentSnapshot)
10605 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10606 else
10607 config.uuidCurrentSnapshot.clear();
10608
10609 config.timeLastStateChange = mData->mLastStateChange;
10610 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10611 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10612
10613 HRESULT hrc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart, config.recordingSettings);
10614 if (FAILED(hrc)) throw hrc;
10615
10616 // save machine's media registry if this is VirtualBox 4.0 or later
10617 if (config.canHaveOwnMediaRegistry())
10618 {
10619 // determine machine folder
10620 Utf8Str strMachineFolder = i_getSettingsFileFull();
10621 strMachineFolder.stripFilename();
10622 mParent->i_saveMediaRegistry(config.mediaRegistry,
10623 i_getId(), // only media with registry ID == machine UUID
10624 strMachineFolder);
10625 // this throws HRESULT
10626 }
10627
10628 // save snapshots
10629 hrc = i_saveAllSnapshots(config);
10630 if (FAILED(hrc)) throw hrc;
10631}
10632
10633/**
10634 * Saves all snapshots of the machine into the given machine config file. Called
10635 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10636 * @param config
10637 * @return
10638 */
10639HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10640{
10641 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10642
10643 HRESULT rc = S_OK;
10644
10645 try
10646 {
10647 config.llFirstSnapshot.clear();
10648
10649 if (mData->mFirstSnapshot)
10650 {
10651 // the settings use a list for "the first snapshot"
10652 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10653
10654 // get reference to the snapshot on the list and work on that
10655 // element straight in the list to avoid excessive copying later
10656 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10657 if (FAILED(rc)) throw rc;
10658 }
10659
10660// if (mType == IsSessionMachine)
10661// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10662
10663 }
10664 catch (HRESULT err)
10665 {
10666 /* we assume that error info is set by the thrower */
10667 rc = err;
10668 }
10669 catch (...)
10670 {
10671 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10672 }
10673
10674 return rc;
10675}
10676
10677/**
10678 * Saves the VM hardware configuration. It is assumed that the
10679 * given node is empty.
10680 *
10681 * @param data Reference to the settings object for the hardware config.
10682 * @param pDbg Pointer to the settings object for the debugging config
10683 * which happens to live in mHWData.
10684 * @param pAutostart Pointer to the settings object for the autostart config
10685 * which happens to live in mHWData.
10686 * @param recording Reference to reecording settings.
10687 */
10688HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10689 settings::Autostart *pAutostart, settings::RecordingSettings &recording)
10690{
10691 HRESULT rc = S_OK;
10692
10693 try
10694 {
10695 /* The hardware version attribute (optional).
10696 Automatically upgrade from 1 to current default hardware version
10697 when there is no saved state. (ugly!) */
10698 if ( mHWData->mHWVersion == "1"
10699 && mSSData->strStateFilePath.isEmpty()
10700 )
10701 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10702
10703 data.strVersion = mHWData->mHWVersion;
10704 data.uuid = mHWData->mHardwareUUID;
10705
10706 // CPU
10707 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10708 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10709 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10710 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10711 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10712 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10713 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10714 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10715 data.fPAE = !!mHWData->mPAEEnabled;
10716 data.enmLongMode = mHWData->mLongMode;
10717 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10718 data.fAPIC = !!mHWData->mAPIC;
10719 data.fX2APIC = !!mHWData->mX2APIC;
10720 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10721 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10722 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10723 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10724 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10725 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10726 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10727 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10728 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10729 data.cCPUs = mHWData->mCPUCount;
10730 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10731 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10732 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10733 data.strCpuProfile = mHWData->mCpuProfile;
10734
10735 data.llCpus.clear();
10736 if (data.fCpuHotPlug)
10737 {
10738 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10739 {
10740 if (mHWData->mCPUAttached[idx])
10741 {
10742 settings::Cpu cpu;
10743 cpu.ulId = idx;
10744 data.llCpus.push_back(cpu);
10745 }
10746 }
10747 }
10748
10749 /* Standard and Extended CPUID leafs. */
10750 data.llCpuIdLeafs.clear();
10751 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10752
10753 // memory
10754 data.ulMemorySizeMB = mHWData->mMemorySize;
10755 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10756
10757 // firmware
10758 data.firmwareType = mHWData->mFirmwareType;
10759
10760 // HID
10761 data.pointingHIDType = mHWData->mPointingHIDType;
10762 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10763
10764 // chipset
10765 data.chipsetType = mHWData->mChipsetType;
10766
10767 // iommu
10768 data.iommuType = mHWData->mIommuType;
10769
10770 // paravirt
10771 data.paravirtProvider = mHWData->mParavirtProvider;
10772 data.strParavirtDebug = mHWData->mParavirtDebug;
10773
10774 // emulated USB card reader
10775 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10776
10777 // HPET
10778 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10779
10780 // boot order
10781 data.mapBootOrder.clear();
10782 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10783 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10784
10785 /* VRDEServer settings (optional) */
10786 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10787 if (FAILED(rc)) throw rc;
10788
10789 /* BIOS settings (required) */
10790 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10791 if (FAILED(rc)) throw rc;
10792
10793 /* Recording settings. */
10794 rc = mRecordingSettings->i_saveSettings(recording);
10795 if (FAILED(rc)) throw rc;
10796
10797 /* Trusted Platform Module settings (required) */
10798 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10799 if (FAILED(rc)) throw rc;
10800
10801 /* NVRAM settings (required) */
10802 rc = mNvramStore->i_saveSettings(data.nvramSettings);
10803 if (FAILED(rc)) throw rc;
10804
10805 /* GraphicsAdapter settings (required) */
10806 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10807 if (FAILED(rc)) throw rc;
10808
10809 /* USB Controller (required) */
10810 data.usbSettings.llUSBControllers.clear();
10811 for (USBControllerList::const_iterator
10812 it = mUSBControllers->begin();
10813 it != mUSBControllers->end();
10814 ++it)
10815 {
10816 ComObjPtr<USBController> ctrl = *it;
10817 settings::USBController settingsCtrl;
10818
10819 settingsCtrl.strName = ctrl->i_getName();
10820 settingsCtrl.enmType = ctrl->i_getControllerType();
10821
10822 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10823 }
10824
10825 /* USB device filters (required) */
10826 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10827 if (FAILED(rc)) throw rc;
10828
10829 /* Network adapters (required) */
10830 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10831 data.llNetworkAdapters.clear();
10832 /* Write out only the nominal number of network adapters for this
10833 * chipset type. Since Machine::commit() hasn't been called there
10834 * may be extra NIC settings in the vector. */
10835 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10836 {
10837 settings::NetworkAdapter nic;
10838 nic.ulSlot = (uint32_t)slot;
10839 /* paranoia check... must not be NULL, but must not crash either. */
10840 if (mNetworkAdapters[slot])
10841 {
10842 if (mNetworkAdapters[slot]->i_hasDefaults())
10843 continue;
10844
10845 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10846 if (FAILED(rc)) throw rc;
10847
10848 data.llNetworkAdapters.push_back(nic);
10849 }
10850 }
10851
10852 /* Serial ports */
10853 data.llSerialPorts.clear();
10854 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10855 {
10856 if (mSerialPorts[slot]->i_hasDefaults())
10857 continue;
10858
10859 settings::SerialPort s;
10860 s.ulSlot = slot;
10861 rc = mSerialPorts[slot]->i_saveSettings(s);
10862 if (FAILED(rc)) return rc;
10863
10864 data.llSerialPorts.push_back(s);
10865 }
10866
10867 /* Parallel ports */
10868 data.llParallelPorts.clear();
10869 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10870 {
10871 if (mParallelPorts[slot]->i_hasDefaults())
10872 continue;
10873
10874 settings::ParallelPort p;
10875 p.ulSlot = slot;
10876 rc = mParallelPorts[slot]->i_saveSettings(p);
10877 if (FAILED(rc)) return rc;
10878
10879 data.llParallelPorts.push_back(p);
10880 }
10881
10882 /* Audio settings */
10883 rc = mAudioSettings->i_saveSettings(data.audioAdapter);
10884 if (FAILED(rc)) return rc;
10885
10886 rc = i_saveStorageControllers(data.storage);
10887 if (FAILED(rc)) return rc;
10888
10889 /* Shared folders */
10890 data.llSharedFolders.clear();
10891 for (HWData::SharedFolderList::const_iterator
10892 it = mHWData->mSharedFolders.begin();
10893 it != mHWData->mSharedFolders.end();
10894 ++it)
10895 {
10896 SharedFolder *pSF = *it;
10897 AutoCaller sfCaller(pSF);
10898 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10899 settings::SharedFolder sf;
10900 sf.strName = pSF->i_getName();
10901 sf.strHostPath = pSF->i_getHostPath();
10902 sf.fWritable = !!pSF->i_isWritable();
10903 sf.fAutoMount = !!pSF->i_isAutoMounted();
10904 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10905
10906 data.llSharedFolders.push_back(sf);
10907 }
10908
10909 // clipboard
10910 data.clipboardMode = mHWData->mClipboardMode;
10911 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10912
10913 // drag'n'drop
10914 data.dndMode = mHWData->mDnDMode;
10915
10916 /* Guest */
10917 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10918
10919 // IO settings
10920 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10921 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10922
10923 /* BandwidthControl (required) */
10924 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10925 if (FAILED(rc)) throw rc;
10926
10927 /* Host PCI devices */
10928 data.pciAttachments.clear();
10929 for (HWData::PCIDeviceAssignmentList::const_iterator
10930 it = mHWData->mPCIDeviceAssignments.begin();
10931 it != mHWData->mPCIDeviceAssignments.end();
10932 ++it)
10933 {
10934 ComObjPtr<PCIDeviceAttachment> pda = *it;
10935 settings::HostPCIDeviceAttachment hpda;
10936
10937 rc = pda->i_saveSettings(hpda);
10938 if (FAILED(rc)) throw rc;
10939
10940 data.pciAttachments.push_back(hpda);
10941 }
10942
10943 // guest properties
10944 data.llGuestProperties.clear();
10945#ifdef VBOX_WITH_GUEST_PROPS
10946 for (HWData::GuestPropertyMap::const_iterator
10947 it = mHWData->mGuestProperties.begin();
10948 it != mHWData->mGuestProperties.end();
10949 ++it)
10950 {
10951 HWData::GuestProperty property = it->second;
10952
10953 /* Remove transient guest properties at shutdown unless we
10954 * are saving state. Note that restoring snapshot intentionally
10955 * keeps them, they will be removed if appropriate once the final
10956 * machine state is set (as crashes etc. need to work). */
10957 if ( ( mData->mMachineState == MachineState_PoweredOff
10958 || mData->mMachineState == MachineState_Aborted
10959 || mData->mMachineState == MachineState_Teleported)
10960 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10961 continue;
10962 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10963 prop.strName = it->first;
10964 prop.strValue = property.strValue;
10965 prop.timestamp = (uint64_t)property.mTimestamp;
10966 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10967 GuestPropWriteFlags(property.mFlags, szFlags);
10968 prop.strFlags = szFlags;
10969
10970 data.llGuestProperties.push_back(prop);
10971 }
10972
10973 /* I presume this doesn't require a backup(). */
10974 mData->mGuestPropertiesModified = FALSE;
10975#endif /* VBOX_WITH_GUEST_PROPS defined */
10976
10977 rc = mGuestDebugControl->i_saveSettings(mHWData->mDebugging);
10978 if (FAILED(rc)) throw rc;
10979
10980 *pDbg = mHWData->mDebugging; /// @todo r=aeichner: Move this to guest debug control. */
10981 *pAutostart = mHWData->mAutostart;
10982
10983 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10984 }
10985 catch (std::bad_alloc &)
10986 {
10987 return E_OUTOFMEMORY;
10988 }
10989
10990 AssertComRC(rc);
10991 return rc;
10992}
10993
10994/**
10995 * Saves the storage controller configuration.
10996 *
10997 * @param data storage settings.
10998 */
10999HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
11000{
11001 data.llStorageControllers.clear();
11002
11003 for (StorageControllerList::const_iterator
11004 it = mStorageControllers->begin();
11005 it != mStorageControllers->end();
11006 ++it)
11007 {
11008 HRESULT rc;
11009 ComObjPtr<StorageController> pCtl = *it;
11010
11011 settings::StorageController ctl;
11012 ctl.strName = pCtl->i_getName();
11013 ctl.controllerType = pCtl->i_getControllerType();
11014 ctl.storageBus = pCtl->i_getStorageBus();
11015 ctl.ulInstance = pCtl->i_getInstance();
11016 ctl.fBootable = pCtl->i_getBootable();
11017
11018 /* Save the port count. */
11019 ULONG portCount;
11020 rc = pCtl->COMGETTER(PortCount)(&portCount);
11021 ComAssertComRCRet(rc, rc);
11022 ctl.ulPortCount = portCount;
11023
11024 /* Save fUseHostIOCache */
11025 BOOL fUseHostIOCache;
11026 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
11027 ComAssertComRCRet(rc, rc);
11028 ctl.fUseHostIOCache = !!fUseHostIOCache;
11029
11030 /* save the devices now. */
11031 rc = i_saveStorageDevices(pCtl, ctl);
11032 ComAssertComRCRet(rc, rc);
11033
11034 data.llStorageControllers.push_back(ctl);
11035 }
11036
11037 return S_OK;
11038}
11039
11040/**
11041 * Saves the hard disk configuration.
11042 */
11043HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
11044 settings::StorageController &data)
11045{
11046 MediumAttachmentList atts;
11047
11048 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
11049 if (FAILED(rc)) return rc;
11050
11051 data.llAttachedDevices.clear();
11052 for (MediumAttachmentList::const_iterator
11053 it = atts.begin();
11054 it != atts.end();
11055 ++it)
11056 {
11057 settings::AttachedDevice dev;
11058 IMediumAttachment *iA = *it;
11059 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
11060 Medium *pMedium = pAttach->i_getMedium();
11061
11062 dev.deviceType = pAttach->i_getType();
11063 dev.lPort = pAttach->i_getPort();
11064 dev.lDevice = pAttach->i_getDevice();
11065 dev.fPassThrough = pAttach->i_getPassthrough();
11066 dev.fHotPluggable = pAttach->i_getHotPluggable();
11067 if (pMedium)
11068 {
11069 if (pMedium->i_isHostDrive())
11070 dev.strHostDriveSrc = pMedium->i_getLocationFull();
11071 else
11072 dev.uuid = pMedium->i_getId();
11073 dev.fTempEject = pAttach->i_getTempEject();
11074 dev.fNonRotational = pAttach->i_getNonRotational();
11075 dev.fDiscard = pAttach->i_getDiscard();
11076 }
11077
11078 dev.strBwGroup = pAttach->i_getBandwidthGroup();
11079
11080 data.llAttachedDevices.push_back(dev);
11081 }
11082
11083 return S_OK;
11084}
11085
11086/**
11087 * Saves machine state settings as defined by aFlags
11088 * (SaveSTS_* values).
11089 *
11090 * @param aFlags Combination of SaveSTS_* flags.
11091 *
11092 * @note Locks objects for writing.
11093 */
11094HRESULT Machine::i_saveStateSettings(int aFlags)
11095{
11096 if (aFlags == 0)
11097 return S_OK;
11098
11099 AutoCaller autoCaller(this);
11100 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11101
11102 /* This object's write lock is also necessary to serialize file access
11103 * (prevent concurrent reads and writes) */
11104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11105
11106 HRESULT rc = S_OK;
11107
11108 Assert(mData->pMachineConfigFile);
11109
11110 try
11111 {
11112 if (aFlags & SaveSTS_CurStateModified)
11113 mData->pMachineConfigFile->fCurrentStateModified = true;
11114
11115 if (aFlags & SaveSTS_StateFilePath)
11116 {
11117 if (!mSSData->strStateFilePath.isEmpty())
11118 /* try to make the file name relative to the settings file dir */
11119 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
11120 else
11121 mData->pMachineConfigFile->strStateFile.setNull();
11122 }
11123
11124 if (aFlags & SaveSTS_StateTimeStamp)
11125 {
11126 Assert( mData->mMachineState != MachineState_Aborted
11127 || mSSData->strStateFilePath.isEmpty());
11128
11129 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
11130
11131 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
11132 || mData->mMachineState == MachineState_AbortedSaved);
11133/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
11134 }
11135
11136 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
11137 }
11138 catch (...)
11139 {
11140 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
11141 }
11142
11143 return rc;
11144}
11145
11146/**
11147 * Ensures that the given medium is added to a media registry. If this machine
11148 * was created with 4.0 or later, then the machine registry is used. Otherwise
11149 * the global VirtualBox media registry is used.
11150 *
11151 * Caller must NOT hold machine lock, media tree or any medium locks!
11152 *
11153 * @param pMedium
11154 */
11155void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
11156{
11157 /* Paranoia checks: do not hold machine or media tree locks. */
11158 AssertReturnVoid(!isWriteLockOnCurrentThread());
11159 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
11160
11161 ComObjPtr<Medium> pBase;
11162 {
11163 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11164 pBase = pMedium->i_getBase();
11165 }
11166
11167 /* Paranoia checks: do not hold medium locks. */
11168 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
11169 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
11170
11171 // decide which medium registry to use now that the medium is attached:
11172 Guid uuid;
11173 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
11174 if (fCanHaveOwnMediaRegistry)
11175 // machine XML is VirtualBox 4.0 or higher:
11176 uuid = i_getId(); // machine UUID
11177 else
11178 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
11179
11180 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
11181 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11182 if (pMedium->i_addRegistry(uuid))
11183 mParent->i_markRegistryModified(uuid);
11184
11185 /* For more complex hard disk structures it can happen that the base
11186 * medium isn't yet associated with any medium registry. Do that now. */
11187 if (pMedium != pBase)
11188 {
11189 /* Tree lock needed by Medium::addRegistryAll. */
11190 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11191 if (fCanHaveOwnMediaRegistry && pBase->i_removeRegistryAll(mParent->i_getGlobalRegistryId()))
11192 {
11193 treeLock.release();
11194 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11195 treeLock.acquire();
11196 }
11197 if (pBase->i_addRegistryAll(uuid))
11198 {
11199 treeLock.release();
11200 mParent->i_markRegistryModified(uuid);
11201 }
11202 }
11203}
11204
11205/**
11206 * Physically deletes a file belonging to a machine.
11207 *
11208 * @returns HRESULT
11209 * @retval VBOX_E_FILE_ERROR on failure.
11210 * @param strFile File to delete.
11211 * @param fIgnoreFailures Whether to ignore deletion failures. Defaults to \c false.
11212 * VERR_FILE_NOT_FOUND and VERR_PATH_NOT_FOUND always will be ignored.
11213 * @param strWhat File hint which will be used when setting an error. Optional.
11214 * @param prc Where to return IPRT's error code on failure. Optional and can be NULL.
11215 */
11216HRESULT Machine::i_deleteFile(const Utf8Str &strFile, bool fIgnoreFailures /* = false */,
11217 const Utf8Str &strWhat /* = "" */, int *prc /* = NULL */)
11218{
11219 AssertReturn(strFile.isNotEmpty(), E_INVALIDARG);
11220
11221 HRESULT hrc = S_OK;
11222
11223 LogFunc(("Deleting file '%s'\n", strFile.c_str()));
11224
11225 int vrc = RTFileDelete(strFile.c_str());
11226 if (RT_FAILURE(vrc))
11227 {
11228 if ( !fIgnoreFailures
11229 /* Don't (externally) bitch about stuff which doesn't exist. */
11230 && ( vrc != VERR_FILE_NOT_FOUND
11231 && vrc != VERR_PATH_NOT_FOUND
11232 )
11233 )
11234 {
11235 LogRel(("Deleting file '%s' failed: %Rrc\n", strFile.c_str(), vrc));
11236
11237 Utf8StrFmt strError("Error deleting %s '%s' (%Rrc)",
11238 strWhat.isEmpty() ? tr("file") : strWhat.c_str(), strFile.c_str(), vrc);
11239 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, strError.c_str(),
11240 strFile.c_str(), vrc);
11241 }
11242
11243 if (prc)
11244 *prc = vrc;
11245 }
11246
11247 return hrc;
11248}
11249
11250/**
11251 * Creates differencing hard disks for all normal hard disks attached to this
11252 * machine and a new set of attachments to refer to created disks.
11253 *
11254 * Used when taking a snapshot or when deleting the current state. Gets called
11255 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
11256 *
11257 * This method assumes that mMediumAttachments contains the original hard disk
11258 * attachments it needs to create diffs for. On success, these attachments will
11259 * be replaced with the created diffs.
11260 *
11261 * Attachments with non-normal hard disks are left as is.
11262 *
11263 * If @a aOnline is @c false then the original hard disks that require implicit
11264 * diffs will be locked for reading. Otherwise it is assumed that they are
11265 * already locked for writing (when the VM was started). Note that in the latter
11266 * case it is responsibility of the caller to lock the newly created diffs for
11267 * writing if this method succeeds.
11268 *
11269 * @param aProgress Progress object to run (must contain at least as
11270 * many operations left as the number of hard disks
11271 * attached).
11272 * @param aWeight Weight of this operation.
11273 * @param aOnline Whether the VM was online prior to this operation.
11274 *
11275 * @note The progress object is not marked as completed, neither on success nor
11276 * on failure. This is a responsibility of the caller.
11277 *
11278 * @note Locks this object and the media tree for writing.
11279 */
11280HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
11281 ULONG aWeight,
11282 bool aOnline)
11283{
11284 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11285
11286 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
11287 AssertReturn(!!pProgressControl, E_INVALIDARG);
11288
11289 AutoCaller autoCaller(this);
11290 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11291
11292 AutoMultiWriteLock2 alock(this->lockHandle(),
11293 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11294
11295 /* must be in a protective state because we release the lock below */
11296 AssertReturn( mData->mMachineState == MachineState_Snapshotting
11297 || mData->mMachineState == MachineState_OnlineSnapshotting
11298 || mData->mMachineState == MachineState_LiveSnapshotting
11299 || mData->mMachineState == MachineState_RestoringSnapshot
11300 || mData->mMachineState == MachineState_DeletingSnapshot
11301 , E_FAIL);
11302
11303 HRESULT rc = S_OK;
11304
11305 // use appropriate locked media map (online or offline)
11306 MediumLockListMap lockedMediaOffline;
11307 MediumLockListMap *lockedMediaMap;
11308 if (aOnline)
11309 lockedMediaMap = &mData->mSession.mLockedMedia;
11310 else
11311 lockedMediaMap = &lockedMediaOffline;
11312
11313 try
11314 {
11315 if (!aOnline)
11316 {
11317 /* lock all attached hard disks early to detect "in use"
11318 * situations before creating actual diffs */
11319 for (MediumAttachmentList::const_iterator
11320 it = mMediumAttachments->begin();
11321 it != mMediumAttachments->end();
11322 ++it)
11323 {
11324 MediumAttachment *pAtt = *it;
11325 if (pAtt->i_getType() == DeviceType_HardDisk)
11326 {
11327 Medium *pMedium = pAtt->i_getMedium();
11328 Assert(pMedium);
11329
11330 MediumLockList *pMediumLockList(new MediumLockList());
11331 alock.release();
11332 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11333 NULL /* pToLockWrite */,
11334 false /* fMediumLockWriteAll */,
11335 NULL,
11336 *pMediumLockList);
11337 alock.acquire();
11338 if (FAILED(rc))
11339 {
11340 delete pMediumLockList;
11341 throw rc;
11342 }
11343 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11344 if (FAILED(rc))
11345 {
11346 throw setError(rc,
11347 tr("Collecting locking information for all attached media failed"));
11348 }
11349 }
11350 }
11351
11352 /* Now lock all media. If this fails, nothing is locked. */
11353 alock.release();
11354 rc = lockedMediaMap->Lock();
11355 alock.acquire();
11356 if (FAILED(rc))
11357 {
11358 throw setError(rc,
11359 tr("Locking of attached media failed"));
11360 }
11361 }
11362
11363 /* remember the current list (note that we don't use backup() since
11364 * mMediumAttachments may be already backed up) */
11365 MediumAttachmentList atts = *mMediumAttachments.data();
11366
11367 /* start from scratch */
11368 mMediumAttachments->clear();
11369
11370 /* go through remembered attachments and create diffs for normal hard
11371 * disks and attach them */
11372 for (MediumAttachmentList::const_iterator
11373 it = atts.begin();
11374 it != atts.end();
11375 ++it)
11376 {
11377 MediumAttachment *pAtt = *it;
11378
11379 DeviceType_T devType = pAtt->i_getType();
11380 Medium *pMedium = pAtt->i_getMedium();
11381
11382 if ( devType != DeviceType_HardDisk
11383 || pMedium == NULL
11384 || pMedium->i_getType() != MediumType_Normal)
11385 {
11386 /* copy the attachment as is */
11387
11388 /** @todo the progress object created in SessionMachine::TakeSnaphot
11389 * only expects operations for hard disks. Later other
11390 * device types need to show up in the progress as well. */
11391 if (devType == DeviceType_HardDisk)
11392 {
11393 if (pMedium == NULL)
11394 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11395 aWeight); // weight
11396 else
11397 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11398 pMedium->i_getBase()->i_getName().c_str()).raw(),
11399 aWeight); // weight
11400 }
11401
11402 mMediumAttachments->push_back(pAtt);
11403 continue;
11404 }
11405
11406 /* need a diff */
11407 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11408 pMedium->i_getBase()->i_getName().c_str()).raw(),
11409 aWeight); // weight
11410
11411 Utf8Str strFullSnapshotFolder;
11412 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11413
11414 ComObjPtr<Medium> diff;
11415 diff.createObject();
11416 // store the diff in the same registry as the parent
11417 // (this cannot fail here because we can't create implicit diffs for
11418 // unregistered images)
11419 Guid uuidRegistryParent;
11420 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11421 Assert(fInRegistry); NOREF(fInRegistry);
11422 rc = diff->init(mParent,
11423 pMedium->i_getPreferredDiffFormat(),
11424 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11425 uuidRegistryParent,
11426 DeviceType_HardDisk);
11427 if (FAILED(rc)) throw rc;
11428
11429 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11430 * the push_back? Looks like we're going to release medium with the
11431 * wrong kind of lock (general issue with if we fail anywhere at all)
11432 * and an orphaned VDI in the snapshots folder. */
11433
11434 /* update the appropriate lock list */
11435 MediumLockList *pMediumLockList;
11436 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11437 AssertComRCThrowRC(rc);
11438 if (aOnline)
11439 {
11440 alock.release();
11441 /* The currently attached medium will be read-only, change
11442 * the lock type to read. */
11443 rc = pMediumLockList->Update(pMedium, false);
11444 alock.acquire();
11445 AssertComRCThrowRC(rc);
11446 }
11447
11448 /* release the locks before the potentially lengthy operation */
11449 alock.release();
11450 rc = pMedium->i_createDiffStorage(diff,
11451 pMedium->i_getPreferredDiffVariant(),
11452 pMediumLockList,
11453 NULL /* aProgress */,
11454 true /* aWait */,
11455 false /* aNotify */);
11456 alock.acquire();
11457 if (FAILED(rc)) throw rc;
11458
11459 /* actual lock list update is done in Machine::i_commitMedia */
11460
11461 rc = diff->i_addBackReference(mData->mUuid);
11462 AssertComRCThrowRC(rc);
11463
11464 /* add a new attachment */
11465 ComObjPtr<MediumAttachment> attachment;
11466 attachment.createObject();
11467 rc = attachment->init(this,
11468 diff,
11469 pAtt->i_getControllerName(),
11470 pAtt->i_getPort(),
11471 pAtt->i_getDevice(),
11472 DeviceType_HardDisk,
11473 true /* aImplicit */,
11474 false /* aPassthrough */,
11475 false /* aTempEject */,
11476 pAtt->i_getNonRotational(),
11477 pAtt->i_getDiscard(),
11478 pAtt->i_getHotPluggable(),
11479 pAtt->i_getBandwidthGroup());
11480 if (FAILED(rc)) throw rc;
11481
11482 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11483 AssertComRCThrowRC(rc);
11484 mMediumAttachments->push_back(attachment);
11485 }
11486 }
11487 catch (HRESULT aRC) { rc = aRC; }
11488
11489 /* unlock all hard disks we locked when there is no VM */
11490 if (!aOnline)
11491 {
11492 ErrorInfoKeeper eik;
11493
11494 HRESULT rc1 = lockedMediaMap->Clear();
11495 AssertComRC(rc1);
11496 }
11497
11498 return rc;
11499}
11500
11501/**
11502 * Deletes implicit differencing hard disks created either by
11503 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11504 * mMediumAttachments.
11505 *
11506 * Note that to delete hard disks created by #attachDevice() this method is
11507 * called from #i_rollbackMedia() when the changes are rolled back.
11508 *
11509 * @note Locks this object and the media tree for writing.
11510 */
11511HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11512{
11513 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11514
11515 AutoCaller autoCaller(this);
11516 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11517
11518 AutoMultiWriteLock2 alock(this->lockHandle(),
11519 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11520
11521 /* We absolutely must have backed up state. */
11522 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11523
11524 /* Check if there are any implicitly created diff images. */
11525 bool fImplicitDiffs = false;
11526 for (MediumAttachmentList::const_iterator
11527 it = mMediumAttachments->begin();
11528 it != mMediumAttachments->end();
11529 ++it)
11530 {
11531 const ComObjPtr<MediumAttachment> &pAtt = *it;
11532 if (pAtt->i_isImplicit())
11533 {
11534 fImplicitDiffs = true;
11535 break;
11536 }
11537 }
11538 /* If there is nothing to do, leave early. This saves lots of image locking
11539 * effort. It also avoids a MachineStateChanged event without real reason.
11540 * This is important e.g. when loading a VM config, because there should be
11541 * no events. Otherwise API clients can become thoroughly confused for
11542 * inaccessible VMs (the code for loading VM configs uses this method for
11543 * cleanup if the config makes no sense), as they take such events as an
11544 * indication that the VM is alive, and they would force the VM config to
11545 * be reread, leading to an endless loop. */
11546 if (!fImplicitDiffs)
11547 return S_OK;
11548
11549 HRESULT rc = S_OK;
11550 MachineState_T oldState = mData->mMachineState;
11551
11552 /* will release the lock before the potentially lengthy operation,
11553 * so protect with the special state (unless already protected) */
11554 if ( oldState != MachineState_Snapshotting
11555 && oldState != MachineState_OnlineSnapshotting
11556 && oldState != MachineState_LiveSnapshotting
11557 && oldState != MachineState_RestoringSnapshot
11558 && oldState != MachineState_DeletingSnapshot
11559 && oldState != MachineState_DeletingSnapshotOnline
11560 && oldState != MachineState_DeletingSnapshotPaused
11561 )
11562 i_setMachineState(MachineState_SettingUp);
11563
11564 // use appropriate locked media map (online or offline)
11565 MediumLockListMap lockedMediaOffline;
11566 MediumLockListMap *lockedMediaMap;
11567 if (aOnline)
11568 lockedMediaMap = &mData->mSession.mLockedMedia;
11569 else
11570 lockedMediaMap = &lockedMediaOffline;
11571
11572 try
11573 {
11574 if (!aOnline)
11575 {
11576 /* lock all attached hard disks early to detect "in use"
11577 * situations before deleting actual diffs */
11578 for (MediumAttachmentList::const_iterator
11579 it = mMediumAttachments->begin();
11580 it != mMediumAttachments->end();
11581 ++it)
11582 {
11583 MediumAttachment *pAtt = *it;
11584 if (pAtt->i_getType() == DeviceType_HardDisk)
11585 {
11586 Medium *pMedium = pAtt->i_getMedium();
11587 Assert(pMedium);
11588
11589 MediumLockList *pMediumLockList(new MediumLockList());
11590 alock.release();
11591 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11592 NULL /* pToLockWrite */,
11593 false /* fMediumLockWriteAll */,
11594 NULL,
11595 *pMediumLockList);
11596 alock.acquire();
11597
11598 if (FAILED(rc))
11599 {
11600 delete pMediumLockList;
11601 throw rc;
11602 }
11603
11604 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11605 if (FAILED(rc))
11606 throw rc;
11607 }
11608 }
11609
11610 if (FAILED(rc))
11611 throw rc;
11612 } // end of offline
11613
11614 /* Lock lists are now up to date and include implicitly created media */
11615
11616 /* Go through remembered attachments and delete all implicitly created
11617 * diffs and fix up the attachment information */
11618 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11619 MediumAttachmentList implicitAtts;
11620 for (MediumAttachmentList::const_iterator
11621 it = mMediumAttachments->begin();
11622 it != mMediumAttachments->end();
11623 ++it)
11624 {
11625 ComObjPtr<MediumAttachment> pAtt = *it;
11626 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11627 if (pMedium.isNull())
11628 continue;
11629
11630 // Implicit attachments go on the list for deletion and back references are removed.
11631 if (pAtt->i_isImplicit())
11632 {
11633 /* Deassociate and mark for deletion */
11634 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11635 rc = pMedium->i_removeBackReference(mData->mUuid);
11636 if (FAILED(rc))
11637 throw rc;
11638 implicitAtts.push_back(pAtt);
11639 continue;
11640 }
11641
11642 /* Was this medium attached before? */
11643 if (!i_findAttachment(oldAtts, pMedium))
11644 {
11645 /* no: de-associate */
11646 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11647 rc = pMedium->i_removeBackReference(mData->mUuid);
11648 if (FAILED(rc))
11649 throw rc;
11650 continue;
11651 }
11652 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11653 }
11654
11655 /* If there are implicit attachments to delete, throw away the lock
11656 * map contents (which will unlock all media) since the medium
11657 * attachments will be rolled back. Below we need to completely
11658 * recreate the lock map anyway since it is infinitely complex to
11659 * do this incrementally (would need reconstructing each attachment
11660 * change, which would be extremely hairy). */
11661 if (implicitAtts.size() != 0)
11662 {
11663 ErrorInfoKeeper eik;
11664
11665 HRESULT rc1 = lockedMediaMap->Clear();
11666 AssertComRC(rc1);
11667 }
11668
11669 /* rollback hard disk changes */
11670 mMediumAttachments.rollback();
11671
11672 MultiResult mrc(S_OK);
11673
11674 // Delete unused implicit diffs.
11675 if (implicitAtts.size() != 0)
11676 {
11677 alock.release();
11678
11679 for (MediumAttachmentList::const_iterator
11680 it = implicitAtts.begin();
11681 it != implicitAtts.end();
11682 ++it)
11683 {
11684 // Remove medium associated with this attachment.
11685 ComObjPtr<MediumAttachment> pAtt = *it;
11686 Assert(pAtt);
11687 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11688 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11689 Assert(pMedium);
11690
11691 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11692 // continue on delete failure, just collect error messages
11693 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11694 pMedium->i_getLocationFull().c_str() ));
11695 mrc = rc;
11696 }
11697 // Clear the list of deleted implicit attachments now, while not
11698 // holding the lock, as it will ultimately trigger Medium::uninit()
11699 // calls which assume that the media tree lock isn't held.
11700 implicitAtts.clear();
11701
11702 alock.acquire();
11703
11704 /* if there is a VM recreate media lock map as mentioned above,
11705 * otherwise it is a waste of time and we leave things unlocked */
11706 if (aOnline)
11707 {
11708 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11709 /* must never be NULL, but better safe than sorry */
11710 if (!pMachine.isNull())
11711 {
11712 alock.release();
11713 rc = mData->mSession.mMachine->i_lockMedia();
11714 alock.acquire();
11715 if (FAILED(rc))
11716 throw rc;
11717 }
11718 }
11719 }
11720 }
11721 catch (HRESULT aRC) {rc = aRC;}
11722
11723 if (mData->mMachineState == MachineState_SettingUp)
11724 i_setMachineState(oldState);
11725
11726 /* unlock all hard disks we locked when there is no VM */
11727 if (!aOnline)
11728 {
11729 ErrorInfoKeeper eik;
11730
11731 HRESULT rc1 = lockedMediaMap->Clear();
11732 AssertComRC(rc1);
11733 }
11734
11735 return rc;
11736}
11737
11738
11739/**
11740 * Looks through the given list of media attachments for one with the given parameters
11741 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11742 * can be searched as well if needed.
11743 *
11744 * @param ll
11745 * @param aControllerName
11746 * @param aControllerPort
11747 * @param aDevice
11748 * @return
11749 */
11750MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11751 const Utf8Str &aControllerName,
11752 LONG aControllerPort,
11753 LONG aDevice)
11754{
11755 for (MediumAttachmentList::const_iterator
11756 it = ll.begin();
11757 it != ll.end();
11758 ++it)
11759 {
11760 MediumAttachment *pAttach = *it;
11761 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11762 return pAttach;
11763 }
11764
11765 return NULL;
11766}
11767
11768/**
11769 * Looks through the given list of media attachments for one with the given parameters
11770 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11771 * can be searched as well if needed.
11772 *
11773 * @param ll
11774 * @param pMedium
11775 * @return
11776 */
11777MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11778 ComObjPtr<Medium> pMedium)
11779{
11780 for (MediumAttachmentList::const_iterator
11781 it = ll.begin();
11782 it != ll.end();
11783 ++it)
11784 {
11785 MediumAttachment *pAttach = *it;
11786 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11787 if (pMediumThis == pMedium)
11788 return pAttach;
11789 }
11790
11791 return NULL;
11792}
11793
11794/**
11795 * Looks through the given list of media attachments for one with the given parameters
11796 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11797 * can be searched as well if needed.
11798 *
11799 * @param ll
11800 * @param id
11801 * @return
11802 */
11803MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11804 Guid &id)
11805{
11806 for (MediumAttachmentList::const_iterator
11807 it = ll.begin();
11808 it != ll.end();
11809 ++it)
11810 {
11811 MediumAttachment *pAttach = *it;
11812 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11813 if (pMediumThis->i_getId() == id)
11814 return pAttach;
11815 }
11816
11817 return NULL;
11818}
11819
11820/**
11821 * Main implementation for Machine::DetachDevice. This also gets called
11822 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11823 *
11824 * @param pAttach Medium attachment to detach.
11825 * @param writeLock Machine write lock which the caller must have locked once.
11826 * This may be released temporarily in here.
11827 * @param pSnapshot If NULL, then the detachment is for the current machine.
11828 * Otherwise this is for a SnapshotMachine, and this must be
11829 * its snapshot.
11830 * @return
11831 */
11832HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11833 AutoWriteLock &writeLock,
11834 Snapshot *pSnapshot)
11835{
11836 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11837 DeviceType_T mediumType = pAttach->i_getType();
11838
11839 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11840
11841 if (pAttach->i_isImplicit())
11842 {
11843 /* attempt to implicitly delete the implicitly created diff */
11844
11845 /// @todo move the implicit flag from MediumAttachment to Medium
11846 /// and forbid any hard disk operation when it is implicit. Or maybe
11847 /// a special media state for it to make it even more simple.
11848
11849 Assert(mMediumAttachments.isBackedUp());
11850
11851 /* will release the lock before the potentially lengthy operation, so
11852 * protect with the special state */
11853 MachineState_T oldState = mData->mMachineState;
11854 i_setMachineState(MachineState_SettingUp);
11855
11856 writeLock.release();
11857
11858 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11859 true /*aWait*/,
11860 false /*aNotify*/);
11861
11862 writeLock.acquire();
11863
11864 i_setMachineState(oldState);
11865
11866 if (FAILED(rc)) return rc;
11867 }
11868
11869 i_setModified(IsModified_Storage);
11870 mMediumAttachments.backup();
11871 mMediumAttachments->remove(pAttach);
11872
11873 if (!oldmedium.isNull())
11874 {
11875 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11876 if (pSnapshot)
11877 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11878 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11879 else if (mediumType != DeviceType_HardDisk)
11880 oldmedium->i_removeBackReference(mData->mUuid);
11881 }
11882
11883 return S_OK;
11884}
11885
11886/**
11887 * Goes thru all media of the given list and
11888 *
11889 * 1) calls i_detachDevice() on each of them for this machine and
11890 * 2) adds all Medium objects found in the process to the given list,
11891 * depending on cleanupMode.
11892 *
11893 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11894 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11895 * media to the list.
11896 * CleanupMode_DetachAllReturnHardDisksAndVMRemovable adds hard disk and
11897 * also removable medias if they are located in the VM folder and referenced
11898 * only by this VM (media prepared by unattended installer).
11899 *
11900 * This gets called from Machine::Unregister, both for the actual Machine and
11901 * the SnapshotMachine objects that might be found in the snapshots.
11902 *
11903 * Requires caller and locking. The machine lock must be passed in because it
11904 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11905 *
11906 * @param writeLock Machine lock from top-level caller; this gets passed to
11907 * i_detachDevice.
11908 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11909 * object if called for a SnapshotMachine.
11910 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11911 * added to llMedia; if Full, then all media get added;
11912 * otherwise no media get added.
11913 * @param llMedia Caller's list to receive Medium objects which got detached so
11914 * caller can close() them, depending on cleanupMode.
11915 * @return
11916 */
11917HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11918 Snapshot *pSnapshot,
11919 CleanupMode_T cleanupMode,
11920 MediaList &llMedia)
11921{
11922 Assert(isWriteLockOnCurrentThread());
11923
11924 HRESULT rc;
11925
11926 // make a temporary list because i_detachDevice invalidates iterators into
11927 // mMediumAttachments
11928 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11929
11930 for (MediumAttachmentList::iterator
11931 it = llAttachments2.begin();
11932 it != llAttachments2.end();
11933 ++it)
11934 {
11935 ComObjPtr<MediumAttachment> &pAttach = *it;
11936 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11937
11938 if (!pMedium.isNull())
11939 {
11940 AutoCaller mac(pMedium);
11941 if (FAILED(mac.rc())) return mac.rc();
11942 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11943 DeviceType_T devType = pMedium->i_getDeviceType();
11944 size_t cBackRefs = pMedium->i_getMachineBackRefCount();
11945 Utf8Str strMediumLocation = pMedium->i_getLocationFull();
11946 strMediumLocation.stripFilename();
11947 Utf8Str strMachineFolder = i_getSettingsFileFull();
11948 strMachineFolder.stripFilename();
11949 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11950 && devType == DeviceType_HardDisk)
11951 || ( cleanupMode == CleanupMode_DetachAllReturnHardDisksAndVMRemovable
11952 && ( devType == DeviceType_HardDisk
11953 || ( cBackRefs <= 1
11954 && strMediumLocation == strMachineFolder
11955 && *pMedium->i_getFirstMachineBackrefId() == i_getId())))
11956 || (cleanupMode == CleanupMode_Full)
11957 )
11958 {
11959 llMedia.push_back(pMedium);
11960 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11961 /* Not allowed to keep this lock as below we need the parent
11962 * medium lock, and the lock order is parent to child. */
11963 lock.release();
11964 /*
11965 * Search for medias which are not attached to any machine, but
11966 * in the chain to an attached disk. Mediums are only consided
11967 * if they are:
11968 * - have only one child
11969 * - no references to any machines
11970 * - are of normal medium type
11971 */
11972 while (!pParent.isNull())
11973 {
11974 AutoCaller mac1(pParent);
11975 if (FAILED(mac1.rc())) return mac1.rc();
11976 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11977 if (pParent->i_getChildren().size() == 1)
11978 {
11979 if ( pParent->i_getMachineBackRefCount() == 0
11980 && pParent->i_getType() == MediumType_Normal
11981 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11982 llMedia.push_back(pParent);
11983 }
11984 else
11985 break;
11986 pParent = pParent->i_getParent();
11987 }
11988 }
11989 }
11990
11991 // real machine: then we need to use the proper method
11992 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11993
11994 if (FAILED(rc))
11995 return rc;
11996 }
11997
11998 return S_OK;
11999}
12000
12001/**
12002 * Perform deferred hard disk detachments.
12003 *
12004 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
12005 * changed (not backed up).
12006 *
12007 * If @a aOnline is @c true then this method will also unlock the old hard
12008 * disks for which the new implicit diffs were created and will lock these new
12009 * diffs for writing.
12010 *
12011 * @param aOnline Whether the VM was online prior to this operation.
12012 *
12013 * @note Locks this object for writing!
12014 */
12015void Machine::i_commitMedia(bool aOnline /*= false*/)
12016{
12017 AutoCaller autoCaller(this);
12018 AssertComRCReturnVoid(autoCaller.rc());
12019
12020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12021
12022 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
12023
12024 HRESULT rc = S_OK;
12025
12026 /* no attach/detach operations -- nothing to do */
12027 if (!mMediumAttachments.isBackedUp())
12028 return;
12029
12030 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
12031 bool fMediaNeedsLocking = false;
12032
12033 /* enumerate new attachments */
12034 for (MediumAttachmentList::const_iterator
12035 it = mMediumAttachments->begin();
12036 it != mMediumAttachments->end();
12037 ++it)
12038 {
12039 MediumAttachment *pAttach = *it;
12040
12041 pAttach->i_commit();
12042
12043 Medium *pMedium = pAttach->i_getMedium();
12044 bool fImplicit = pAttach->i_isImplicit();
12045
12046 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
12047 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
12048 fImplicit));
12049
12050 /** @todo convert all this Machine-based voodoo to MediumAttachment
12051 * based commit logic. */
12052 if (fImplicit)
12053 {
12054 /* convert implicit attachment to normal */
12055 pAttach->i_setImplicit(false);
12056
12057 if ( aOnline
12058 && pMedium
12059 && pAttach->i_getType() == DeviceType_HardDisk
12060 )
12061 {
12062 /* update the appropriate lock list */
12063 MediumLockList *pMediumLockList;
12064 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
12065 AssertComRC(rc);
12066 if (pMediumLockList)
12067 {
12068 /* unlock if there's a need to change the locking */
12069 if (!fMediaNeedsLocking)
12070 {
12071 rc = mData->mSession.mLockedMedia.Unlock();
12072 AssertComRC(rc);
12073 fMediaNeedsLocking = true;
12074 }
12075 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
12076 AssertComRC(rc);
12077 rc = pMediumLockList->Append(pMedium, true);
12078 AssertComRC(rc);
12079 }
12080 }
12081
12082 continue;
12083 }
12084
12085 if (pMedium)
12086 {
12087 /* was this medium attached before? */
12088 for (MediumAttachmentList::iterator
12089 oldIt = oldAtts.begin();
12090 oldIt != oldAtts.end();
12091 ++oldIt)
12092 {
12093 MediumAttachment *pOldAttach = *oldIt;
12094 if (pOldAttach->i_getMedium() == pMedium)
12095 {
12096 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
12097
12098 /* yes: remove from old to avoid de-association */
12099 oldAtts.erase(oldIt);
12100 break;
12101 }
12102 }
12103 }
12104 }
12105
12106 /* enumerate remaining old attachments and de-associate from the
12107 * current machine state */
12108 for (MediumAttachmentList::const_iterator
12109 it = oldAtts.begin();
12110 it != oldAtts.end();
12111 ++it)
12112 {
12113 MediumAttachment *pAttach = *it;
12114 Medium *pMedium = pAttach->i_getMedium();
12115
12116 /* Detach only hard disks, since DVD/floppy media is detached
12117 * instantly in MountMedium. */
12118 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
12119 {
12120 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
12121
12122 /* now de-associate from the current machine state */
12123 rc = pMedium->i_removeBackReference(mData->mUuid);
12124 AssertComRC(rc);
12125
12126 if (aOnline)
12127 {
12128 /* unlock since medium is not used anymore */
12129 MediumLockList *pMediumLockList;
12130 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
12131 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
12132 {
12133 /* this happens for online snapshots, there the attachment
12134 * is changing, but only to a diff image created under
12135 * the old one, so there is no separate lock list */
12136 Assert(!pMediumLockList);
12137 }
12138 else
12139 {
12140 AssertComRC(rc);
12141 if (pMediumLockList)
12142 {
12143 rc = mData->mSession.mLockedMedia.Remove(pAttach);
12144 AssertComRC(rc);
12145 }
12146 }
12147 }
12148 }
12149 }
12150
12151 /* take media locks again so that the locking state is consistent */
12152 if (fMediaNeedsLocking)
12153 {
12154 Assert(aOnline);
12155 rc = mData->mSession.mLockedMedia.Lock();
12156 AssertComRC(rc);
12157 }
12158
12159 /* commit the hard disk changes */
12160 mMediumAttachments.commit();
12161
12162 if (i_isSessionMachine())
12163 {
12164 /*
12165 * Update the parent machine to point to the new owner.
12166 * This is necessary because the stored parent will point to the
12167 * session machine otherwise and cause crashes or errors later
12168 * when the session machine gets invalid.
12169 */
12170 /** @todo Change the MediumAttachment class to behave like any other
12171 * class in this regard by creating peer MediumAttachment
12172 * objects for session machines and share the data with the peer
12173 * machine.
12174 */
12175 for (MediumAttachmentList::const_iterator
12176 it = mMediumAttachments->begin();
12177 it != mMediumAttachments->end();
12178 ++it)
12179 (*it)->i_updateParentMachine(mPeer);
12180
12181 /* attach new data to the primary machine and reshare it */
12182 mPeer->mMediumAttachments.attach(mMediumAttachments);
12183 }
12184
12185 return;
12186}
12187
12188/**
12189 * Perform deferred deletion of implicitly created diffs.
12190 *
12191 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
12192 * changed (not backed up).
12193 *
12194 * @note Locks this object for writing!
12195 */
12196void Machine::i_rollbackMedia()
12197{
12198 AutoCaller autoCaller(this);
12199 AssertComRCReturnVoid(autoCaller.rc());
12200
12201 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12202 LogFlowThisFunc(("Entering rollbackMedia\n"));
12203
12204 HRESULT rc = S_OK;
12205
12206 /* no attach/detach operations -- nothing to do */
12207 if (!mMediumAttachments.isBackedUp())
12208 return;
12209
12210 /* enumerate new attachments */
12211 for (MediumAttachmentList::const_iterator
12212 it = mMediumAttachments->begin();
12213 it != mMediumAttachments->end();
12214 ++it)
12215 {
12216 MediumAttachment *pAttach = *it;
12217 /* Fix up the backrefs for DVD/floppy media. */
12218 if (pAttach->i_getType() != DeviceType_HardDisk)
12219 {
12220 Medium *pMedium = pAttach->i_getMedium();
12221 if (pMedium)
12222 {
12223 rc = pMedium->i_removeBackReference(mData->mUuid);
12224 AssertComRC(rc);
12225 }
12226 }
12227
12228 (*it)->i_rollback();
12229
12230 pAttach = *it;
12231 /* Fix up the backrefs for DVD/floppy media. */
12232 if (pAttach->i_getType() != DeviceType_HardDisk)
12233 {
12234 Medium *pMedium = pAttach->i_getMedium();
12235 if (pMedium)
12236 {
12237 rc = pMedium->i_addBackReference(mData->mUuid);
12238 AssertComRC(rc);
12239 }
12240 }
12241 }
12242
12243 /** @todo convert all this Machine-based voodoo to MediumAttachment
12244 * based rollback logic. */
12245 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
12246
12247 return;
12248}
12249
12250/**
12251 * Returns true if the settings file is located in the directory named exactly
12252 * as the machine; this means, among other things, that the machine directory
12253 * should be auto-renamed.
12254 *
12255 * @param aSettingsDir if not NULL, the full machine settings file directory
12256 * name will be assigned there.
12257 *
12258 * @note Doesn't lock anything.
12259 * @note Not thread safe (must be called from this object's lock).
12260 */
12261bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
12262{
12263 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12264 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
12265 if (aSettingsDir)
12266 *aSettingsDir = strMachineDirName;
12267 strMachineDirName.stripPath(); // vmname
12268 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12269 strConfigFileOnly.stripPath() // vmname.vbox
12270 .stripSuffix(); // vmname
12271 /** @todo hack, make somehow use of ComposeMachineFilename */
12272 if (mUserData->s.fDirectoryIncludesUUID)
12273 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
12274
12275 AssertReturn(!strMachineDirName.isEmpty(), false);
12276 AssertReturn(!strConfigFileOnly.isEmpty(), false);
12277
12278 return strMachineDirName == strConfigFileOnly;
12279}
12280
12281/**
12282 * Discards all changes to machine settings.
12283 *
12284 * @param aNotify Whether to notify the direct session about changes or not.
12285 *
12286 * @note Locks objects for writing!
12287 */
12288void Machine::i_rollback(bool aNotify)
12289{
12290 AutoCaller autoCaller(this);
12291 AssertComRCReturn(autoCaller.rc(), (void)0);
12292
12293 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12294
12295 if (!mStorageControllers.isNull())
12296 {
12297 if (mStorageControllers.isBackedUp())
12298 {
12299 /* unitialize all new devices (absent in the backed up list). */
12300 StorageControllerList *backedList = mStorageControllers.backedUpData();
12301 for (StorageControllerList::const_iterator
12302 it = mStorageControllers->begin();
12303 it != mStorageControllers->end();
12304 ++it)
12305 {
12306 if ( std::find(backedList->begin(), backedList->end(), *it)
12307 == backedList->end()
12308 )
12309 {
12310 (*it)->uninit();
12311 }
12312 }
12313
12314 /* restore the list */
12315 mStorageControllers.rollback();
12316 }
12317
12318 /* rollback any changes to devices after restoring the list */
12319 if (mData->flModifications & IsModified_Storage)
12320 {
12321 for (StorageControllerList::const_iterator
12322 it = mStorageControllers->begin();
12323 it != mStorageControllers->end();
12324 ++it)
12325 {
12326 (*it)->i_rollback();
12327 }
12328 }
12329 }
12330
12331 if (!mUSBControllers.isNull())
12332 {
12333 if (mUSBControllers.isBackedUp())
12334 {
12335 /* unitialize all new devices (absent in the backed up list). */
12336 USBControllerList *backedList = mUSBControllers.backedUpData();
12337 for (USBControllerList::const_iterator
12338 it = mUSBControllers->begin();
12339 it != mUSBControllers->end();
12340 ++it)
12341 {
12342 if ( std::find(backedList->begin(), backedList->end(), *it)
12343 == backedList->end()
12344 )
12345 {
12346 (*it)->uninit();
12347 }
12348 }
12349
12350 /* restore the list */
12351 mUSBControllers.rollback();
12352 }
12353
12354 /* rollback any changes to devices after restoring the list */
12355 if (mData->flModifications & IsModified_USB)
12356 {
12357 for (USBControllerList::const_iterator
12358 it = mUSBControllers->begin();
12359 it != mUSBControllers->end();
12360 ++it)
12361 {
12362 (*it)->i_rollback();
12363 }
12364 }
12365 }
12366
12367 mUserData.rollback();
12368
12369 mHWData.rollback();
12370
12371 if (mData->flModifications & IsModified_Storage)
12372 i_rollbackMedia();
12373
12374 if (mBIOSSettings)
12375 mBIOSSettings->i_rollback();
12376
12377 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
12378 mRecordingSettings->i_rollback();
12379
12380 if (mTrustedPlatformModule)
12381 mTrustedPlatformModule->i_rollback();
12382
12383 if (mNvramStore)
12384 mNvramStore->i_rollback();
12385
12386 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
12387 mGraphicsAdapter->i_rollback();
12388
12389 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12390 mVRDEServer->i_rollback();
12391
12392 if (mAudioSettings && (mData->flModifications & IsModified_AudioSettings))
12393 mAudioSettings->i_rollback();
12394
12395 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12396 mUSBDeviceFilters->i_rollback();
12397
12398 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12399 mBandwidthControl->i_rollback();
12400
12401 if (mGuestDebugControl && (mData->flModifications & IsModified_GuestDebugControl))
12402 mGuestDebugControl->i_rollback();
12403
12404 if (!mHWData.isNull())
12405 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12406 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12407 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12408 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12409
12410 if (mData->flModifications & IsModified_NetworkAdapters)
12411 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12412 if ( mNetworkAdapters[slot]
12413 && mNetworkAdapters[slot]->i_isModified())
12414 {
12415 mNetworkAdapters[slot]->i_rollback();
12416 networkAdapters[slot] = mNetworkAdapters[slot];
12417 }
12418
12419 if (mData->flModifications & IsModified_SerialPorts)
12420 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12421 if ( mSerialPorts[slot]
12422 && mSerialPorts[slot]->i_isModified())
12423 {
12424 mSerialPorts[slot]->i_rollback();
12425 serialPorts[slot] = mSerialPorts[slot];
12426 }
12427
12428 if (mData->flModifications & IsModified_ParallelPorts)
12429 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12430 if ( mParallelPorts[slot]
12431 && mParallelPorts[slot]->i_isModified())
12432 {
12433 mParallelPorts[slot]->i_rollback();
12434 parallelPorts[slot] = mParallelPorts[slot];
12435 }
12436
12437 if (aNotify)
12438 {
12439 /* inform the direct session about changes */
12440
12441 ComObjPtr<Machine> that = this;
12442 uint32_t flModifications = mData->flModifications;
12443 alock.release();
12444
12445 if (flModifications & IsModified_SharedFolders)
12446 that->i_onSharedFolderChange();
12447
12448 if (flModifications & IsModified_VRDEServer)
12449 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12450 if (flModifications & IsModified_USB)
12451 that->i_onUSBControllerChange();
12452
12453 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12454 if (networkAdapters[slot])
12455 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12456 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12457 if (serialPorts[slot])
12458 that->i_onSerialPortChange(serialPorts[slot]);
12459 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12460 if (parallelPorts[slot])
12461 that->i_onParallelPortChange(parallelPorts[slot]);
12462
12463 if (flModifications & IsModified_Storage)
12464 {
12465 for (StorageControllerList::const_iterator
12466 it = mStorageControllers->begin();
12467 it != mStorageControllers->end();
12468 ++it)
12469 {
12470 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
12471 }
12472 }
12473
12474 if (flModifications & IsModified_GuestDebugControl)
12475 that->i_onGuestDebugControlChange(mGuestDebugControl);
12476
12477#if 0
12478 if (flModifications & IsModified_BandwidthControl)
12479 that->onBandwidthControlChange();
12480#endif
12481 }
12482}
12483
12484/**
12485 * Commits all the changes to machine settings.
12486 *
12487 * Note that this operation is supposed to never fail.
12488 *
12489 * @note Locks this object and children for writing.
12490 */
12491void Machine::i_commit()
12492{
12493 AutoCaller autoCaller(this);
12494 AssertComRCReturnVoid(autoCaller.rc());
12495
12496 AutoCaller peerCaller(mPeer);
12497 AssertComRCReturnVoid(peerCaller.rc());
12498
12499 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12500
12501 /*
12502 * use safe commit to ensure Snapshot machines (that share mUserData)
12503 * will still refer to a valid memory location
12504 */
12505 mUserData.commitCopy();
12506
12507 mHWData.commit();
12508
12509 if (mMediumAttachments.isBackedUp())
12510 i_commitMedia(Global::IsOnline(mData->mMachineState));
12511
12512 mBIOSSettings->i_commit();
12513 mRecordingSettings->i_commit();
12514 mTrustedPlatformModule->i_commit();
12515 mNvramStore->i_commit();
12516 mGraphicsAdapter->i_commit();
12517 mVRDEServer->i_commit();
12518 mAudioSettings->i_commit();
12519 mUSBDeviceFilters->i_commit();
12520 mBandwidthControl->i_commit();
12521 mGuestDebugControl->i_commit();
12522
12523 /* Since mNetworkAdapters is a list which might have been changed (resized)
12524 * without using the Backupable<> template we need to handle the copying
12525 * of the list entries manually, including the creation of peers for the
12526 * new objects. */
12527 bool commitNetworkAdapters = false;
12528 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12529 if (mPeer)
12530 {
12531 /* commit everything, even the ones which will go away */
12532 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12533 mNetworkAdapters[slot]->i_commit();
12534 /* copy over the new entries, creating a peer and uninit the original */
12535 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12536 for (size_t slot = 0; slot < newSize; slot++)
12537 {
12538 /* look if this adapter has a peer device */
12539 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12540 if (!peer)
12541 {
12542 /* no peer means the adapter is a newly created one;
12543 * create a peer owning data this data share it with */
12544 peer.createObject();
12545 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12546 }
12547 mPeer->mNetworkAdapters[slot] = peer;
12548 }
12549 /* uninit any no longer needed network adapters */
12550 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12551 mNetworkAdapters[slot]->uninit();
12552 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12553 {
12554 if (mPeer->mNetworkAdapters[slot])
12555 mPeer->mNetworkAdapters[slot]->uninit();
12556 }
12557 /* Keep the original network adapter count until this point, so that
12558 * discarding a chipset type change will not lose settings. */
12559 mNetworkAdapters.resize(newSize);
12560 mPeer->mNetworkAdapters.resize(newSize);
12561 }
12562 else
12563 {
12564 /* we have no peer (our parent is the newly created machine);
12565 * just commit changes to the network adapters */
12566 commitNetworkAdapters = true;
12567 }
12568 if (commitNetworkAdapters)
12569 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12570 mNetworkAdapters[slot]->i_commit();
12571
12572 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12573 mSerialPorts[slot]->i_commit();
12574 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12575 mParallelPorts[slot]->i_commit();
12576
12577 bool commitStorageControllers = false;
12578
12579 if (mStorageControllers.isBackedUp())
12580 {
12581 mStorageControllers.commit();
12582
12583 if (mPeer)
12584 {
12585 /* Commit all changes to new controllers (this will reshare data with
12586 * peers for those who have peers) */
12587 StorageControllerList *newList = new StorageControllerList();
12588 for (StorageControllerList::const_iterator
12589 it = mStorageControllers->begin();
12590 it != mStorageControllers->end();
12591 ++it)
12592 {
12593 (*it)->i_commit();
12594
12595 /* look if this controller has a peer device */
12596 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12597 if (!peer)
12598 {
12599 /* no peer means the device is a newly created one;
12600 * create a peer owning data this device share it with */
12601 peer.createObject();
12602 peer->init(mPeer, *it, true /* aReshare */);
12603 }
12604 else
12605 {
12606 /* remove peer from the old list */
12607 mPeer->mStorageControllers->remove(peer);
12608 }
12609 /* and add it to the new list */
12610 newList->push_back(peer);
12611 }
12612
12613 /* uninit old peer's controllers that are left */
12614 for (StorageControllerList::const_iterator
12615 it = mPeer->mStorageControllers->begin();
12616 it != mPeer->mStorageControllers->end();
12617 ++it)
12618 {
12619 (*it)->uninit();
12620 }
12621
12622 /* attach new list of controllers to our peer */
12623 mPeer->mStorageControllers.attach(newList);
12624 }
12625 else
12626 {
12627 /* we have no peer (our parent is the newly created machine);
12628 * just commit changes to devices */
12629 commitStorageControllers = true;
12630 }
12631 }
12632 else
12633 {
12634 /* the list of controllers itself is not changed,
12635 * just commit changes to controllers themselves */
12636 commitStorageControllers = true;
12637 }
12638
12639 if (commitStorageControllers)
12640 {
12641 for (StorageControllerList::const_iterator
12642 it = mStorageControllers->begin();
12643 it != mStorageControllers->end();
12644 ++it)
12645 {
12646 (*it)->i_commit();
12647 }
12648 }
12649
12650 bool commitUSBControllers = false;
12651
12652 if (mUSBControllers.isBackedUp())
12653 {
12654 mUSBControllers.commit();
12655
12656 if (mPeer)
12657 {
12658 /* Commit all changes to new controllers (this will reshare data with
12659 * peers for those who have peers) */
12660 USBControllerList *newList = new USBControllerList();
12661 for (USBControllerList::const_iterator
12662 it = mUSBControllers->begin();
12663 it != mUSBControllers->end();
12664 ++it)
12665 {
12666 (*it)->i_commit();
12667
12668 /* look if this controller has a peer device */
12669 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12670 if (!peer)
12671 {
12672 /* no peer means the device is a newly created one;
12673 * create a peer owning data this device share it with */
12674 peer.createObject();
12675 peer->init(mPeer, *it, true /* aReshare */);
12676 }
12677 else
12678 {
12679 /* remove peer from the old list */
12680 mPeer->mUSBControllers->remove(peer);
12681 }
12682 /* and add it to the new list */
12683 newList->push_back(peer);
12684 }
12685
12686 /* uninit old peer's controllers that are left */
12687 for (USBControllerList::const_iterator
12688 it = mPeer->mUSBControllers->begin();
12689 it != mPeer->mUSBControllers->end();
12690 ++it)
12691 {
12692 (*it)->uninit();
12693 }
12694
12695 /* attach new list of controllers to our peer */
12696 mPeer->mUSBControllers.attach(newList);
12697 }
12698 else
12699 {
12700 /* we have no peer (our parent is the newly created machine);
12701 * just commit changes to devices */
12702 commitUSBControllers = true;
12703 }
12704 }
12705 else
12706 {
12707 /* the list of controllers itself is not changed,
12708 * just commit changes to controllers themselves */
12709 commitUSBControllers = true;
12710 }
12711
12712 if (commitUSBControllers)
12713 {
12714 for (USBControllerList::const_iterator
12715 it = mUSBControllers->begin();
12716 it != mUSBControllers->end();
12717 ++it)
12718 {
12719 (*it)->i_commit();
12720 }
12721 }
12722
12723 if (i_isSessionMachine())
12724 {
12725 /* attach new data to the primary machine and reshare it */
12726 mPeer->mUserData.attach(mUserData);
12727 mPeer->mHWData.attach(mHWData);
12728 /* mmMediumAttachments is reshared by fixupMedia */
12729 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12730 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12731 }
12732}
12733
12734/**
12735 * Copies all the hardware data from the given machine.
12736 *
12737 * Currently, only called when the VM is being restored from a snapshot. In
12738 * particular, this implies that the VM is not running during this method's
12739 * call.
12740 *
12741 * @note This method must be called from under this object's lock.
12742 *
12743 * @note This method doesn't call #i_commit(), so all data remains backed up and
12744 * unsaved.
12745 */
12746void Machine::i_copyFrom(Machine *aThat)
12747{
12748 AssertReturnVoid(!i_isSnapshotMachine());
12749 AssertReturnVoid(aThat->i_isSnapshotMachine());
12750
12751 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12752
12753 mHWData.assignCopy(aThat->mHWData);
12754
12755 // create copies of all shared folders (mHWData after attaching a copy
12756 // contains just references to original objects)
12757 for (HWData::SharedFolderList::iterator
12758 it = mHWData->mSharedFolders.begin();
12759 it != mHWData->mSharedFolders.end();
12760 ++it)
12761 {
12762 ComObjPtr<SharedFolder> folder;
12763 folder.createObject();
12764 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12765 AssertComRC(rc);
12766 *it = folder;
12767 }
12768
12769 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12770 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12771 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12772 mNvramStore->i_copyFrom(aThat->mNvramStore);
12773 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12774 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12775 mAudioSettings->i_copyFrom(aThat->mAudioSettings);
12776 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12777 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12778 mGuestDebugControl->i_copyFrom(aThat->mGuestDebugControl);
12779
12780 /* create private copies of all controllers */
12781 mStorageControllers.backup();
12782 mStorageControllers->clear();
12783 for (StorageControllerList::const_iterator
12784 it = aThat->mStorageControllers->begin();
12785 it != aThat->mStorageControllers->end();
12786 ++it)
12787 {
12788 ComObjPtr<StorageController> ctrl;
12789 ctrl.createObject();
12790 ctrl->initCopy(this, *it);
12791 mStorageControllers->push_back(ctrl);
12792 }
12793
12794 /* create private copies of all USB controllers */
12795 mUSBControllers.backup();
12796 mUSBControllers->clear();
12797 for (USBControllerList::const_iterator
12798 it = aThat->mUSBControllers->begin();
12799 it != aThat->mUSBControllers->end();
12800 ++it)
12801 {
12802 ComObjPtr<USBController> ctrl;
12803 ctrl.createObject();
12804 ctrl->initCopy(this, *it);
12805 mUSBControllers->push_back(ctrl);
12806 }
12807
12808 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12809 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12810 {
12811 if (mNetworkAdapters[slot].isNotNull())
12812 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12813 else
12814 {
12815 unconst(mNetworkAdapters[slot]).createObject();
12816 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12817 }
12818 }
12819 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12820 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12821 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12822 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12823}
12824
12825/**
12826 * Returns whether the given storage controller is hotplug capable.
12827 *
12828 * @returns true if the controller supports hotplugging
12829 * false otherwise.
12830 * @param enmCtrlType The controller type to check for.
12831 */
12832bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12833{
12834 ComPtr<ISystemProperties> systemProperties;
12835 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12836 if (FAILED(rc))
12837 return false;
12838
12839 BOOL aHotplugCapable = FALSE;
12840 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12841
12842 return RT_BOOL(aHotplugCapable);
12843}
12844
12845#ifdef VBOX_WITH_RESOURCE_USAGE_API
12846
12847void Machine::i_getDiskList(MediaList &list)
12848{
12849 for (MediumAttachmentList::const_iterator
12850 it = mMediumAttachments->begin();
12851 it != mMediumAttachments->end();
12852 ++it)
12853 {
12854 MediumAttachment *pAttach = *it;
12855 /* just in case */
12856 AssertContinue(pAttach);
12857
12858 AutoCaller localAutoCallerA(pAttach);
12859 if (FAILED(localAutoCallerA.rc())) continue;
12860
12861 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12862
12863 if (pAttach->i_getType() == DeviceType_HardDisk)
12864 list.push_back(pAttach->i_getMedium());
12865 }
12866}
12867
12868void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12869{
12870 AssertReturnVoid(isWriteLockOnCurrentThread());
12871 AssertPtrReturnVoid(aCollector);
12872
12873 pm::CollectorHAL *hal = aCollector->getHAL();
12874 /* Create sub metrics */
12875 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12876 "Percentage of processor time spent in user mode by the VM process.");
12877 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12878 "Percentage of processor time spent in kernel mode by the VM process.");
12879 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12880 "Size of resident portion of VM process in memory.");
12881 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12882 "Actual size of all VM disks combined.");
12883 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12884 "Network receive rate.");
12885 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12886 "Network transmit rate.");
12887 /* Create and register base metrics */
12888 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12889 cpuLoadUser, cpuLoadKernel);
12890 aCollector->registerBaseMetric(cpuLoad);
12891 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12892 ramUsageUsed);
12893 aCollector->registerBaseMetric(ramUsage);
12894 MediaList disks;
12895 i_getDiskList(disks);
12896 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12897 diskUsageUsed);
12898 aCollector->registerBaseMetric(diskUsage);
12899
12900 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12901 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12902 new pm::AggregateAvg()));
12903 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12904 new pm::AggregateMin()));
12905 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12906 new pm::AggregateMax()));
12907 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12908 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12909 new pm::AggregateAvg()));
12910 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12911 new pm::AggregateMin()));
12912 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12913 new pm::AggregateMax()));
12914
12915 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12916 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12917 new pm::AggregateAvg()));
12918 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12919 new pm::AggregateMin()));
12920 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12921 new pm::AggregateMax()));
12922
12923 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12924 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12925 new pm::AggregateAvg()));
12926 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12927 new pm::AggregateMin()));
12928 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12929 new pm::AggregateMax()));
12930
12931
12932 /* Guest metrics collector */
12933 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12934 aCollector->registerGuest(mCollectorGuest);
12935 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12936
12937 /* Create sub metrics */
12938 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12939 "Percentage of processor time spent in user mode as seen by the guest.");
12940 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12941 "Percentage of processor time spent in kernel mode as seen by the guest.");
12942 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12943 "Percentage of processor time spent idling as seen by the guest.");
12944
12945 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12946 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12947 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12948 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12949 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12950 pm::SubMetric *guestMemCache = new pm::SubMetric(
12951 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12952
12953 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12954 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12955
12956 /* Create and register base metrics */
12957 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12958 machineNetRx, machineNetTx);
12959 aCollector->registerBaseMetric(machineNetRate);
12960
12961 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12962 guestLoadUser, guestLoadKernel, guestLoadIdle);
12963 aCollector->registerBaseMetric(guestCpuLoad);
12964
12965 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12966 guestMemTotal, guestMemFree,
12967 guestMemBalloon, guestMemShared,
12968 guestMemCache, guestPagedTotal);
12969 aCollector->registerBaseMetric(guestCpuMem);
12970
12971 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12972 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12973 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12974 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12975
12976 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12977 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12978 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12979 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12980
12981 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12982 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12983 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12984 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12985
12986 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12987 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12988 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12989 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12990
12991 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12992 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12993 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12994 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12995
12996 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12997 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12998 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12999 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
13000
13001 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
13002 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
13003 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
13004 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
13005
13006 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
13007 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
13008 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
13009 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
13010
13011 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
13012 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
13013 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
13014 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
13015
13016 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
13017 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
13018 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
13019 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
13020
13021 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
13022 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
13023 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
13024 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
13025}
13026
13027void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
13028{
13029 AssertReturnVoid(isWriteLockOnCurrentThread());
13030
13031 if (aCollector)
13032 {
13033 aCollector->unregisterMetricsFor(aMachine);
13034 aCollector->unregisterBaseMetricsFor(aMachine);
13035 }
13036}
13037
13038#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13039
13040
13041////////////////////////////////////////////////////////////////////////////////
13042
13043DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
13044
13045HRESULT SessionMachine::FinalConstruct()
13046{
13047 LogFlowThisFunc(("\n"));
13048
13049 mClientToken = NULL;
13050
13051 return BaseFinalConstruct();
13052}
13053
13054void SessionMachine::FinalRelease()
13055{
13056 LogFlowThisFunc(("\n"));
13057
13058 Assert(!mClientToken);
13059 /* paranoia, should not hang around any more */
13060 if (mClientToken)
13061 {
13062 delete mClientToken;
13063 mClientToken = NULL;
13064 }
13065
13066 uninit(Uninit::Unexpected);
13067
13068 BaseFinalRelease();
13069}
13070
13071/**
13072 * @note Must be called only by Machine::LockMachine() from its own write lock.
13073 */
13074HRESULT SessionMachine::init(Machine *aMachine)
13075{
13076 LogFlowThisFuncEnter();
13077 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
13078
13079 AssertReturn(aMachine, E_INVALIDARG);
13080
13081 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
13082
13083 /* Enclose the state transition NotReady->InInit->Ready */
13084 AutoInitSpan autoInitSpan(this);
13085 AssertReturn(autoInitSpan.isOk(), E_FAIL);
13086
13087 HRESULT rc = S_OK;
13088
13089 RT_ZERO(mAuthLibCtx);
13090
13091 /* create the machine client token */
13092 try
13093 {
13094 mClientToken = new ClientToken(aMachine, this);
13095 if (!mClientToken->isReady())
13096 {
13097 delete mClientToken;
13098 mClientToken = NULL;
13099 rc = E_FAIL;
13100 }
13101 }
13102 catch (std::bad_alloc &)
13103 {
13104 rc = E_OUTOFMEMORY;
13105 }
13106 if (FAILED(rc))
13107 return rc;
13108
13109 /* memorize the peer Machine */
13110 unconst(mPeer) = aMachine;
13111 /* share the parent pointer */
13112 unconst(mParent) = aMachine->mParent;
13113
13114 /* take the pointers to data to share */
13115 mData.share(aMachine->mData);
13116 mSSData.share(aMachine->mSSData);
13117
13118 mUserData.share(aMachine->mUserData);
13119 mHWData.share(aMachine->mHWData);
13120 mMediumAttachments.share(aMachine->mMediumAttachments);
13121
13122 mStorageControllers.allocate();
13123 for (StorageControllerList::const_iterator
13124 it = aMachine->mStorageControllers->begin();
13125 it != aMachine->mStorageControllers->end();
13126 ++it)
13127 {
13128 ComObjPtr<StorageController> ctl;
13129 ctl.createObject();
13130 ctl->init(this, *it);
13131 mStorageControllers->push_back(ctl);
13132 }
13133
13134 mUSBControllers.allocate();
13135 for (USBControllerList::const_iterator
13136 it = aMachine->mUSBControllers->begin();
13137 it != aMachine->mUSBControllers->end();
13138 ++it)
13139 {
13140 ComObjPtr<USBController> ctl;
13141 ctl.createObject();
13142 ctl->init(this, *it);
13143 mUSBControllers->push_back(ctl);
13144 }
13145
13146 unconst(mBIOSSettings).createObject();
13147 mBIOSSettings->init(this, aMachine->mBIOSSettings);
13148
13149 unconst(mRecordingSettings).createObject();
13150 mRecordingSettings->init(this, aMachine->mRecordingSettings);
13151
13152 unconst(mTrustedPlatformModule).createObject();
13153 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
13154
13155 unconst(mNvramStore).createObject();
13156 mNvramStore->init(this, aMachine->mNvramStore);
13157
13158 /* create another GraphicsAdapter object that will be mutable */
13159 unconst(mGraphicsAdapter).createObject();
13160 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
13161 /* create another VRDEServer object that will be mutable */
13162 unconst(mVRDEServer).createObject();
13163 mVRDEServer->init(this, aMachine->mVRDEServer);
13164 /* create another audio settings object that will be mutable */
13165 unconst(mAudioSettings).createObject();
13166 mAudioSettings->init(this, aMachine->mAudioSettings);
13167 /* create a list of serial ports that will be mutable */
13168 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
13169 {
13170 unconst(mSerialPorts[slot]).createObject();
13171 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
13172 }
13173 /* create a list of parallel ports that will be mutable */
13174 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
13175 {
13176 unconst(mParallelPorts[slot]).createObject();
13177 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
13178 }
13179
13180 /* create another USB device filters object that will be mutable */
13181 unconst(mUSBDeviceFilters).createObject();
13182 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
13183
13184 /* create a list of network adapters that will be mutable */
13185 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
13186 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13187 {
13188 unconst(mNetworkAdapters[slot]).createObject();
13189 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
13190 }
13191
13192 /* create another bandwidth control object that will be mutable */
13193 unconst(mBandwidthControl).createObject();
13194 mBandwidthControl->init(this, aMachine->mBandwidthControl);
13195
13196 unconst(mGuestDebugControl).createObject();
13197 mGuestDebugControl->init(this, aMachine->mGuestDebugControl);
13198
13199 /* default is to delete saved state on Saved -> PoweredOff transition */
13200 mRemoveSavedState = true;
13201
13202 /* Confirm a successful initialization when it's the case */
13203 autoInitSpan.setSucceeded();
13204
13205 miNATNetworksStarted = 0;
13206
13207 LogFlowThisFuncLeave();
13208 return rc;
13209}
13210
13211/**
13212 * Uninitializes this session object. If the reason is other than
13213 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
13214 * or the client watcher code.
13215 *
13216 * @param aReason uninitialization reason
13217 *
13218 * @note Locks mParent + this object for writing.
13219 */
13220void SessionMachine::uninit(Uninit::Reason aReason)
13221{
13222 LogFlowThisFuncEnter();
13223 LogFlowThisFunc(("reason=%d\n", aReason));
13224
13225 /*
13226 * Strongly reference ourselves to prevent this object deletion after
13227 * mData->mSession.mMachine.setNull() below (which can release the last
13228 * reference and call the destructor). Important: this must be done before
13229 * accessing any members (and before AutoUninitSpan that does it as well).
13230 * This self reference will be released as the very last step on return.
13231 */
13232 ComObjPtr<SessionMachine> selfRef;
13233 if (aReason != Uninit::Unexpected)
13234 selfRef = this;
13235
13236 /* Enclose the state transition Ready->InUninit->NotReady */
13237 AutoUninitSpan autoUninitSpan(this);
13238 if (autoUninitSpan.uninitDone())
13239 {
13240 LogFlowThisFunc(("Already uninitialized\n"));
13241 LogFlowThisFuncLeave();
13242 return;
13243 }
13244
13245 if (autoUninitSpan.initFailed())
13246 {
13247 /* We've been called by init() because it's failed. It's not really
13248 * necessary (nor it's safe) to perform the regular uninit sequence
13249 * below, the following is enough.
13250 */
13251 LogFlowThisFunc(("Initialization failed.\n"));
13252 /* destroy the machine client token */
13253 if (mClientToken)
13254 {
13255 delete mClientToken;
13256 mClientToken = NULL;
13257 }
13258 uninitDataAndChildObjects();
13259 mData.free();
13260 unconst(mParent) = NULL;
13261 unconst(mPeer) = NULL;
13262 LogFlowThisFuncLeave();
13263 return;
13264 }
13265
13266 MachineState_T lastState;
13267 {
13268 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
13269 lastState = mData->mMachineState;
13270 }
13271 NOREF(lastState);
13272
13273#ifdef VBOX_WITH_USB
13274 // release all captured USB devices, but do this before requesting the locks below
13275 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
13276 {
13277 /* Console::captureUSBDevices() is called in the VM process only after
13278 * setting the machine state to Starting or Restoring.
13279 * Console::detachAllUSBDevices() will be called upon successful
13280 * termination. So, we need to release USB devices only if there was
13281 * an abnormal termination of a running VM.
13282 *
13283 * This is identical to SessionMachine::DetachAllUSBDevices except
13284 * for the aAbnormal argument. */
13285 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13286 AssertComRC(rc);
13287 NOREF(rc);
13288
13289 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13290 if (service)
13291 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
13292 }
13293#endif /* VBOX_WITH_USB */
13294
13295 // we need to lock this object in uninit() because the lock is shared
13296 // with mPeer (as well as data we modify below). mParent lock is needed
13297 // by several calls to it.
13298 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
13299
13300#ifdef VBOX_WITH_RESOURCE_USAGE_API
13301 /*
13302 * It is safe to call Machine::i_unregisterMetrics() here because
13303 * PerformanceCollector::samplerCallback no longer accesses guest methods
13304 * holding the lock.
13305 */
13306 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
13307 /* The guest must be unregistered after its metrics (@bugref{5949}). */
13308 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
13309 if (mCollectorGuest)
13310 {
13311 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
13312 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
13313 mCollectorGuest = NULL;
13314 }
13315#endif
13316
13317 if (aReason == Uninit::Abnormal)
13318 {
13319 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
13320
13321 /*
13322 * Move the VM to the 'Aborted' machine state unless we are restoring a
13323 * VM that was in the 'Saved' machine state. In that case, if the VM
13324 * fails before reaching either the 'Restoring' machine state or the
13325 * 'Running' machine state then we set the machine state to
13326 * 'AbortedSaved' in order to preserve the saved state file so that the
13327 * VM can be restored in the future.
13328 */
13329 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
13330 i_setMachineState(MachineState_AbortedSaved);
13331 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
13332 i_setMachineState(MachineState_Aborted);
13333 }
13334
13335 // any machine settings modified?
13336 if (mData->flModifications)
13337 {
13338 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
13339 i_rollback(false /* aNotify */);
13340 }
13341
13342 mData->mSession.mPID = NIL_RTPROCESS;
13343
13344 if (aReason == Uninit::Unexpected)
13345 {
13346 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
13347 * client watcher thread to update the set of machines that have open
13348 * sessions. */
13349 mParent->i_updateClientWatcher();
13350 }
13351
13352 /* uninitialize all remote controls */
13353 if (mData->mSession.mRemoteControls.size())
13354 {
13355 LogFlowThisFunc(("Closing remote sessions (%d):\n",
13356 mData->mSession.mRemoteControls.size()));
13357
13358 /* Always restart a the beginning, since the iterator is invalidated
13359 * by using erase(). */
13360 for (Data::Session::RemoteControlList::iterator
13361 it = mData->mSession.mRemoteControls.begin();
13362 it != mData->mSession.mRemoteControls.end();
13363 it = mData->mSession.mRemoteControls.begin())
13364 {
13365 ComPtr<IInternalSessionControl> pControl = *it;
13366 mData->mSession.mRemoteControls.erase(it);
13367 multilock.release();
13368 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
13369 HRESULT rc = pControl->Uninitialize();
13370 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
13371 if (FAILED(rc))
13372 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
13373 multilock.acquire();
13374 }
13375 mData->mSession.mRemoteControls.clear();
13376 }
13377
13378 /* Remove all references to the NAT network service. The service will stop
13379 * if all references (also from other VMs) are removed. */
13380 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
13381 {
13382 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13383 {
13384 BOOL enabled;
13385 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13386 if ( FAILED(hrc)
13387 || !enabled)
13388 continue;
13389
13390 NetworkAttachmentType_T type;
13391 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13392 if ( SUCCEEDED(hrc)
13393 && type == NetworkAttachmentType_NATNetwork)
13394 {
13395 Bstr name;
13396 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13397 if (SUCCEEDED(hrc))
13398 {
13399 multilock.release();
13400 Utf8Str strName(name);
13401 LogRel(("VM '%s' stops using NAT network '%s'\n",
13402 mUserData->s.strName.c_str(), strName.c_str()));
13403 mParent->i_natNetworkRefDec(strName);
13404 multilock.acquire();
13405 }
13406 }
13407 }
13408 }
13409
13410 /*
13411 * An expected uninitialization can come only from #i_checkForDeath().
13412 * Otherwise it means that something's gone really wrong (for example,
13413 * the Session implementation has released the VirtualBox reference
13414 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
13415 * etc). However, it's also possible, that the client releases the IPC
13416 * semaphore correctly (i.e. before it releases the VirtualBox reference),
13417 * but the VirtualBox release event comes first to the server process.
13418 * This case is practically possible, so we should not assert on an
13419 * unexpected uninit, just log a warning.
13420 */
13421
13422 if (aReason == Uninit::Unexpected)
13423 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
13424
13425 if (aReason != Uninit::Normal)
13426 {
13427 mData->mSession.mDirectControl.setNull();
13428 }
13429 else
13430 {
13431 /* this must be null here (see #OnSessionEnd()) */
13432 Assert(mData->mSession.mDirectControl.isNull());
13433 Assert(mData->mSession.mState == SessionState_Unlocking);
13434 Assert(!mData->mSession.mProgress.isNull());
13435 }
13436 if (mData->mSession.mProgress)
13437 {
13438 if (aReason == Uninit::Normal)
13439 mData->mSession.mProgress->i_notifyComplete(S_OK);
13440 else
13441 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
13442 COM_IIDOF(ISession),
13443 getComponentName(),
13444 tr("The VM session was aborted"));
13445 mData->mSession.mProgress.setNull();
13446 }
13447
13448 if (mConsoleTaskData.mProgress)
13449 {
13450 Assert(aReason == Uninit::Abnormal);
13451 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
13452 COM_IIDOF(ISession),
13453 getComponentName(),
13454 tr("The VM session was aborted"));
13455 mConsoleTaskData.mProgress.setNull();
13456 }
13457
13458 /* remove the association between the peer machine and this session machine */
13459 Assert( (SessionMachine*)mData->mSession.mMachine == this
13460 || aReason == Uninit::Unexpected);
13461
13462 /* reset the rest of session data */
13463 mData->mSession.mLockType = LockType_Null;
13464 mData->mSession.mMachine.setNull();
13465 mData->mSession.mState = SessionState_Unlocked;
13466 mData->mSession.mName.setNull();
13467
13468 /* destroy the machine client token before leaving the exclusive lock */
13469 if (mClientToken)
13470 {
13471 delete mClientToken;
13472 mClientToken = NULL;
13473 }
13474
13475 /* fire an event */
13476 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
13477
13478 uninitDataAndChildObjects();
13479
13480 /* free the essential data structure last */
13481 mData.free();
13482
13483 /* release the exclusive lock before setting the below two to NULL */
13484 multilock.release();
13485
13486 unconst(mParent) = NULL;
13487 unconst(mPeer) = NULL;
13488
13489 AuthLibUnload(&mAuthLibCtx);
13490
13491 LogFlowThisFuncLeave();
13492}
13493
13494// util::Lockable interface
13495////////////////////////////////////////////////////////////////////////////////
13496
13497/**
13498 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13499 * with the primary Machine instance (mPeer).
13500 */
13501RWLockHandle *SessionMachine::lockHandle() const
13502{
13503 AssertReturn(mPeer != NULL, NULL);
13504 return mPeer->lockHandle();
13505}
13506
13507// IInternalMachineControl methods
13508////////////////////////////////////////////////////////////////////////////////
13509
13510/**
13511 * Passes collected guest statistics to performance collector object
13512 */
13513HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13514 ULONG aCpuKernel, ULONG aCpuIdle,
13515 ULONG aMemTotal, ULONG aMemFree,
13516 ULONG aMemBalloon, ULONG aMemShared,
13517 ULONG aMemCache, ULONG aPageTotal,
13518 ULONG aAllocVMM, ULONG aFreeVMM,
13519 ULONG aBalloonedVMM, ULONG aSharedVMM,
13520 ULONG aVmNetRx, ULONG aVmNetTx)
13521{
13522#ifdef VBOX_WITH_RESOURCE_USAGE_API
13523 if (mCollectorGuest)
13524 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13525 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13526 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13527 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13528
13529 return S_OK;
13530#else
13531 NOREF(aValidStats);
13532 NOREF(aCpuUser);
13533 NOREF(aCpuKernel);
13534 NOREF(aCpuIdle);
13535 NOREF(aMemTotal);
13536 NOREF(aMemFree);
13537 NOREF(aMemBalloon);
13538 NOREF(aMemShared);
13539 NOREF(aMemCache);
13540 NOREF(aPageTotal);
13541 NOREF(aAllocVMM);
13542 NOREF(aFreeVMM);
13543 NOREF(aBalloonedVMM);
13544 NOREF(aSharedVMM);
13545 NOREF(aVmNetRx);
13546 NOREF(aVmNetTx);
13547 return E_NOTIMPL;
13548#endif
13549}
13550
13551////////////////////////////////////////////////////////////////////////////////
13552//
13553// SessionMachine task records
13554//
13555////////////////////////////////////////////////////////////////////////////////
13556
13557/**
13558 * Task record for saving the machine state.
13559 */
13560class SessionMachine::SaveStateTask
13561 : public Machine::Task
13562{
13563public:
13564 SaveStateTask(SessionMachine *m,
13565 Progress *p,
13566 const Utf8Str &t,
13567 Reason_T enmReason,
13568 const Utf8Str &strStateFilePath)
13569 : Task(m, p, t),
13570 m_enmReason(enmReason),
13571 m_strStateFilePath(strStateFilePath)
13572 {}
13573
13574private:
13575 void handler()
13576 {
13577 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13578 }
13579
13580 Reason_T m_enmReason;
13581 Utf8Str m_strStateFilePath;
13582
13583 friend class SessionMachine;
13584};
13585
13586/**
13587 * Task thread implementation for SessionMachine::SaveState(), called from
13588 * SessionMachine::taskHandler().
13589 *
13590 * @note Locks this object for writing.
13591 *
13592 * @param task
13593 * @return
13594 */
13595void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13596{
13597 LogFlowThisFuncEnter();
13598
13599 AutoCaller autoCaller(this);
13600 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13601 if (FAILED(autoCaller.rc()))
13602 {
13603 /* we might have been uninitialized because the session was accidentally
13604 * closed by the client, so don't assert */
13605 HRESULT rc = setError(E_FAIL,
13606 tr("The session has been accidentally closed"));
13607 task.m_pProgress->i_notifyComplete(rc);
13608 LogFlowThisFuncLeave();
13609 return;
13610 }
13611
13612 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13613
13614 HRESULT rc = S_OK;
13615
13616 try
13617 {
13618 ComPtr<IInternalSessionControl> directControl;
13619 if (mData->mSession.mLockType == LockType_VM)
13620 directControl = mData->mSession.mDirectControl;
13621 if (directControl.isNull())
13622 throw setError(VBOX_E_INVALID_VM_STATE,
13623 tr("Trying to save state without a running VM"));
13624 alock.release();
13625 BOOL fSuspendedBySave;
13626 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13627 Assert(!fSuspendedBySave);
13628 alock.acquire();
13629
13630 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13631 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13632 throw E_FAIL);
13633
13634 if (SUCCEEDED(rc))
13635 {
13636 mSSData->strStateFilePath = task.m_strStateFilePath;
13637
13638 /* save all VM settings */
13639 rc = i_saveSettings(NULL, alock);
13640 // no need to check whether VirtualBox.xml needs saving also since
13641 // we can't have a name change pending at this point
13642 }
13643 else
13644 {
13645 // On failure, set the state to the state we had at the beginning.
13646 i_setMachineState(task.m_machineStateBackup);
13647 i_updateMachineStateOnClient();
13648
13649 // Delete the saved state file (might have been already created).
13650 // No need to check whether this is shared with a snapshot here
13651 // because we certainly created a fresh saved state file here.
13652 i_deleteFile(task.m_strStateFilePath, true /* fIgnoreFailures */);
13653 }
13654 }
13655 catch (HRESULT aRC) { rc = aRC; }
13656
13657 task.m_pProgress->i_notifyComplete(rc);
13658
13659 LogFlowThisFuncLeave();
13660}
13661
13662/**
13663 * @note Locks this object for writing.
13664 */
13665HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13666{
13667 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13668}
13669
13670HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13671{
13672 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13673
13674 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13675 if (FAILED(rc)) return rc;
13676
13677 if ( mData->mMachineState != MachineState_Running
13678 && mData->mMachineState != MachineState_Paused
13679 )
13680 return setError(VBOX_E_INVALID_VM_STATE,
13681 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13682 Global::stringifyMachineState(mData->mMachineState));
13683
13684 ComObjPtr<Progress> pProgress;
13685 pProgress.createObject();
13686 rc = pProgress->init(i_getVirtualBox(),
13687 static_cast<IMachine *>(this) /* aInitiator */,
13688 tr("Saving the execution state of the virtual machine"),
13689 FALSE /* aCancelable */);
13690 if (FAILED(rc))
13691 return rc;
13692
13693 Utf8Str strStateFilePath;
13694 i_composeSavedStateFilename(strStateFilePath);
13695
13696 /* create and start the task on a separate thread (note that it will not
13697 * start working until we release alock) */
13698 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13699 rc = pTask->createThread();
13700 if (FAILED(rc))
13701 return rc;
13702
13703 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13704 i_setMachineState(MachineState_Saving);
13705 i_updateMachineStateOnClient();
13706
13707 pProgress.queryInterfaceTo(aProgress.asOutParam());
13708
13709 return S_OK;
13710}
13711
13712/**
13713 * @note Locks this object for writing.
13714 */
13715HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13716{
13717 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13718
13719 HRESULT rc = i_checkStateDependency(MutableStateDep);
13720 if (FAILED(rc)) return rc;
13721
13722 if ( mData->mMachineState != MachineState_PoweredOff
13723 && mData->mMachineState != MachineState_Teleported
13724 && mData->mMachineState != MachineState_Aborted
13725 )
13726 return setError(VBOX_E_INVALID_VM_STATE,
13727 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13728 Global::stringifyMachineState(mData->mMachineState));
13729
13730 com::Utf8Str stateFilePathFull;
13731 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13732 if (RT_FAILURE(vrc))
13733 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13734 tr("Invalid saved state file path '%s' (%Rrc)"),
13735 aSavedStateFile.c_str(),
13736 vrc);
13737
13738 mSSData->strStateFilePath = stateFilePathFull;
13739
13740 /* The below i_setMachineState() will detect the state transition and will
13741 * update the settings file */
13742
13743 return i_setMachineState(MachineState_Saved);
13744}
13745
13746/**
13747 * @note Locks this object for writing.
13748 */
13749HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13750{
13751 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13752
13753 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13754 if (FAILED(rc)) return rc;
13755
13756 if ( mData->mMachineState != MachineState_Saved
13757 && mData->mMachineState != MachineState_AbortedSaved)
13758 return setError(VBOX_E_INVALID_VM_STATE,
13759 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13760 Global::stringifyMachineState(mData->mMachineState));
13761
13762 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13763
13764 /*
13765 * Saved -> PoweredOff transition will be detected in the SessionMachine
13766 * and properly handled.
13767 */
13768 rc = i_setMachineState(MachineState_PoweredOff);
13769 return rc;
13770}
13771
13772
13773/**
13774 * @note Locks the same as #i_setMachineState() does.
13775 */
13776HRESULT SessionMachine::updateState(MachineState_T aState)
13777{
13778 return i_setMachineState(aState);
13779}
13780
13781/**
13782 * @note Locks this object for writing.
13783 */
13784HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13785{
13786 IProgress *pProgress(aProgress);
13787
13788 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13789
13790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13791
13792 if (mData->mSession.mState != SessionState_Locked)
13793 return VBOX_E_INVALID_OBJECT_STATE;
13794
13795 if (!mData->mSession.mProgress.isNull())
13796 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13797
13798 /* If we didn't reference the NAT network service yet, add a reference to
13799 * force a start */
13800 if (miNATNetworksStarted < 1)
13801 {
13802 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13803 {
13804 BOOL enabled;
13805 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13806 if ( FAILED(hrc)
13807 || !enabled)
13808 continue;
13809
13810 NetworkAttachmentType_T type;
13811 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13812 if ( SUCCEEDED(hrc)
13813 && type == NetworkAttachmentType_NATNetwork)
13814 {
13815 Bstr name;
13816 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13817 if (SUCCEEDED(hrc))
13818 {
13819 Utf8Str strName(name);
13820 LogRel(("VM '%s' starts using NAT network '%s'\n",
13821 mUserData->s.strName.c_str(), strName.c_str()));
13822 mPeer->lockHandle()->unlockWrite();
13823 mParent->i_natNetworkRefInc(strName);
13824#ifdef RT_LOCK_STRICT
13825 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13826#else
13827 mPeer->lockHandle()->lockWrite();
13828#endif
13829 }
13830 }
13831 }
13832 miNATNetworksStarted++;
13833 }
13834
13835 LogFlowThisFunc(("returns S_OK.\n"));
13836 return S_OK;
13837}
13838
13839/**
13840 * @note Locks this object for writing.
13841 */
13842HRESULT SessionMachine::endPowerUp(LONG aResult)
13843{
13844 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13845
13846 if (mData->mSession.mState != SessionState_Locked)
13847 return VBOX_E_INVALID_OBJECT_STATE;
13848
13849 /* Finalize the LaunchVMProcess progress object. */
13850 if (mData->mSession.mProgress)
13851 {
13852 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13853 mData->mSession.mProgress.setNull();
13854 }
13855
13856 if (SUCCEEDED((HRESULT)aResult))
13857 {
13858#ifdef VBOX_WITH_RESOURCE_USAGE_API
13859 /* The VM has been powered up successfully, so it makes sense
13860 * now to offer the performance metrics for a running machine
13861 * object. Doing it earlier wouldn't be safe. */
13862 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13863 mData->mSession.mPID);
13864#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13865 }
13866
13867 return S_OK;
13868}
13869
13870/**
13871 * @note Locks this object for writing.
13872 */
13873HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13874{
13875 LogFlowThisFuncEnter();
13876
13877 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13878
13879 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13880 E_FAIL);
13881
13882 /* create a progress object to track operation completion */
13883 ComObjPtr<Progress> pProgress;
13884 pProgress.createObject();
13885 pProgress->init(i_getVirtualBox(),
13886 static_cast<IMachine *>(this) /* aInitiator */,
13887 tr("Stopping the virtual machine"),
13888 FALSE /* aCancelable */);
13889
13890 /* fill in the console task data */
13891 mConsoleTaskData.mLastState = mData->mMachineState;
13892 mConsoleTaskData.mProgress = pProgress;
13893
13894 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13895 i_setMachineState(MachineState_Stopping);
13896
13897 pProgress.queryInterfaceTo(aProgress.asOutParam());
13898
13899 return S_OK;
13900}
13901
13902/**
13903 * @note Locks this object for writing.
13904 */
13905HRESULT SessionMachine::endPoweringDown(LONG aResult,
13906 const com::Utf8Str &aErrMsg)
13907{
13908 HRESULT const hrcResult = (HRESULT)aResult;
13909 LogFlowThisFuncEnter();
13910
13911 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13912
13913 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13914 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13915 && mConsoleTaskData.mLastState != MachineState_Null,
13916 E_FAIL);
13917
13918 /*
13919 * On failure, set the state to the state we had when BeginPoweringDown()
13920 * was called (this is expected by Console::PowerDown() and the associated
13921 * task). On success the VM process already changed the state to
13922 * MachineState_PoweredOff, so no need to do anything.
13923 */
13924 if (FAILED(hrcResult))
13925 i_setMachineState(mConsoleTaskData.mLastState);
13926
13927 /* notify the progress object about operation completion */
13928 Assert(mConsoleTaskData.mProgress);
13929 if (SUCCEEDED(hrcResult))
13930 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13931 else
13932 {
13933 if (aErrMsg.length())
13934 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13935 COM_IIDOF(ISession),
13936 getComponentName(),
13937 aErrMsg.c_str());
13938 else
13939 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13940 }
13941
13942 /* clear out the temporary saved state data */
13943 mConsoleTaskData.mLastState = MachineState_Null;
13944 mConsoleTaskData.mProgress.setNull();
13945
13946 LogFlowThisFuncLeave();
13947 return S_OK;
13948}
13949
13950
13951/**
13952 * Goes through the USB filters of the given machine to see if the given
13953 * device matches any filter or not.
13954 *
13955 * @note Locks the same as USBController::hasMatchingFilter() does.
13956 */
13957HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13958 BOOL *aMatched,
13959 ULONG *aMaskedInterfaces)
13960{
13961 LogFlowThisFunc(("\n"));
13962
13963#ifdef VBOX_WITH_USB
13964 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13965#else
13966 NOREF(aDevice);
13967 NOREF(aMaskedInterfaces);
13968 *aMatched = FALSE;
13969#endif
13970
13971 return S_OK;
13972}
13973
13974/**
13975 * @note Locks the same as Host::captureUSBDevice() does.
13976 */
13977HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13978{
13979 LogFlowThisFunc(("\n"));
13980
13981#ifdef VBOX_WITH_USB
13982 /* if captureDeviceForVM() fails, it must have set extended error info */
13983 clearError();
13984 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13985 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13986 return rc;
13987
13988 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13989 AssertReturn(service, E_FAIL);
13990 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13991#else
13992 RT_NOREF(aId, aCaptureFilename);
13993 return E_NOTIMPL;
13994#endif
13995}
13996
13997/**
13998 * @note Locks the same as Host::detachUSBDevice() does.
13999 */
14000HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
14001 BOOL aDone)
14002{
14003 LogFlowThisFunc(("\n"));
14004
14005#ifdef VBOX_WITH_USB
14006 USBProxyService *service = mParent->i_host()->i_usbProxyService();
14007 AssertReturn(service, E_FAIL);
14008 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
14009#else
14010 NOREF(aId);
14011 NOREF(aDone);
14012 return E_NOTIMPL;
14013#endif
14014}
14015
14016/**
14017 * Inserts all machine filters to the USB proxy service and then calls
14018 * Host::autoCaptureUSBDevices().
14019 *
14020 * Called by Console from the VM process upon VM startup.
14021 *
14022 * @note Locks what called methods lock.
14023 */
14024HRESULT SessionMachine::autoCaptureUSBDevices()
14025{
14026 LogFlowThisFunc(("\n"));
14027
14028#ifdef VBOX_WITH_USB
14029 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
14030 AssertComRC(rc);
14031 NOREF(rc);
14032
14033 USBProxyService *service = mParent->i_host()->i_usbProxyService();
14034 AssertReturn(service, E_FAIL);
14035 return service->autoCaptureDevicesForVM(this);
14036#else
14037 return S_OK;
14038#endif
14039}
14040
14041/**
14042 * Removes all machine filters from the USB proxy service and then calls
14043 * Host::detachAllUSBDevices().
14044 *
14045 * Called by Console from the VM process upon normal VM termination or by
14046 * SessionMachine::uninit() upon abnormal VM termination (from under the
14047 * Machine/SessionMachine lock).
14048 *
14049 * @note Locks what called methods lock.
14050 */
14051HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
14052{
14053 LogFlowThisFunc(("\n"));
14054
14055#ifdef VBOX_WITH_USB
14056 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
14057 AssertComRC(rc);
14058 NOREF(rc);
14059
14060 USBProxyService *service = mParent->i_host()->i_usbProxyService();
14061 AssertReturn(service, E_FAIL);
14062 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
14063#else
14064 NOREF(aDone);
14065 return S_OK;
14066#endif
14067}
14068
14069/**
14070 * @note Locks this object for writing.
14071 */
14072HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
14073 ComPtr<IProgress> &aProgress)
14074{
14075 LogFlowThisFuncEnter();
14076
14077 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
14078 /*
14079 * We don't assert below because it might happen that a non-direct session
14080 * informs us it is closed right after we've been uninitialized -- it's ok.
14081 */
14082
14083 /* get IInternalSessionControl interface */
14084 ComPtr<IInternalSessionControl> control(aSession);
14085
14086 ComAssertRet(!control.isNull(), E_INVALIDARG);
14087
14088 /* Creating a Progress object requires the VirtualBox lock, and
14089 * thus locking it here is required by the lock order rules. */
14090 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
14091
14092 if (control == mData->mSession.mDirectControl)
14093 {
14094 /* The direct session is being normally closed by the client process
14095 * ----------------------------------------------------------------- */
14096
14097 /* go to the closing state (essential for all open*Session() calls and
14098 * for #i_checkForDeath()) */
14099 Assert(mData->mSession.mState == SessionState_Locked);
14100 mData->mSession.mState = SessionState_Unlocking;
14101
14102 /* set direct control to NULL to release the remote instance */
14103 mData->mSession.mDirectControl.setNull();
14104 LogFlowThisFunc(("Direct control is set to NULL\n"));
14105
14106 if (mData->mSession.mProgress)
14107 {
14108 /* finalize the progress, someone might wait if a frontend
14109 * closes the session before powering on the VM. */
14110 mData->mSession.mProgress->notifyComplete(E_FAIL,
14111 COM_IIDOF(ISession),
14112 getComponentName(),
14113 tr("The VM session was closed before any attempt to power it on"));
14114 mData->mSession.mProgress.setNull();
14115 }
14116
14117 /* Create the progress object the client will use to wait until
14118 * #i_checkForDeath() is called to uninitialize this session object after
14119 * it releases the IPC semaphore.
14120 * Note! Because we're "reusing" mProgress here, this must be a proxy
14121 * object just like for LaunchVMProcess. */
14122 Assert(mData->mSession.mProgress.isNull());
14123 ComObjPtr<ProgressProxy> progress;
14124 progress.createObject();
14125 ComPtr<IUnknown> pPeer(mPeer);
14126 progress->init(mParent, pPeer,
14127 Bstr(tr("Closing session")).raw(),
14128 FALSE /* aCancelable */);
14129 progress.queryInterfaceTo(aProgress.asOutParam());
14130 mData->mSession.mProgress = progress;
14131 }
14132 else
14133 {
14134 /* the remote session is being normally closed */
14135 bool found = false;
14136 for (Data::Session::RemoteControlList::iterator
14137 it = mData->mSession.mRemoteControls.begin();
14138 it != mData->mSession.mRemoteControls.end();
14139 ++it)
14140 {
14141 if (control == *it)
14142 {
14143 found = true;
14144 // This MUST be erase(it), not remove(*it) as the latter
14145 // triggers a very nasty use after free due to the place where
14146 // the value "lives".
14147 mData->mSession.mRemoteControls.erase(it);
14148 break;
14149 }
14150 }
14151 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
14152 E_INVALIDARG);
14153 }
14154
14155 /* signal the client watcher thread, because the client is going away */
14156 mParent->i_updateClientWatcher();
14157
14158 LogFlowThisFuncLeave();
14159 return S_OK;
14160}
14161
14162HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14163 std::vector<com::Utf8Str> &aValues,
14164 std::vector<LONG64> &aTimestamps,
14165 std::vector<com::Utf8Str> &aFlags)
14166{
14167 LogFlowThisFunc(("\n"));
14168
14169#ifdef VBOX_WITH_GUEST_PROPS
14170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14171
14172 size_t cEntries = mHWData->mGuestProperties.size();
14173 aNames.resize(cEntries);
14174 aValues.resize(cEntries);
14175 aTimestamps.resize(cEntries);
14176 aFlags.resize(cEntries);
14177
14178 size_t i = 0;
14179 for (HWData::GuestPropertyMap::const_iterator
14180 it = mHWData->mGuestProperties.begin();
14181 it != mHWData->mGuestProperties.end();
14182 ++it, ++i)
14183 {
14184 aNames[i] = it->first;
14185 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
14186 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
14187
14188 aValues[i] = it->second.strValue;
14189 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
14190 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
14191
14192 aTimestamps[i] = it->second.mTimestamp;
14193
14194 /* If it is NULL, keep it NULL. */
14195 if (it->second.mFlags)
14196 {
14197 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
14198 GuestPropWriteFlags(it->second.mFlags, szFlags);
14199 aFlags[i] = szFlags;
14200 }
14201 else
14202 aFlags[i] = "";
14203 }
14204 return S_OK;
14205#else
14206 ReturnComNotImplemented();
14207#endif
14208}
14209
14210HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
14211 const com::Utf8Str &aValue,
14212 LONG64 aTimestamp,
14213 const com::Utf8Str &aFlags,
14214 BOOL fWasDeleted)
14215{
14216 LogFlowThisFunc(("\n"));
14217
14218#ifdef VBOX_WITH_GUEST_PROPS
14219 try
14220 {
14221 /*
14222 * Convert input up front.
14223 */
14224 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
14225 if (aFlags.length())
14226 {
14227 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
14228 AssertRCReturn(vrc, E_INVALIDARG);
14229 }
14230
14231 /*
14232 * Now grab the object lock, validate the state and do the update.
14233 */
14234
14235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14236
14237 if (!Global::IsOnline(mData->mMachineState))
14238 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
14239
14240 i_setModified(IsModified_MachineData);
14241 mHWData.backup();
14242
14243 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
14244 if (it != mHWData->mGuestProperties.end())
14245 {
14246 if (!fWasDeleted)
14247 {
14248 it->second.strValue = aValue;
14249 it->second.mTimestamp = aTimestamp;
14250 it->second.mFlags = fFlags;
14251 }
14252 else
14253 mHWData->mGuestProperties.erase(it);
14254
14255 mData->mGuestPropertiesModified = TRUE;
14256 }
14257 else if (!fWasDeleted)
14258 {
14259 HWData::GuestProperty prop;
14260 prop.strValue = aValue;
14261 prop.mTimestamp = aTimestamp;
14262 prop.mFlags = fFlags;
14263
14264 mHWData->mGuestProperties[aName] = prop;
14265 mData->mGuestPropertiesModified = TRUE;
14266 }
14267
14268 alock.release();
14269
14270 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fWasDeleted);
14271 }
14272 catch (...)
14273 {
14274 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
14275 }
14276 return S_OK;
14277#else
14278 ReturnComNotImplemented();
14279#endif
14280}
14281
14282
14283HRESULT SessionMachine::lockMedia()
14284{
14285 AutoMultiWriteLock2 alock(this->lockHandle(),
14286 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14287
14288 AssertReturn( mData->mMachineState == MachineState_Starting
14289 || mData->mMachineState == MachineState_Restoring
14290 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
14291
14292 clearError();
14293 alock.release();
14294 return i_lockMedia();
14295}
14296
14297HRESULT SessionMachine::unlockMedia()
14298{
14299 HRESULT hrc = i_unlockMedia();
14300 return hrc;
14301}
14302
14303HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14304 ComPtr<IMediumAttachment> &aNewAttachment)
14305{
14306 // request the host lock first, since might be calling Host methods for getting host drives;
14307 // next, protect the media tree all the while we're in here, as well as our member variables
14308 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
14309 this->lockHandle(),
14310 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14311
14312 IMediumAttachment *iAttach = aAttachment;
14313 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
14314
14315 Utf8Str ctrlName;
14316 LONG lPort;
14317 LONG lDevice;
14318 bool fTempEject;
14319 {
14320 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14321
14322 /* Need to query the details first, as the IMediumAttachment reference
14323 * might be to the original settings, which we are going to change. */
14324 ctrlName = pAttach->i_getControllerName();
14325 lPort = pAttach->i_getPort();
14326 lDevice = pAttach->i_getDevice();
14327 fTempEject = pAttach->i_getTempEject();
14328 }
14329
14330 if (!fTempEject)
14331 {
14332 /* Remember previously mounted medium. The medium before taking the
14333 * backup is not necessarily the same thing. */
14334 ComObjPtr<Medium> oldmedium;
14335 oldmedium = pAttach->i_getMedium();
14336
14337 i_setModified(IsModified_Storage);
14338 mMediumAttachments.backup();
14339
14340 // The backup operation makes the pAttach reference point to the
14341 // old settings. Re-get the correct reference.
14342 pAttach = i_findAttachment(*mMediumAttachments.data(),
14343 ctrlName,
14344 lPort,
14345 lDevice);
14346
14347 {
14348 AutoCaller autoAttachCaller(this);
14349 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
14350
14351 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14352 if (!oldmedium.isNull())
14353 oldmedium->i_removeBackReference(mData->mUuid);
14354
14355 pAttach->i_updateMedium(NULL);
14356 pAttach->i_updateEjected();
14357 }
14358
14359 i_setModified(IsModified_Storage);
14360 }
14361 else
14362 {
14363 {
14364 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14365 pAttach->i_updateEjected();
14366 }
14367 }
14368
14369 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
14370
14371 return S_OK;
14372}
14373
14374HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
14375 com::Utf8Str &aResult)
14376{
14377 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14378
14379 HRESULT hr = S_OK;
14380
14381 if (!mAuthLibCtx.hAuthLibrary)
14382 {
14383 /* Load the external authentication library. */
14384 Bstr authLibrary;
14385 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
14386
14387 Utf8Str filename = authLibrary;
14388
14389 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
14390 if (RT_FAILURE(vrc))
14391 hr = setErrorBoth(E_FAIL, vrc,
14392 tr("Could not load the external authentication library '%s' (%Rrc)"),
14393 filename.c_str(), vrc);
14394 }
14395
14396 /* The auth library might need the machine lock. */
14397 alock.release();
14398
14399 if (FAILED(hr))
14400 return hr;
14401
14402 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
14403 {
14404 enum VRDEAuthParams
14405 {
14406 parmUuid = 1,
14407 parmGuestJudgement,
14408 parmUser,
14409 parmPassword,
14410 parmDomain,
14411 parmClientId
14412 };
14413
14414 AuthResult result = AuthResultAccessDenied;
14415
14416 Guid uuid(aAuthParams[parmUuid]);
14417 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
14418 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
14419
14420 result = AuthLibAuthenticate(&mAuthLibCtx,
14421 uuid.raw(), guestJudgement,
14422 aAuthParams[parmUser].c_str(),
14423 aAuthParams[parmPassword].c_str(),
14424 aAuthParams[parmDomain].c_str(),
14425 u32ClientId);
14426
14427 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
14428 size_t cbPassword = aAuthParams[parmPassword].length();
14429 if (cbPassword)
14430 {
14431 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
14432 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
14433 }
14434
14435 if (result == AuthResultAccessGranted)
14436 aResult = "granted";
14437 else
14438 aResult = "denied";
14439
14440 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
14441 aAuthParams[parmUser].c_str(), aResult.c_str()));
14442 }
14443 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
14444 {
14445 enum VRDEAuthDisconnectParams
14446 {
14447 parmUuid = 1,
14448 parmClientId
14449 };
14450
14451 Guid uuid(aAuthParams[parmUuid]);
14452 uint32_t u32ClientId = 0;
14453 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
14454 }
14455 else
14456 {
14457 hr = E_INVALIDARG;
14458 }
14459
14460 return hr;
14461}
14462
14463// public methods only for internal purposes
14464/////////////////////////////////////////////////////////////////////////////
14465
14466#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14467/**
14468 * Called from the client watcher thread to check for expected or unexpected
14469 * death of the client process that has a direct session to this machine.
14470 *
14471 * On Win32 and on OS/2, this method is called only when we've got the
14472 * mutex (i.e. the client has either died or terminated normally) so it always
14473 * returns @c true (the client is terminated, the session machine is
14474 * uninitialized).
14475 *
14476 * On other platforms, the method returns @c true if the client process has
14477 * terminated normally or abnormally and the session machine was uninitialized,
14478 * and @c false if the client process is still alive.
14479 *
14480 * @note Locks this object for writing.
14481 */
14482bool SessionMachine::i_checkForDeath()
14483{
14484 Uninit::Reason reason;
14485 bool terminated = false;
14486
14487 /* Enclose autoCaller with a block because calling uninit() from under it
14488 * will deadlock. */
14489 {
14490 AutoCaller autoCaller(this);
14491 if (!autoCaller.isOk())
14492 {
14493 /* return true if not ready, to cause the client watcher to exclude
14494 * the corresponding session from watching */
14495 LogFlowThisFunc(("Already uninitialized!\n"));
14496 return true;
14497 }
14498
14499 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14500
14501 /* Determine the reason of death: if the session state is Closing here,
14502 * everything is fine. Otherwise it means that the client did not call
14503 * OnSessionEnd() before it released the IPC semaphore. This may happen
14504 * either because the client process has abnormally terminated, or
14505 * because it simply forgot to call ISession::Close() before exiting. We
14506 * threat the latter also as an abnormal termination (see
14507 * Session::uninit() for details). */
14508 reason = mData->mSession.mState == SessionState_Unlocking ?
14509 Uninit::Normal :
14510 Uninit::Abnormal;
14511
14512 if (mClientToken)
14513 terminated = mClientToken->release();
14514 } /* AutoCaller block */
14515
14516 if (terminated)
14517 uninit(reason);
14518
14519 return terminated;
14520}
14521
14522void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14523{
14524 LogFlowThisFunc(("\n"));
14525
14526 strTokenId.setNull();
14527
14528 AutoCaller autoCaller(this);
14529 AssertComRCReturnVoid(autoCaller.rc());
14530
14531 Assert(mClientToken);
14532 if (mClientToken)
14533 mClientToken->getId(strTokenId);
14534}
14535#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14536IToken *SessionMachine::i_getToken()
14537{
14538 LogFlowThisFunc(("\n"));
14539
14540 AutoCaller autoCaller(this);
14541 AssertComRCReturn(autoCaller.rc(), NULL);
14542
14543 Assert(mClientToken);
14544 if (mClientToken)
14545 return mClientToken->getToken();
14546 else
14547 return NULL;
14548}
14549#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14550
14551Machine::ClientToken *SessionMachine::i_getClientToken()
14552{
14553 LogFlowThisFunc(("\n"));
14554
14555 AutoCaller autoCaller(this);
14556 AssertComRCReturn(autoCaller.rc(), NULL);
14557
14558 return mClientToken;
14559}
14560
14561
14562/**
14563 * @note Locks this object for reading.
14564 */
14565HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14566{
14567 LogFlowThisFunc(("\n"));
14568
14569 AutoCaller autoCaller(this);
14570 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14571
14572 ComPtr<IInternalSessionControl> directControl;
14573 {
14574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14575 if (mData->mSession.mLockType == LockType_VM)
14576 directControl = mData->mSession.mDirectControl;
14577 }
14578
14579 /* ignore notifications sent after #OnSessionEnd() is called */
14580 if (!directControl)
14581 return S_OK;
14582
14583 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14584}
14585
14586/**
14587 * @note Locks this object for reading.
14588 */
14589HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
14590 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
14591 const Utf8Str &aGuestIp, LONG aGuestPort)
14592{
14593 LogFlowThisFunc(("\n"));
14594
14595 AutoCaller autoCaller(this);
14596 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14597
14598 ComPtr<IInternalSessionControl> directControl;
14599 {
14600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14601 if (mData->mSession.mLockType == LockType_VM)
14602 directControl = mData->mSession.mDirectControl;
14603 }
14604
14605 /* ignore notifications sent after #OnSessionEnd() is called */
14606 if (!directControl)
14607 return S_OK;
14608 /*
14609 * instead acting like callback we ask IVirtualBox deliver corresponding event
14610 */
14611
14612 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14613 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14614 return S_OK;
14615}
14616
14617/**
14618 * @note Locks this object for reading.
14619 */
14620HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14621{
14622 LogFlowThisFunc(("\n"));
14623
14624 AutoCaller autoCaller(this);
14625 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14626
14627 ComPtr<IInternalSessionControl> directControl;
14628 {
14629 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14630 if (mData->mSession.mLockType == LockType_VM)
14631 directControl = mData->mSession.mDirectControl;
14632 }
14633
14634 /* ignore notifications sent after #OnSessionEnd() is called */
14635 if (!directControl)
14636 return S_OK;
14637
14638 return directControl->OnAudioAdapterChange(audioAdapter);
14639}
14640
14641/**
14642 * @note Locks this object for reading.
14643 */
14644HRESULT SessionMachine::i_onHostAudioDeviceChange(IHostAudioDevice *aDevice, BOOL aNew, AudioDeviceState_T aState, IVirtualBoxErrorInfo *aErrInfo)
14645{
14646 LogFlowThisFunc(("\n"));
14647
14648 AutoCaller autoCaller(this);
14649 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14650
14651 ComPtr<IInternalSessionControl> directControl;
14652 {
14653 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14654 if (mData->mSession.mLockType == LockType_VM)
14655 directControl = mData->mSession.mDirectControl;
14656 }
14657
14658 /* ignore notifications sent after #OnSessionEnd() is called */
14659 if (!directControl)
14660 return S_OK;
14661
14662 return directControl->OnHostAudioDeviceChange(aDevice, aNew, aState, aErrInfo);
14663}
14664
14665/**
14666 * @note Locks this object for reading.
14667 */
14668HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14669{
14670 LogFlowThisFunc(("\n"));
14671
14672 AutoCaller autoCaller(this);
14673 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14674
14675 ComPtr<IInternalSessionControl> directControl;
14676 {
14677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14678 if (mData->mSession.mLockType == LockType_VM)
14679 directControl = mData->mSession.mDirectControl;
14680 }
14681
14682 /* ignore notifications sent after #OnSessionEnd() is called */
14683 if (!directControl)
14684 return S_OK;
14685
14686 return directControl->OnSerialPortChange(serialPort);
14687}
14688
14689/**
14690 * @note Locks this object for reading.
14691 */
14692HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14693{
14694 LogFlowThisFunc(("\n"));
14695
14696 AutoCaller autoCaller(this);
14697 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14698
14699 ComPtr<IInternalSessionControl> directControl;
14700 {
14701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14702 if (mData->mSession.mLockType == LockType_VM)
14703 directControl = mData->mSession.mDirectControl;
14704 }
14705
14706 /* ignore notifications sent after #OnSessionEnd() is called */
14707 if (!directControl)
14708 return S_OK;
14709
14710 return directControl->OnParallelPortChange(parallelPort);
14711}
14712
14713/**
14714 * @note Locks this object for reading.
14715 */
14716HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14717{
14718 LogFlowThisFunc(("\n"));
14719
14720 AutoCaller autoCaller(this);
14721 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14722
14723 ComPtr<IInternalSessionControl> directControl;
14724 {
14725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14726 if (mData->mSession.mLockType == LockType_VM)
14727 directControl = mData->mSession.mDirectControl;
14728 }
14729
14730 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14731
14732 /* ignore notifications sent after #OnSessionEnd() is called */
14733 if (!directControl)
14734 return S_OK;
14735
14736 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14737}
14738
14739/**
14740 * @note Locks this object for reading.
14741 */
14742HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14743{
14744 LogFlowThisFunc(("\n"));
14745
14746 AutoCaller autoCaller(this);
14747 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14748
14749 ComPtr<IInternalSessionControl> directControl;
14750 {
14751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14752 if (mData->mSession.mLockType == LockType_VM)
14753 directControl = mData->mSession.mDirectControl;
14754 }
14755
14756 mParent->i_onMediumChanged(aAttachment);
14757
14758 /* ignore notifications sent after #OnSessionEnd() is called */
14759 if (!directControl)
14760 return S_OK;
14761
14762 return directControl->OnMediumChange(aAttachment, aForce);
14763}
14764
14765HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14766{
14767 LogFlowThisFunc(("\n"));
14768
14769 AutoCaller autoCaller(this);
14770 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14771
14772 ComPtr<IInternalSessionControl> directControl;
14773 {
14774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14775 if (mData->mSession.mLockType == LockType_VM)
14776 directControl = mData->mSession.mDirectControl;
14777 }
14778
14779 /* ignore notifications sent after #OnSessionEnd() is called */
14780 if (!directControl)
14781 return S_OK;
14782
14783 return directControl->OnVMProcessPriorityChange(aPriority);
14784}
14785
14786/**
14787 * @note Locks this object for reading.
14788 */
14789HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14790{
14791 LogFlowThisFunc(("\n"));
14792
14793 AutoCaller autoCaller(this);
14794 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14795
14796 ComPtr<IInternalSessionControl> directControl;
14797 {
14798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14799 if (mData->mSession.mLockType == LockType_VM)
14800 directControl = mData->mSession.mDirectControl;
14801 }
14802
14803 /* ignore notifications sent after #OnSessionEnd() is called */
14804 if (!directControl)
14805 return S_OK;
14806
14807 return directControl->OnCPUChange(aCPU, aRemove);
14808}
14809
14810HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14811{
14812 LogFlowThisFunc(("\n"));
14813
14814 AutoCaller autoCaller(this);
14815 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14816
14817 ComPtr<IInternalSessionControl> directControl;
14818 {
14819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14820 if (mData->mSession.mLockType == LockType_VM)
14821 directControl = mData->mSession.mDirectControl;
14822 }
14823
14824 /* ignore notifications sent after #OnSessionEnd() is called */
14825 if (!directControl)
14826 return S_OK;
14827
14828 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14829}
14830
14831/**
14832 * @note Locks this object for reading.
14833 */
14834HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14835{
14836 LogFlowThisFunc(("\n"));
14837
14838 AutoCaller autoCaller(this);
14839 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14840
14841 ComPtr<IInternalSessionControl> directControl;
14842 {
14843 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14844 if (mData->mSession.mLockType == LockType_VM)
14845 directControl = mData->mSession.mDirectControl;
14846 }
14847
14848 /* ignore notifications sent after #OnSessionEnd() is called */
14849 if (!directControl)
14850 return S_OK;
14851
14852 return directControl->OnVRDEServerChange(aRestart);
14853}
14854
14855/**
14856 * @note Locks this object for reading.
14857 */
14858HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14859{
14860 LogFlowThisFunc(("\n"));
14861
14862 AutoCaller autoCaller(this);
14863 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14864
14865 ComPtr<IInternalSessionControl> directControl;
14866 {
14867 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14868 if (mData->mSession.mLockType == LockType_VM)
14869 directControl = mData->mSession.mDirectControl;
14870 }
14871
14872 /* ignore notifications sent after #OnSessionEnd() is called */
14873 if (!directControl)
14874 return S_OK;
14875
14876 return directControl->OnRecordingChange(aEnable);
14877}
14878
14879/**
14880 * @note Locks this object for reading.
14881 */
14882HRESULT SessionMachine::i_onUSBControllerChange()
14883{
14884 LogFlowThisFunc(("\n"));
14885
14886 AutoCaller autoCaller(this);
14887 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14888
14889 ComPtr<IInternalSessionControl> directControl;
14890 {
14891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14892 if (mData->mSession.mLockType == LockType_VM)
14893 directControl = mData->mSession.mDirectControl;
14894 }
14895
14896 /* ignore notifications sent after #OnSessionEnd() is called */
14897 if (!directControl)
14898 return S_OK;
14899
14900 return directControl->OnUSBControllerChange();
14901}
14902
14903/**
14904 * @note Locks this object for reading.
14905 */
14906HRESULT SessionMachine::i_onSharedFolderChange()
14907{
14908 LogFlowThisFunc(("\n"));
14909
14910 AutoCaller autoCaller(this);
14911 AssertComRCReturnRC(autoCaller.rc());
14912
14913 ComPtr<IInternalSessionControl> directControl;
14914 {
14915 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14916 if (mData->mSession.mLockType == LockType_VM)
14917 directControl = mData->mSession.mDirectControl;
14918 }
14919
14920 /* ignore notifications sent after #OnSessionEnd() is called */
14921 if (!directControl)
14922 return S_OK;
14923
14924 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14925}
14926
14927/**
14928 * @note Locks this object for reading.
14929 */
14930HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14931{
14932 LogFlowThisFunc(("\n"));
14933
14934 AutoCaller autoCaller(this);
14935 AssertComRCReturnRC(autoCaller.rc());
14936
14937 ComPtr<IInternalSessionControl> directControl;
14938 {
14939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14940 if (mData->mSession.mLockType == LockType_VM)
14941 directControl = mData->mSession.mDirectControl;
14942 }
14943
14944 /* ignore notifications sent after #OnSessionEnd() is called */
14945 if (!directControl)
14946 return S_OK;
14947
14948 return directControl->OnClipboardModeChange(aClipboardMode);
14949}
14950
14951/**
14952 * @note Locks this object for reading.
14953 */
14954HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14955{
14956 LogFlowThisFunc(("\n"));
14957
14958 AutoCaller autoCaller(this);
14959 AssertComRCReturnRC(autoCaller.rc());
14960
14961 ComPtr<IInternalSessionControl> directControl;
14962 {
14963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14964 if (mData->mSession.mLockType == LockType_VM)
14965 directControl = mData->mSession.mDirectControl;
14966 }
14967
14968 /* ignore notifications sent after #OnSessionEnd() is called */
14969 if (!directControl)
14970 return S_OK;
14971
14972 return directControl->OnClipboardFileTransferModeChange(aEnable);
14973}
14974
14975/**
14976 * @note Locks this object for reading.
14977 */
14978HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14979{
14980 LogFlowThisFunc(("\n"));
14981
14982 AutoCaller autoCaller(this);
14983 AssertComRCReturnRC(autoCaller.rc());
14984
14985 ComPtr<IInternalSessionControl> directControl;
14986 {
14987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14988 if (mData->mSession.mLockType == LockType_VM)
14989 directControl = mData->mSession.mDirectControl;
14990 }
14991
14992 /* ignore notifications sent after #OnSessionEnd() is called */
14993 if (!directControl)
14994 return S_OK;
14995
14996 return directControl->OnDnDModeChange(aDnDMode);
14997}
14998
14999/**
15000 * @note Locks this object for reading.
15001 */
15002HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
15003{
15004 LogFlowThisFunc(("\n"));
15005
15006 AutoCaller autoCaller(this);
15007 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15008
15009 ComPtr<IInternalSessionControl> directControl;
15010 {
15011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15012 if (mData->mSession.mLockType == LockType_VM)
15013 directControl = mData->mSession.mDirectControl;
15014 }
15015
15016 /* ignore notifications sent after #OnSessionEnd() is called */
15017 if (!directControl)
15018 return S_OK;
15019
15020 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
15021}
15022
15023/**
15024 * @note Locks this object for reading.
15025 */
15026HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
15027{
15028 LogFlowThisFunc(("\n"));
15029
15030 AutoCaller autoCaller(this);
15031 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15032
15033 ComPtr<IInternalSessionControl> directControl;
15034 {
15035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15036 if (mData->mSession.mLockType == LockType_VM)
15037 directControl = mData->mSession.mDirectControl;
15038 }
15039
15040 /* ignore notifications sent after #OnSessionEnd() is called */
15041 if (!directControl)
15042 return S_OK;
15043
15044 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
15045}
15046
15047/**
15048 * @note Locks this object for reading.
15049 */
15050HRESULT SessionMachine::i_onGuestDebugControlChange(IGuestDebugControl *guestDebugControl)
15051{
15052 LogFlowThisFunc(("\n"));
15053
15054 AutoCaller autoCaller(this);
15055 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15056
15057 ComPtr<IInternalSessionControl> directControl;
15058 {
15059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15060 if (mData->mSession.mLockType == LockType_VM)
15061 directControl = mData->mSession.mDirectControl;
15062 }
15063
15064 /* ignore notifications sent after #OnSessionEnd() is called */
15065 if (!directControl)
15066 return S_OK;
15067
15068 return directControl->OnGuestDebugControlChange(guestDebugControl);
15069}
15070
15071/**
15072 * Returns @c true if this machine's USB controller reports it has a matching
15073 * filter for the given USB device and @c false otherwise.
15074 *
15075 * @note locks this object for reading.
15076 */
15077bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
15078{
15079 AutoCaller autoCaller(this);
15080 /* silently return if not ready -- this method may be called after the
15081 * direct machine session has been called */
15082 if (!autoCaller.isOk())
15083 return false;
15084
15085#ifdef VBOX_WITH_USB
15086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15087
15088 switch (mData->mMachineState)
15089 {
15090 case MachineState_Starting:
15091 case MachineState_Restoring:
15092 case MachineState_TeleportingIn:
15093 case MachineState_Paused:
15094 case MachineState_Running:
15095 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
15096 * elsewhere... */
15097 alock.release();
15098 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
15099 default: break;
15100 }
15101#else
15102 NOREF(aDevice);
15103 NOREF(aMaskedIfs);
15104#endif
15105 return false;
15106}
15107
15108/**
15109 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
15110 */
15111HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
15112 IVirtualBoxErrorInfo *aError,
15113 ULONG aMaskedIfs,
15114 const com::Utf8Str &aCaptureFilename)
15115{
15116 LogFlowThisFunc(("\n"));
15117
15118 AutoCaller autoCaller(this);
15119
15120 /* This notification may happen after the machine object has been
15121 * uninitialized (the session was closed), so don't assert. */
15122 if (FAILED(autoCaller.rc())) return autoCaller.rc();
15123
15124 ComPtr<IInternalSessionControl> directControl;
15125 {
15126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15127 if (mData->mSession.mLockType == LockType_VM)
15128 directControl = mData->mSession.mDirectControl;
15129 }
15130
15131 /* fail on notifications sent after #OnSessionEnd() is called, it is
15132 * expected by the caller */
15133 if (!directControl)
15134 return E_FAIL;
15135
15136 /* No locks should be held at this point. */
15137 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
15138 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
15139
15140 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
15141}
15142
15143/**
15144 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
15145 */
15146HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
15147 IVirtualBoxErrorInfo *aError)
15148{
15149 LogFlowThisFunc(("\n"));
15150
15151 AutoCaller autoCaller(this);
15152
15153 /* This notification may happen after the machine object has been
15154 * uninitialized (the session was closed), so don't assert. */
15155 if (FAILED(autoCaller.rc())) return autoCaller.rc();
15156
15157 ComPtr<IInternalSessionControl> directControl;
15158 {
15159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15160 if (mData->mSession.mLockType == LockType_VM)
15161 directControl = mData->mSession.mDirectControl;
15162 }
15163
15164 /* fail on notifications sent after #OnSessionEnd() is called, it is
15165 * expected by the caller */
15166 if (!directControl)
15167 return E_FAIL;
15168
15169 /* No locks should be held at this point. */
15170 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
15171 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
15172
15173 return directControl->OnUSBDeviceDetach(aId, aError);
15174}
15175
15176// protected methods
15177/////////////////////////////////////////////////////////////////////////////
15178
15179/**
15180 * Deletes the given file if it is no longer in use by either the current machine state
15181 * (if the machine is "saved") or any of the machine's snapshots.
15182 *
15183 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
15184 * but is different for each SnapshotMachine. When calling this, the order of calling this
15185 * function on the one hand and changing that variable OR the snapshots tree on the other hand
15186 * is therefore critical. I know, it's all rather messy.
15187 *
15188 * @param strStateFile
15189 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
15190 * the test for whether the saved state file is in use.
15191 */
15192void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
15193 Snapshot *pSnapshotToIgnore)
15194{
15195 // it is safe to delete this saved state file if it is not currently in use by the machine ...
15196 if ( (strStateFile.isNotEmpty())
15197 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
15198 )
15199 // ... and it must also not be shared with other snapshots
15200 if ( !mData->mFirstSnapshot
15201 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
15202 // this checks the SnapshotMachine's state file paths
15203 )
15204 i_deleteFile(strStateFile, true /* fIgnoreFailures */);
15205}
15206
15207/**
15208 * Locks the attached media.
15209 *
15210 * All attached hard disks are locked for writing and DVD/floppy are locked for
15211 * reading. Parents of attached hard disks (if any) are locked for reading.
15212 *
15213 * This method also performs accessibility check of all media it locks: if some
15214 * media is inaccessible, the method will return a failure and a bunch of
15215 * extended error info objects per each inaccessible medium.
15216 *
15217 * Note that this method is atomic: if it returns a success, all media are
15218 * locked as described above; on failure no media is locked at all (all
15219 * succeeded individual locks will be undone).
15220 *
15221 * The caller is responsible for doing the necessary state sanity checks.
15222 *
15223 * The locks made by this method must be undone by calling #unlockMedia() when
15224 * no more needed.
15225 */
15226HRESULT SessionMachine::i_lockMedia()
15227{
15228 AutoCaller autoCaller(this);
15229 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15230
15231 AutoMultiWriteLock2 alock(this->lockHandle(),
15232 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
15233
15234 /* bail out if trying to lock things with already set up locking */
15235 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
15236
15237 MultiResult mrc(S_OK);
15238
15239 /* Collect locking information for all medium objects attached to the VM. */
15240 for (MediumAttachmentList::const_iterator
15241 it = mMediumAttachments->begin();
15242 it != mMediumAttachments->end();
15243 ++it)
15244 {
15245 MediumAttachment *pAtt = *it;
15246 DeviceType_T devType = pAtt->i_getType();
15247 Medium *pMedium = pAtt->i_getMedium();
15248
15249 MediumLockList *pMediumLockList(new MediumLockList());
15250 // There can be attachments without a medium (floppy/dvd), and thus
15251 // it's impossible to create a medium lock list. It still makes sense
15252 // to have the empty medium lock list in the map in case a medium is
15253 // attached later.
15254 if (pMedium != NULL)
15255 {
15256 MediumType_T mediumType = pMedium->i_getType();
15257 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
15258 || mediumType == MediumType_Shareable;
15259 bool fIsVitalImage = (devType == DeviceType_HardDisk);
15260
15261 alock.release();
15262 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
15263 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
15264 false /* fMediumLockWriteAll */,
15265 NULL,
15266 *pMediumLockList);
15267 alock.acquire();
15268 if (FAILED(mrc))
15269 {
15270 delete pMediumLockList;
15271 mData->mSession.mLockedMedia.Clear();
15272 break;
15273 }
15274 }
15275
15276 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
15277 if (FAILED(rc))
15278 {
15279 mData->mSession.mLockedMedia.Clear();
15280 mrc = setError(rc,
15281 tr("Collecting locking information for all attached media failed"));
15282 break;
15283 }
15284 }
15285
15286 if (SUCCEEDED(mrc))
15287 {
15288 /* Now lock all media. If this fails, nothing is locked. */
15289 alock.release();
15290 HRESULT rc = mData->mSession.mLockedMedia.Lock();
15291 alock.acquire();
15292 if (FAILED(rc))
15293 {
15294 mrc = setError(rc,
15295 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
15296 }
15297 }
15298
15299 return mrc;
15300}
15301
15302/**
15303 * Undoes the locks made by by #lockMedia().
15304 */
15305HRESULT SessionMachine::i_unlockMedia()
15306{
15307 AutoCaller autoCaller(this);
15308 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15309
15310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15311
15312 /* we may be holding important error info on the current thread;
15313 * preserve it */
15314 ErrorInfoKeeper eik;
15315
15316 HRESULT rc = mData->mSession.mLockedMedia.Clear();
15317 AssertComRC(rc);
15318 return rc;
15319}
15320
15321/**
15322 * Helper to change the machine state (reimplementation).
15323 *
15324 * @note Locks this object for writing.
15325 * @note This method must not call i_saveSettings or SaveSettings, otherwise
15326 * it can cause crashes in random places due to unexpectedly committing
15327 * the current settings. The caller is responsible for that. The call
15328 * to saveStateSettings is fine, because this method does not commit.
15329 */
15330HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
15331{
15332 LogFlowThisFuncEnter();
15333
15334 AutoCaller autoCaller(this);
15335 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15336
15337 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15338
15339 MachineState_T oldMachineState = mData->mMachineState;
15340
15341 AssertMsgReturn(oldMachineState != aMachineState,
15342 ("oldMachineState=%s, aMachineState=%s\n",
15343 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
15344 E_FAIL);
15345
15346 HRESULT rc = S_OK;
15347
15348 int stsFlags = 0;
15349 bool deleteSavedState = false;
15350
15351 /* detect some state transitions */
15352
15353 if ( ( ( oldMachineState == MachineState_Saved
15354 || oldMachineState == MachineState_AbortedSaved
15355 )
15356 && aMachineState == MachineState_Restoring
15357 )
15358 || ( ( oldMachineState == MachineState_PoweredOff
15359 || oldMachineState == MachineState_Teleported
15360 || oldMachineState == MachineState_Aborted
15361 )
15362 && ( aMachineState == MachineState_TeleportingIn
15363 || aMachineState == MachineState_Starting
15364 )
15365 )
15366 )
15367 {
15368 /* The EMT thread is about to start */
15369
15370 /* Nothing to do here for now... */
15371
15372 /// @todo NEWMEDIA don't let mDVDDrive and other children
15373 /// change anything when in the Starting/Restoring state
15374 }
15375 else if ( ( oldMachineState == MachineState_Running
15376 || oldMachineState == MachineState_Paused
15377 || oldMachineState == MachineState_Teleporting
15378 || oldMachineState == MachineState_OnlineSnapshotting
15379 || oldMachineState == MachineState_LiveSnapshotting
15380 || oldMachineState == MachineState_Stuck
15381 || oldMachineState == MachineState_Starting
15382 || oldMachineState == MachineState_Stopping
15383 || oldMachineState == MachineState_Saving
15384 || oldMachineState == MachineState_Restoring
15385 || oldMachineState == MachineState_TeleportingPausedVM
15386 || oldMachineState == MachineState_TeleportingIn
15387 )
15388 && ( aMachineState == MachineState_PoweredOff
15389 || aMachineState == MachineState_Saved
15390 || aMachineState == MachineState_Teleported
15391 || aMachineState == MachineState_Aborted
15392 || aMachineState == MachineState_AbortedSaved
15393 )
15394 )
15395 {
15396 /* The EMT thread has just stopped, unlock attached media. Note that as
15397 * opposed to locking that is done from Console, we do unlocking here
15398 * because the VM process may have aborted before having a chance to
15399 * properly unlock all media it locked. */
15400
15401 unlockMedia();
15402 }
15403
15404 if (oldMachineState == MachineState_Restoring)
15405 {
15406 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
15407 {
15408 /*
15409 * delete the saved state file once the machine has finished
15410 * restoring from it (note that Console sets the state from
15411 * Restoring to AbortedSaved if the VM couldn't restore successfully,
15412 * to give the user an ability to fix an error and retry --
15413 * we keep the saved state file in this case)
15414 */
15415 deleteSavedState = true;
15416 }
15417 }
15418 else if ( oldMachineState == MachineState_Saved
15419 && ( aMachineState == MachineState_PoweredOff
15420 || aMachineState == MachineState_Teleported
15421 )
15422 )
15423 {
15424 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
15425 deleteSavedState = true;
15426 mData->mCurrentStateModified = TRUE;
15427 stsFlags |= SaveSTS_CurStateModified;
15428 }
15429 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
15430 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
15431
15432 if ( aMachineState == MachineState_Starting
15433 || aMachineState == MachineState_Restoring
15434 || aMachineState == MachineState_TeleportingIn
15435 )
15436 {
15437 /* set the current state modified flag to indicate that the current
15438 * state is no more identical to the state in the
15439 * current snapshot */
15440 if (!mData->mCurrentSnapshot.isNull())
15441 {
15442 mData->mCurrentStateModified = TRUE;
15443 stsFlags |= SaveSTS_CurStateModified;
15444 }
15445 }
15446
15447 if (deleteSavedState)
15448 {
15449 if (mRemoveSavedState)
15450 {
15451 Assert(!mSSData->strStateFilePath.isEmpty());
15452
15453 // it is safe to delete the saved state file if ...
15454 if ( !mData->mFirstSnapshot // ... we have no snapshots or
15455 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
15456 // ... none of the snapshots share the saved state file
15457 )
15458 i_deleteFile(mSSData->strStateFilePath, true /* fIgnoreFailures */);
15459 }
15460
15461 mSSData->strStateFilePath.setNull();
15462 stsFlags |= SaveSTS_StateFilePath;
15463 }
15464
15465 /* redirect to the underlying peer machine */
15466 mPeer->i_setMachineState(aMachineState);
15467
15468 if ( oldMachineState != MachineState_RestoringSnapshot
15469 && ( aMachineState == MachineState_PoweredOff
15470 || aMachineState == MachineState_Teleported
15471 || aMachineState == MachineState_Aborted
15472 || aMachineState == MachineState_AbortedSaved
15473 || aMachineState == MachineState_Saved))
15474 {
15475 /* the machine has stopped execution
15476 * (or the saved state file was adopted) */
15477 stsFlags |= SaveSTS_StateTimeStamp;
15478 }
15479
15480 if ( ( oldMachineState == MachineState_PoweredOff
15481 || oldMachineState == MachineState_Aborted
15482 || oldMachineState == MachineState_Teleported
15483 )
15484 && aMachineState == MachineState_Saved)
15485 {
15486 /* the saved state file was adopted */
15487 Assert(!mSSData->strStateFilePath.isEmpty());
15488 stsFlags |= SaveSTS_StateFilePath;
15489 }
15490
15491#ifdef VBOX_WITH_GUEST_PROPS
15492 if ( aMachineState == MachineState_PoweredOff
15493 || aMachineState == MachineState_Aborted
15494 || aMachineState == MachineState_Teleported)
15495 {
15496 /* Make sure any transient guest properties get removed from the
15497 * property store on shutdown. */
15498 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
15499
15500 /* remove it from the settings representation */
15501 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
15502 for (settings::GuestPropertiesList::iterator
15503 it = llGuestProperties.begin();
15504 it != llGuestProperties.end();
15505 /*nothing*/)
15506 {
15507 const settings::GuestProperty &prop = *it;
15508 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
15509 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
15510 {
15511 it = llGuestProperties.erase(it);
15512 fNeedsSaving = true;
15513 }
15514 else
15515 {
15516 ++it;
15517 }
15518 }
15519
15520 /* Additionally remove it from the HWData representation. Required to
15521 * keep everything in sync, as this is what the API keeps using. */
15522 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
15523 for (HWData::GuestPropertyMap::iterator
15524 it = llHWGuestProperties.begin();
15525 it != llHWGuestProperties.end();
15526 /*nothing*/)
15527 {
15528 uint32_t fFlags = it->second.mFlags;
15529 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
15530 {
15531 /* iterator where we need to continue after the erase call
15532 * (C++03 is a fact still, and it doesn't return the iterator
15533 * which would allow continuing) */
15534 HWData::GuestPropertyMap::iterator it2 = it;
15535 ++it2;
15536 llHWGuestProperties.erase(it);
15537 it = it2;
15538 fNeedsSaving = true;
15539 }
15540 else
15541 {
15542 ++it;
15543 }
15544 }
15545
15546 if (fNeedsSaving)
15547 {
15548 mData->mCurrentStateModified = TRUE;
15549 stsFlags |= SaveSTS_CurStateModified;
15550 }
15551 }
15552#endif /* VBOX_WITH_GUEST_PROPS */
15553
15554 rc = i_saveStateSettings(stsFlags);
15555
15556 if ( ( oldMachineState != MachineState_PoweredOff
15557 && oldMachineState != MachineState_Aborted
15558 && oldMachineState != MachineState_Teleported
15559 )
15560 && ( aMachineState == MachineState_PoweredOff
15561 || aMachineState == MachineState_Aborted
15562 || aMachineState == MachineState_Teleported
15563 )
15564 )
15565 {
15566 /* we've been shut down for any reason */
15567 /* no special action so far */
15568 }
15569
15570 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, ::stringifyMachineState(mData->mMachineState) ));
15571 LogFlowThisFuncLeave();
15572 return rc;
15573}
15574
15575/**
15576 * Sends the current machine state value to the VM process.
15577 *
15578 * @note Locks this object for reading, then calls a client process.
15579 */
15580HRESULT SessionMachine::i_updateMachineStateOnClient()
15581{
15582 AutoCaller autoCaller(this);
15583 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15584
15585 ComPtr<IInternalSessionControl> directControl;
15586 {
15587 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15588 AssertReturn(!!mData, E_FAIL);
15589 if (mData->mSession.mLockType == LockType_VM)
15590 directControl = mData->mSession.mDirectControl;
15591
15592 /* directControl may be already set to NULL here in #OnSessionEnd()
15593 * called too early by the direct session process while there is still
15594 * some operation (like deleting the snapshot) in progress. The client
15595 * process in this case is waiting inside Session::close() for the
15596 * "end session" process object to complete, while #uninit() called by
15597 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15598 * operation to complete. For now, we accept this inconsistent behavior
15599 * and simply do nothing here. */
15600
15601 if (mData->mSession.mState == SessionState_Unlocking)
15602 return S_OK;
15603 }
15604
15605 /* ignore notifications sent after #OnSessionEnd() is called */
15606 if (!directControl)
15607 return S_OK;
15608
15609 return directControl->UpdateMachineState(mData->mMachineState);
15610}
15611
15612
15613/*static*/
15614HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15615{
15616 va_list args;
15617 va_start(args, pcszMsg);
15618 HRESULT rc = setErrorInternalV(aResultCode,
15619 getStaticClassIID(),
15620 getStaticComponentName(),
15621 pcszMsg, args,
15622 false /* aWarning */,
15623 true /* aLogIt */);
15624 va_end(args);
15625 return rc;
15626}
15627
15628
15629HRESULT Machine::updateState(MachineState_T aState)
15630{
15631 NOREF(aState);
15632 ReturnComNotImplemented();
15633}
15634
15635HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15636{
15637 NOREF(aProgress);
15638 ReturnComNotImplemented();
15639}
15640
15641HRESULT Machine::endPowerUp(LONG aResult)
15642{
15643 NOREF(aResult);
15644 ReturnComNotImplemented();
15645}
15646
15647HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15648{
15649 NOREF(aProgress);
15650 ReturnComNotImplemented();
15651}
15652
15653HRESULT Machine::endPoweringDown(LONG aResult,
15654 const com::Utf8Str &aErrMsg)
15655{
15656 NOREF(aResult);
15657 NOREF(aErrMsg);
15658 ReturnComNotImplemented();
15659}
15660
15661HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15662 BOOL *aMatched,
15663 ULONG *aMaskedInterfaces)
15664{
15665 NOREF(aDevice);
15666 NOREF(aMatched);
15667 NOREF(aMaskedInterfaces);
15668 ReturnComNotImplemented();
15669
15670}
15671
15672HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15673{
15674 NOREF(aId); NOREF(aCaptureFilename);
15675 ReturnComNotImplemented();
15676}
15677
15678HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15679 BOOL aDone)
15680{
15681 NOREF(aId);
15682 NOREF(aDone);
15683 ReturnComNotImplemented();
15684}
15685
15686HRESULT Machine::autoCaptureUSBDevices()
15687{
15688 ReturnComNotImplemented();
15689}
15690
15691HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15692{
15693 NOREF(aDone);
15694 ReturnComNotImplemented();
15695}
15696
15697HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15698 ComPtr<IProgress> &aProgress)
15699{
15700 NOREF(aSession);
15701 NOREF(aProgress);
15702 ReturnComNotImplemented();
15703}
15704
15705HRESULT Machine::finishOnlineMergeMedium()
15706{
15707 ReturnComNotImplemented();
15708}
15709
15710HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15711 std::vector<com::Utf8Str> &aValues,
15712 std::vector<LONG64> &aTimestamps,
15713 std::vector<com::Utf8Str> &aFlags)
15714{
15715 NOREF(aNames);
15716 NOREF(aValues);
15717 NOREF(aTimestamps);
15718 NOREF(aFlags);
15719 ReturnComNotImplemented();
15720}
15721
15722HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15723 const com::Utf8Str &aValue,
15724 LONG64 aTimestamp,
15725 const com::Utf8Str &aFlags,
15726 BOOL fWasDeleted)
15727{
15728 NOREF(aName);
15729 NOREF(aValue);
15730 NOREF(aTimestamp);
15731 NOREF(aFlags);
15732 NOREF(fWasDeleted);
15733 ReturnComNotImplemented();
15734}
15735
15736HRESULT Machine::lockMedia()
15737{
15738 ReturnComNotImplemented();
15739}
15740
15741HRESULT Machine::unlockMedia()
15742{
15743 ReturnComNotImplemented();
15744}
15745
15746HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15747 ComPtr<IMediumAttachment> &aNewAttachment)
15748{
15749 NOREF(aAttachment);
15750 NOREF(aNewAttachment);
15751 ReturnComNotImplemented();
15752}
15753
15754HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15755 ULONG aCpuUser,
15756 ULONG aCpuKernel,
15757 ULONG aCpuIdle,
15758 ULONG aMemTotal,
15759 ULONG aMemFree,
15760 ULONG aMemBalloon,
15761 ULONG aMemShared,
15762 ULONG aMemCache,
15763 ULONG aPagedTotal,
15764 ULONG aMemAllocTotal,
15765 ULONG aMemFreeTotal,
15766 ULONG aMemBalloonTotal,
15767 ULONG aMemSharedTotal,
15768 ULONG aVmNetRx,
15769 ULONG aVmNetTx)
15770{
15771 NOREF(aValidStats);
15772 NOREF(aCpuUser);
15773 NOREF(aCpuKernel);
15774 NOREF(aCpuIdle);
15775 NOREF(aMemTotal);
15776 NOREF(aMemFree);
15777 NOREF(aMemBalloon);
15778 NOREF(aMemShared);
15779 NOREF(aMemCache);
15780 NOREF(aPagedTotal);
15781 NOREF(aMemAllocTotal);
15782 NOREF(aMemFreeTotal);
15783 NOREF(aMemBalloonTotal);
15784 NOREF(aMemSharedTotal);
15785 NOREF(aVmNetRx);
15786 NOREF(aVmNetTx);
15787 ReturnComNotImplemented();
15788}
15789
15790HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15791 com::Utf8Str &aResult)
15792{
15793 NOREF(aAuthParams);
15794 NOREF(aResult);
15795 ReturnComNotImplemented();
15796}
15797
15798com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15799{
15800 com::Utf8Str strControllerName = "Unknown";
15801 switch (aBusType)
15802 {
15803 case StorageBus_IDE:
15804 {
15805 strControllerName = "IDE";
15806 break;
15807 }
15808 case StorageBus_SATA:
15809 {
15810 strControllerName = "SATA";
15811 break;
15812 }
15813 case StorageBus_SCSI:
15814 {
15815 strControllerName = "SCSI";
15816 break;
15817 }
15818 case StorageBus_Floppy:
15819 {
15820 strControllerName = "Floppy";
15821 break;
15822 }
15823 case StorageBus_SAS:
15824 {
15825 strControllerName = "SAS";
15826 break;
15827 }
15828 case StorageBus_USB:
15829 {
15830 strControllerName = "USB";
15831 break;
15832 }
15833 default:
15834 break;
15835 }
15836 return strControllerName;
15837}
15838
15839HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15840{
15841 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15842
15843 AutoCaller autoCaller(this);
15844 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15845
15846 HRESULT rc = S_OK;
15847
15848 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15849 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15850 rc = getUSBDeviceFilters(usbDeviceFilters);
15851 if (FAILED(rc)) return rc;
15852
15853 NOREF(aFlags);
15854 com::Utf8Str osTypeId;
15855 ComObjPtr<GuestOSType> osType = NULL;
15856
15857 /* Get the guest os type as a string from the VB. */
15858 rc = getOSTypeId(osTypeId);
15859 if (FAILED(rc)) return rc;
15860
15861 /* Get the os type obj that coresponds, can be used to get
15862 * the defaults for this guest OS. */
15863 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15864 if (FAILED(rc)) return rc;
15865
15866 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15867
15868 /* Let the OS type select 64-bit ness. */
15869 mHWData->mLongMode = osType->i_is64Bit()
15870 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15871
15872 /* Let the OS type enable the X2APIC */
15873 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15874
15875 /* This one covers IOAPICEnabled. */
15876 mBIOSSettings->i_applyDefaults(osType);
15877
15878 /* Initialize default record settings. */
15879 mRecordingSettings->i_applyDefaults();
15880
15881 /* Initialize default BIOS settings here */
15882 /* Hardware virtualization must be ON by default */
15883 mHWData->mAPIC = true;
15884 mHWData->mHWVirtExEnabled = true;
15885
15886 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15887 if (FAILED(rc)) return rc;
15888
15889 rc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15890 if (FAILED(rc)) return rc;
15891
15892 /* Graphics stuff. */
15893 GraphicsControllerType_T graphicsController;
15894 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15895 if (FAILED(rc)) return rc;
15896
15897 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15898 if (FAILED(rc)) return rc;
15899
15900 ULONG vramSize;
15901 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15902 if (FAILED(rc)) return rc;
15903
15904 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15905 if (FAILED(rc)) return rc;
15906
15907 BOOL fAccelerate2DVideoEnabled;
15908 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15909 if (FAILED(rc)) return rc;
15910
15911 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15912 if (FAILED(rc)) return rc;
15913
15914 BOOL fAccelerate3DEnabled;
15915 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15916 if (FAILED(rc)) return rc;
15917
15918 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15919 if (FAILED(rc)) return rc;
15920
15921 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15922 if (FAILED(rc)) return rc;
15923
15924 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15925 if (FAILED(rc)) return rc;
15926
15927 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15928 if (FAILED(rc)) return rc;
15929
15930 BOOL mRTCUseUTC;
15931 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15932 if (FAILED(rc)) return rc;
15933
15934 setRTCUseUTC(mRTCUseUTC);
15935 if (FAILED(rc)) return rc;
15936
15937 /* the setter does more than just the assignment, so use it */
15938 ChipsetType_T enmChipsetType;
15939 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15940 if (FAILED(rc)) return rc;
15941
15942 rc = COMSETTER(ChipsetType)(enmChipsetType);
15943 if (FAILED(rc)) return rc;
15944
15945 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15946 if (FAILED(rc)) return rc;
15947
15948 /* Apply IOMMU defaults. */
15949 IommuType_T enmIommuType;
15950 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15951 if (FAILED(rc)) return rc;
15952
15953 rc = COMSETTER(IommuType)(enmIommuType);
15954 if (FAILED(rc)) return rc;
15955
15956 /* Apply network adapters defaults */
15957 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15958 mNetworkAdapters[slot]->i_applyDefaults(osType);
15959
15960 /* Apply serial port defaults */
15961 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15962 mSerialPorts[slot]->i_applyDefaults(osType);
15963
15964 /* Apply parallel port defaults - not OS dependent*/
15965 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15966 mParallelPorts[slot]->i_applyDefaults();
15967
15968 /* This one covers the TPM type. */
15969 mTrustedPlatformModule->i_applyDefaults(osType);
15970
15971 /* This one covers secure boot. */
15972 rc = mNvramStore->i_applyDefaults(osType);
15973 if (FAILED(rc)) return rc;
15974
15975 /* Audio stuff. */
15976 rc = mAudioSettings->i_applyDefaults(osType);
15977 if (FAILED(rc)) return rc;
15978
15979 /* Storage Controllers */
15980 StorageControllerType_T hdStorageControllerType;
15981 StorageBus_T hdStorageBusType;
15982 StorageControllerType_T dvdStorageControllerType;
15983 StorageBus_T dvdStorageBusType;
15984 BOOL recommendedFloppy;
15985 ComPtr<IStorageController> floppyController;
15986 ComPtr<IStorageController> hdController;
15987 ComPtr<IStorageController> dvdController;
15988 Utf8Str strFloppyName, strDVDName, strHDName;
15989
15990 /* GUI auto generates controller names using bus type. Do the same*/
15991 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15992
15993 /* Floppy recommended? add one. */
15994 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15995 if (FAILED(rc)) return rc;
15996 if (recommendedFloppy)
15997 {
15998 rc = addStorageController(strFloppyName,
15999 StorageBus_Floppy,
16000 floppyController);
16001 if (FAILED(rc)) return rc;
16002 }
16003
16004 /* Setup one DVD storage controller. */
16005 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
16006 if (FAILED(rc)) return rc;
16007
16008 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
16009 if (FAILED(rc)) return rc;
16010
16011 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
16012
16013 rc = addStorageController(strDVDName,
16014 dvdStorageBusType,
16015 dvdController);
16016 if (FAILED(rc)) return rc;
16017
16018 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
16019 if (FAILED(rc)) return rc;
16020
16021 /* Setup one HDD storage controller. */
16022 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
16023 if (FAILED(rc)) return rc;
16024
16025 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
16026 if (FAILED(rc)) return rc;
16027
16028 strHDName = i_controllerNameFromBusType(hdStorageBusType);
16029
16030 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
16031 {
16032 rc = addStorageController(strHDName,
16033 hdStorageBusType,
16034 hdController);
16035 if (FAILED(rc)) return rc;
16036
16037 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
16038 if (FAILED(rc)) return rc;
16039 }
16040 else
16041 {
16042 /* The HD controller is the same as DVD: */
16043 hdController = dvdController;
16044 }
16045
16046 /* Limit the AHCI port count if it's used because windows has trouble with
16047 * too many ports and other guest (OS X in particular) may take extra long
16048 * boot: */
16049
16050 // pParent = static_cast<Medium*>(aP)
16051 IStorageController *temp = hdController;
16052 ComObjPtr<StorageController> storageController;
16053 storageController = static_cast<StorageController *>(temp);
16054
16055 // tempHDController = aHDController;
16056 if (hdStorageControllerType == StorageControllerType_IntelAhci)
16057 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
16058 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
16059 storageController->COMSETTER(PortCount)(1);
16060
16061 /* USB stuff */
16062
16063 bool ohciEnabled = false;
16064
16065 ComPtr<IUSBController> usbController;
16066 BOOL recommendedUSB3;
16067 BOOL recommendedUSB;
16068 BOOL usbProxyAvailable;
16069
16070 getUSBProxyAvailable(&usbProxyAvailable);
16071 if (FAILED(rc)) return rc;
16072
16073 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
16074 if (FAILED(rc)) return rc;
16075 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
16076 if (FAILED(rc)) return rc;
16077
16078 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
16079 {
16080 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
16081 if (FAILED(rc)) return rc;
16082
16083 /* xHci includes OHCI */
16084 ohciEnabled = true;
16085 }
16086 if ( !ohciEnabled
16087 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
16088 {
16089 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16090 if (FAILED(rc)) return rc;
16091 ohciEnabled = true;
16092
16093 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
16094 if (FAILED(rc)) return rc;
16095 }
16096
16097 /* Set recommended human interface device types: */
16098 BOOL recommendedUSBHID;
16099 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
16100 if (FAILED(rc)) return rc;
16101
16102 if (recommendedUSBHID)
16103 {
16104 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
16105 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
16106 if (!ohciEnabled && !usbDeviceFilters.isNull())
16107 {
16108 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16109 if (FAILED(rc)) return rc;
16110 }
16111 }
16112
16113 BOOL recommendedUSBTablet;
16114 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
16115 if (FAILED(rc)) return rc;
16116
16117 if (recommendedUSBTablet)
16118 {
16119 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
16120 if (!ohciEnabled && !usbDeviceFilters.isNull())
16121 {
16122 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16123 if (FAILED(rc)) return rc;
16124 }
16125 }
16126
16127 /* Enable the VMMDev testing feature for bootsector VMs: */
16128 if (osTypeId == "VBoxBS_64")
16129 {
16130 rc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
16131 if (FAILED(rc))
16132 return rc;
16133 }
16134
16135 return S_OK;
16136}
16137
16138#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
16139/**
16140 * Task record for change encryption settins.
16141 */
16142class Machine::ChangeEncryptionTask
16143 : public Machine::Task
16144{
16145public:
16146 ChangeEncryptionTask(Machine *m,
16147 Progress *p,
16148 const Utf8Str &t,
16149 const com::Utf8Str &aCurrentPassword,
16150 const com::Utf8Str &aCipher,
16151 const com::Utf8Str &aNewPassword,
16152 const com::Utf8Str &aNewPasswordId,
16153 const BOOL aForce,
16154 const MediaList &llMedia)
16155 : Task(m, p, t),
16156 mstrNewPassword(aNewPassword),
16157 mstrCurrentPassword(aCurrentPassword),
16158 mstrCipher(aCipher),
16159 mstrNewPasswordId(aNewPasswordId),
16160 mForce(aForce),
16161 mllMedia(llMedia)
16162 {}
16163
16164 ~ChangeEncryptionTask()
16165 {
16166 if (mstrNewPassword.length())
16167 RTMemWipeThoroughly(mstrNewPassword.mutableRaw(), mstrNewPassword.length(), 10 /* cPasses */);
16168 if (mstrCurrentPassword.length())
16169 RTMemWipeThoroughly(mstrCurrentPassword.mutableRaw(), mstrCurrentPassword.length(), 10 /* cPasses */);
16170 if (m_pCryptoIf)
16171 {
16172 m_pMachine->i_getVirtualBox()->i_releaseCryptoIf(m_pCryptoIf);
16173 m_pCryptoIf = NULL;
16174 }
16175 }
16176
16177 Utf8Str mstrNewPassword;
16178 Utf8Str mstrCurrentPassword;
16179 Utf8Str mstrCipher;
16180 Utf8Str mstrNewPasswordId;
16181 BOOL mForce;
16182 MediaList mllMedia;
16183 PCVBOXCRYPTOIF m_pCryptoIf;
16184private:
16185 void handler()
16186 {
16187 try
16188 {
16189 m_pMachine->i_changeEncryptionHandler(*this);
16190 }
16191 catch (...)
16192 {
16193 LogRel(("Some exception in the function Machine::i_changeEncryptionHandler()\n"));
16194 }
16195 }
16196
16197 friend void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task);
16198};
16199
16200/**
16201 * Scans specified directory and fills list by files found
16202 *
16203 * @returns VBox status code.
16204 * @param lstFiles
16205 * @param strDir
16206 * @param filePattern
16207 */
16208int Machine::i_findFiles(std::list<com::Utf8Str> &lstFiles, const com::Utf8Str &strDir,
16209 const com::Utf8Str &strPattern)
16210{
16211 /* To get all entries including subdirectories. */
16212 char *pszFilePattern = RTPathJoinA(strDir.c_str(), "*");
16213 if (!pszFilePattern)
16214 return VERR_NO_STR_MEMORY;
16215
16216 PRTDIRENTRYEX pDirEntry = NULL;
16217 RTDIR hDir;
16218 size_t cbDirEntry = sizeof(RTDIRENTRYEX);
16219 int rc = RTDirOpenFiltered(&hDir, pszFilePattern, RTDIRFILTER_WINNT, 0 /*fFlags*/);
16220 if (RT_SUCCESS(rc))
16221 {
16222 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
16223 if (pDirEntry)
16224 {
16225 while ( (rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK))
16226 != VERR_NO_MORE_FILES)
16227 {
16228 char *pszFilePath = NULL;
16229
16230 if (rc == VERR_BUFFER_OVERFLOW)
16231 {
16232 /* allocate new buffer. */
16233 RTMemFree(pDirEntry);
16234 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbDirEntry);
16235 if (!pDirEntry)
16236 {
16237 rc = VERR_NO_MEMORY;
16238 break;
16239 }
16240 /* Retry. */
16241 rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
16242 if (RT_FAILURE(rc))
16243 break;
16244 }
16245 else if (RT_FAILURE(rc))
16246 break;
16247
16248 /* Exclude . and .. */
16249 if ( (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '\0')
16250 || (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '.' && pDirEntry->szName[2] == '\0'))
16251 continue;
16252 if (RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode))
16253 {
16254 char *pszSubDirPath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
16255 if (!pszSubDirPath)
16256 {
16257 rc = VERR_NO_STR_MEMORY;
16258 break;
16259 }
16260 rc = i_findFiles(lstFiles, pszSubDirPath, strPattern);
16261 RTMemFree(pszSubDirPath);
16262 if (RT_FAILURE(rc))
16263 break;
16264 continue;
16265 }
16266
16267 /* We got the new entry. */
16268 if (!RTFS_IS_FILE(pDirEntry->Info.Attr.fMode))
16269 continue;
16270
16271 if (!RTStrSimplePatternMatch(strPattern.c_str(), pDirEntry->szName))
16272 continue;
16273
16274 /* Prepend the path to the libraries. */
16275 pszFilePath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
16276 if (!pszFilePath)
16277 {
16278 rc = VERR_NO_STR_MEMORY;
16279 break;
16280 }
16281
16282 lstFiles.push_back(pszFilePath);
16283 RTStrFree(pszFilePath);
16284 }
16285
16286 RTMemFree(pDirEntry);
16287 }
16288 else
16289 rc = VERR_NO_MEMORY;
16290
16291 RTDirClose(hDir);
16292 }
16293 else
16294 {
16295 /* On Windows the above immediately signals that there are no
16296 * files matching, while on other platforms enumerating the
16297 * files below fails. Either way: stop searching. */
16298 }
16299
16300 if ( rc == VERR_NO_MORE_FILES
16301 || rc == VERR_FILE_NOT_FOUND
16302 || rc == VERR_PATH_NOT_FOUND)
16303 rc = VINF_SUCCESS;
16304 RTStrFree(pszFilePattern);
16305 return rc;
16306}
16307
16308/**
16309 * Helper to set up an I/O stream to read or write a possibly encrypted file.
16310 *
16311 * @returns VBox status code.
16312 * @param pszFilename The file to open.
16313 * @param pCryptoIf Pointer to the cryptographic interface if the file should be encrypted or contains encrypted data.
16314 * @param pszKeyStore The keystore if the file should be encrypted or contains encrypted data.
16315 * @param pszPassword The password if the file should be encrypted or contains encrypted data.
16316 * @param fOpen The open flags for the file.
16317 * @param phVfsIos Where to store the handle to the I/O stream on success.
16318 */
16319int Machine::i_createIoStreamForFile(const char *pszFilename, PCVBOXCRYPTOIF pCryptoIf,
16320 const char *pszKeyStore, const char *pszPassword,
16321 uint64_t fOpen, PRTVFSIOSTREAM phVfsIos)
16322{
16323 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
16324 int vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
16325 if (RT_SUCCESS(vrc))
16326 {
16327 if (pCryptoIf)
16328 {
16329 RTVFSFILE hVfsFileCrypto = NIL_RTVFSFILE;
16330 vrc = pCryptoIf->pfnCryptoFileFromVfsFile(hVfsFile, pszKeyStore, pszPassword, &hVfsFileCrypto);
16331 if (RT_SUCCESS(vrc))
16332 {
16333 RTVfsFileRelease(hVfsFile);
16334 hVfsFile = hVfsFileCrypto;
16335 }
16336 }
16337
16338 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
16339 RTVfsFileRelease(hVfsFile);
16340 }
16341
16342 return vrc;
16343}
16344
16345/**
16346 * Helper function processing all actions for one component (saved state files,
16347 * NVRAM files, etc). Used by Machine::i_changeEncryptionHandler only.
16348 *
16349 * @param task
16350 * @param strDirectory
16351 * @param strFilePattern
16352 * @param strMagic
16353 * @param strKeyStore
16354 * @param strKeyId
16355 * @return
16356 */
16357HRESULT Machine::i_changeEncryptionForComponent(ChangeEncryptionTask &task, const com::Utf8Str strDirectory,
16358 const com::Utf8Str strFilePattern, com::Utf8Str &strKeyStore,
16359 com::Utf8Str &strKeyId, int iCipherMode)
16360{
16361 bool fDecrypt = task.mstrCurrentPassword.isNotEmpty()
16362 && task.mstrCipher.isEmpty()
16363 && task.mstrNewPassword.isEmpty()
16364 && task.mstrNewPasswordId.isEmpty();
16365 bool fEncrypt = task.mstrCurrentPassword.isEmpty()
16366 && task.mstrCipher.isNotEmpty()
16367 && task.mstrNewPassword.isNotEmpty()
16368 && task.mstrNewPasswordId.isNotEmpty();
16369
16370 /* check if the cipher is changed which causes the reencryption*/
16371
16372 const char *pszTaskCipher = NULL;
16373 if (task.mstrCipher.isNotEmpty())
16374 pszTaskCipher = getCipherString(task.mstrCipher.c_str(), iCipherMode);
16375
16376 if (!task.mForce && !fDecrypt && !fEncrypt)
16377 {
16378 char *pszCipher = NULL;
16379 int vrc = task.m_pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(strKeyStore.c_str(),
16380 NULL /*pszPassword*/,
16381 NULL /*ppbKey*/,
16382 NULL /*pcbKey*/,
16383 &pszCipher);
16384 if (RT_SUCCESS(vrc))
16385 {
16386 task.mForce = strcmp(pszTaskCipher, pszCipher) != 0;
16387 RTMemFree(pszCipher);
16388 }
16389 else
16390 return setErrorBoth(E_FAIL, vrc, tr("Obtain cipher for '%s' files failed (%Rrc)"),
16391 strFilePattern.c_str(), vrc);
16392 }
16393
16394 /* Only the password needs to be changed */
16395 if (!task.mForce && !fDecrypt && !fEncrypt)
16396 {
16397 Assert(task.m_pCryptoIf);
16398
16399 VBOXCRYPTOCTX hCryptoCtx;
16400 int vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(strKeyStore.c_str(), task.mstrCurrentPassword.c_str(), &hCryptoCtx);
16401 if (RT_FAILURE(vrc))
16402 return setErrorBoth(E_FAIL, vrc, tr("Loading old key store for '%s' files failed, (%Rrc)"),
16403 strFilePattern.c_str(), vrc);
16404 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
16405 if (RT_FAILURE(vrc))
16406 return setErrorBoth(E_FAIL, vrc, tr("Changing the password for '%s' files failed, (%Rrc)"),
16407 strFilePattern.c_str(), vrc);
16408
16409 char *pszKeyStore = NULL;
16410 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16411 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16412 if (RT_FAILURE(vrc))
16413 return setErrorBoth(E_FAIL, vrc, tr("Saving the key store for '%s' files failed, (%Rrc)"),
16414 strFilePattern.c_str(), vrc);
16415 strKeyStore = pszKeyStore;
16416 RTMemFree(pszKeyStore);
16417 strKeyId = task.mstrNewPasswordId;
16418 return S_OK;
16419 }
16420
16421 /* Reencryption required */
16422 HRESULT rc = S_OK;
16423 int vrc = VINF_SUCCESS;
16424
16425 std::list<com::Utf8Str> lstFiles;
16426 if (SUCCEEDED(rc))
16427 {
16428 vrc = i_findFiles(lstFiles, strDirectory, strFilePattern);
16429 if (RT_FAILURE(vrc))
16430 rc = setErrorBoth(E_FAIL, vrc, tr("Getting file list for '%s' files failed, (%Rrc)"),
16431 strFilePattern.c_str(), vrc);
16432 }
16433 com::Utf8Str strNewKeyStore;
16434 if (SUCCEEDED(rc))
16435 {
16436 if (!fDecrypt)
16437 {
16438 VBOXCRYPTOCTX hCryptoCtx;
16439 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(pszTaskCipher, task.mstrNewPassword.c_str(), &hCryptoCtx);
16440 if (RT_FAILURE(vrc))
16441 return setErrorBoth(E_FAIL, vrc, tr("Create new key store for '%s' files failed, (%Rrc)"),
16442 strFilePattern.c_str(), vrc);
16443
16444 char *pszKeyStore = NULL;
16445 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16446 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16447 if (RT_FAILURE(vrc))
16448 return setErrorBoth(E_FAIL, vrc, tr("Saving the new key store for '%s' files failed, (%Rrc)"),
16449 strFilePattern.c_str(), vrc);
16450 strNewKeyStore = pszKeyStore;
16451 RTMemFree(pszKeyStore);
16452 }
16453
16454 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
16455 it != lstFiles.end();
16456 ++it)
16457 {
16458 RTVFSIOSTREAM hVfsIosOld = NIL_RTVFSIOSTREAM;
16459 RTVFSIOSTREAM hVfsIosNew = NIL_RTVFSIOSTREAM;
16460
16461 uint64_t fOpenForRead = RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE;
16462 uint64_t fOpenForWrite = RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE;
16463
16464 vrc = i_createIoStreamForFile((*it).c_str(),
16465 fEncrypt ? NULL : task.m_pCryptoIf,
16466 fEncrypt ? NULL : strKeyStore.c_str(),
16467 fEncrypt ? NULL : task.mstrCurrentPassword.c_str(),
16468 fOpenForRead, &hVfsIosOld);
16469 if (RT_SUCCESS(vrc))
16470 {
16471 vrc = i_createIoStreamForFile((*it + ".tmp").c_str(),
16472 fDecrypt ? NULL : task.m_pCryptoIf,
16473 fDecrypt ? NULL : strNewKeyStore.c_str(),
16474 fDecrypt ? NULL : task.mstrNewPassword.c_str(),
16475 fOpenForWrite, &hVfsIosNew);
16476 if (RT_FAILURE(vrc))
16477 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
16478 (*it + ".tmp").c_str(), vrc);
16479 }
16480 else
16481 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
16482 (*it).c_str(), vrc);
16483
16484 if (RT_SUCCESS(vrc))
16485 {
16486 vrc = RTVfsUtilPumpIoStreams(hVfsIosOld, hVfsIosNew, BUF_DATA_SIZE);
16487 if (RT_FAILURE(vrc))
16488 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Changing encryption of the file '%s' failed with %Rrc"),
16489 (*it).c_str(), vrc);
16490 }
16491
16492 if (hVfsIosOld != NIL_RTVFSIOSTREAM)
16493 RTVfsIoStrmRelease(hVfsIosOld);
16494 if (hVfsIosNew != NIL_RTVFSIOSTREAM)
16495 RTVfsIoStrmRelease(hVfsIosNew);
16496 }
16497 }
16498
16499 if (SUCCEEDED(rc))
16500 {
16501 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
16502 it != lstFiles.end();
16503 ++it)
16504 {
16505 vrc = RTFileRename((*it + ".tmp").c_str(), (*it).c_str(), RTPATHRENAME_FLAGS_REPLACE);
16506 if (RT_FAILURE(vrc))
16507 {
16508 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming the file '%s' failed, (%Rrc)"),
16509 (*it + ".tmp").c_str(), vrc);
16510 break;
16511 }
16512 }
16513 }
16514
16515 if (SUCCEEDED(rc))
16516 {
16517 strKeyStore = strNewKeyStore;
16518 strKeyId = task.mstrNewPasswordId;
16519 }
16520
16521 return rc;
16522}
16523
16524/**
16525 * Task thread implementation for Machine::changeEncryption(), called from
16526 * Machine::taskHandler().
16527 *
16528 * @note Locks this object for writing.
16529 *
16530 * @param task
16531 * @return
16532 */
16533void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task)
16534{
16535 LogFlowThisFuncEnter();
16536
16537 AutoCaller autoCaller(this);
16538 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
16539 if (FAILED(autoCaller.rc()))
16540 {
16541 /* we might have been uninitialized because the session was accidentally
16542 * closed by the client, so don't assert */
16543 HRESULT rc = setError(E_FAIL,
16544 tr("The session has been accidentally closed"));
16545 task.m_pProgress->i_notifyComplete(rc);
16546 LogFlowThisFuncLeave();
16547 return;
16548 }
16549
16550 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16551
16552 HRESULT rc = S_OK;
16553 com::Utf8Str strOldKeyId = mData->mstrKeyId;
16554 com::Utf8Str strOldKeyStore = mData->mstrKeyStore;
16555 try
16556 {
16557 rc = this->i_getVirtualBox()->i_retainCryptoIf(&task.m_pCryptoIf);
16558 if (FAILED(rc))
16559 throw rc;
16560
16561 if (task.mstrCurrentPassword.isEmpty())
16562 {
16563 if (mData->mstrKeyStore.isNotEmpty())
16564 throw setError(VBOX_E_PASSWORD_INCORRECT,
16565 tr("The password given for the encrypted VM is incorrect"));
16566 }
16567 else
16568 {
16569 if (mData->mstrKeyStore.isEmpty())
16570 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16571 tr("The VM is not configured for encryption"));
16572 rc = checkEncryptionPassword(task.mstrCurrentPassword);
16573 if (rc == VBOX_E_PASSWORD_INCORRECT)
16574 throw setError(VBOX_E_PASSWORD_INCORRECT,
16575 tr("The password to decrypt the VM is incorrect"));
16576 }
16577
16578 if (task.mstrCipher.isNotEmpty())
16579 {
16580 if ( task.mstrNewPassword.isEmpty()
16581 && task.mstrNewPasswordId.isEmpty()
16582 && task.mstrCurrentPassword.isNotEmpty())
16583 {
16584 /* An empty password and password ID will default to the current password. */
16585 task.mstrNewPassword = task.mstrCurrentPassword;
16586 }
16587 else if (task.mstrNewPassword.isEmpty())
16588 throw setError(VBOX_E_OBJECT_NOT_FOUND,
16589 tr("A password must be given for the VM encryption"));
16590 else if (task.mstrNewPasswordId.isEmpty())
16591 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16592 tr("A valid identifier for the password must be given"));
16593 }
16594 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
16595 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16596 tr("The password and password identifier must be empty if the output should be unencrypted"));
16597
16598 /*
16599 * Save config.
16600 * Must be first operation to prevent making encrypted copies
16601 * for old version of the config file.
16602 */
16603 int fSave = Machine::SaveS_Force;
16604 if (task.mstrNewPassword.isNotEmpty())
16605 {
16606 VBOXCRYPTOCTX hCryptoCtx;
16607
16608 int vrc = VINF_SUCCESS;
16609 if (task.mForce || task.mstrCurrentPassword.isEmpty() || task.mstrCipher.isNotEmpty())
16610 {
16611 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(getCipherString(task.mstrCipher.c_str(), CipherModeGcm),
16612 task.mstrNewPassword.c_str(), &hCryptoCtx);
16613 if (RT_FAILURE(vrc))
16614 throw setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
16615 }
16616 else
16617 {
16618 vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(mData->mstrKeyStore.c_str(),
16619 task.mstrCurrentPassword.c_str(),
16620 &hCryptoCtx);
16621 if (RT_FAILURE(vrc))
16622 throw setErrorBoth(E_FAIL, vrc, tr("Loading old key store failed, (%Rrc)"), vrc);
16623 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
16624 if (RT_FAILURE(vrc))
16625 throw setErrorBoth(E_FAIL, vrc, tr("Changing the password failed, (%Rrc)"), vrc);
16626 }
16627
16628 char *pszKeyStore;
16629 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16630 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16631 if (RT_FAILURE(vrc))
16632 throw setErrorBoth(E_FAIL, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
16633 mData->mstrKeyStore = pszKeyStore;
16634 RTStrFree(pszKeyStore);
16635 mData->mstrKeyId = task.mstrNewPasswordId;
16636 size_t cbPassword = task.mstrNewPassword.length() + 1;
16637 uint8_t *pbPassword = (uint8_t *)task.mstrNewPassword.c_str();
16638 mData->mpKeyStore->deleteSecretKey(task.mstrNewPasswordId);
16639 mData->mpKeyStore->addSecretKey(task.mstrNewPasswordId, pbPassword, cbPassword);
16640 mNvramStore->i_addPassword(task.mstrNewPasswordId, task.mstrNewPassword);
16641
16642 /*
16643 * Remove backuped config after saving because it can contain
16644 * unencrypted version of the config
16645 */
16646 fSave |= Machine::SaveS_RemoveBackup;
16647 }
16648 else
16649 {
16650 mData->mstrKeyId.setNull();
16651 mData->mstrKeyStore.setNull();
16652 }
16653
16654 Bstr bstrCurrentPassword(task.mstrCurrentPassword);
16655 Bstr bstrCipher(getCipherString(task.mstrCipher.c_str(), CipherModeXts));
16656 Bstr bstrNewPassword(task.mstrNewPassword);
16657 Bstr bstrNewPasswordId(task.mstrNewPasswordId);
16658 /* encrypt mediums */
16659 alock.release();
16660 for (MediaList::iterator it = task.mllMedia.begin();
16661 it != task.mllMedia.end();
16662 ++it)
16663 {
16664 ComPtr<IProgress> pProgress1;
16665 HRESULT hrc = (*it)->ChangeEncryption(bstrCurrentPassword.raw(), bstrCipher.raw(),
16666 bstrNewPassword.raw(), bstrNewPasswordId.raw(),
16667 pProgress1.asOutParam());
16668 if (FAILED(hrc)) throw hrc;
16669 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress1, 0 /* indefinite wait */);
16670 if (FAILED(hrc)) throw hrc;
16671 }
16672 alock.acquire();
16673
16674 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the SAV files")).raw(), 1);
16675
16676 Utf8Str strFullSnapshotFolder;
16677 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
16678
16679 /* .sav files (main and snapshots) */
16680 rc = i_changeEncryptionForComponent(task, strFullSnapshotFolder, "*.sav",
16681 mSSData->strStateKeyStore, mSSData->strStateKeyId, CipherModeGcm);
16682 if (FAILED(rc))
16683 /* the helper function already sets error object */
16684 throw rc;
16685
16686 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the NVRAM files")).raw(), 1);
16687
16688 /* .nvram files */
16689 com::Utf8Str strNVRAMKeyId;
16690 com::Utf8Str strNVRAMKeyStore;
16691 rc = mNvramStore->i_getEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16692 if (FAILED(rc))
16693 throw setError(rc, tr("Getting NVRAM encryption settings failed (%Rhrc)"), rc);
16694
16695 Utf8Str strMachineFolder;
16696 i_calculateFullPath(".", strMachineFolder);
16697
16698 rc = i_changeEncryptionForComponent(task, strMachineFolder, "*.nvram",
16699 strNVRAMKeyStore, strNVRAMKeyId, CipherModeGcm);
16700 if (FAILED(rc))
16701 /* the helper function already sets error object */
16702 throw rc;
16703
16704 rc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16705 if (FAILED(rc))
16706 throw setError(rc, tr("Setting NVRAM encryption settings failed (%Rhrc)"), rc);
16707
16708 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of log files")).raw(), 1);
16709
16710 /* .log files */
16711 com::Utf8Str strLogFolder;
16712 i_getLogFolder(strLogFolder);
16713 rc = i_changeEncryptionForComponent(task, strLogFolder, "VBox.log*",
16714 mData->mstrLogKeyStore, mData->mstrLogKeyId, CipherModeCtr);
16715 if (FAILED(rc))
16716 /* the helper function already sets error object */
16717 throw rc;
16718
16719 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the config file")).raw(), 1);
16720
16721 i_saveSettings(NULL, alock, fSave);
16722 }
16723 catch (HRESULT aRC)
16724 {
16725 rc = aRC;
16726 mData->mstrKeyId = strOldKeyId;
16727 mData->mstrKeyStore = strOldKeyStore;
16728 }
16729
16730 task.m_pProgress->i_notifyComplete(rc);
16731
16732 LogFlowThisFuncLeave();
16733}
16734#endif /*!VBOX_WITH_FULL_VM_ENCRYPTION*/
16735
16736HRESULT Machine::changeEncryption(const com::Utf8Str &aCurrentPassword,
16737 const com::Utf8Str &aCipher,
16738 const com::Utf8Str &aNewPassword,
16739 const com::Utf8Str &aNewPasswordId,
16740 BOOL aForce,
16741 ComPtr<IProgress> &aProgress)
16742{
16743 LogFlowFuncEnter();
16744
16745#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16746 RT_NOREF(aCurrentPassword, aCipher, aNewPassword, aNewPasswordId, aForce, aProgress);
16747 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16748#else
16749 /* make the VM accessible */
16750 if (!mData->mAccessible)
16751 {
16752 if ( aCurrentPassword.isEmpty()
16753 || mData->mstrKeyId.isEmpty())
16754 return setError(E_ACCESSDENIED, tr("Machine is inaccessible"));
16755
16756 HRESULT rc = addEncryptionPassword(mData->mstrKeyId, aCurrentPassword);
16757 if (FAILED(rc))
16758 return rc;
16759 }
16760
16761 AutoLimitedCaller autoCaller(this);
16762 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16763
16764 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16765
16766 /* define mediums to be change encryption */
16767
16768 MediaList llMedia;
16769 for (MediumAttachmentList::iterator
16770 it = mMediumAttachments->begin();
16771 it != mMediumAttachments->end();
16772 ++it)
16773 {
16774 ComObjPtr<MediumAttachment> &pAttach = *it;
16775 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
16776
16777 if (!pMedium.isNull())
16778 {
16779 AutoCaller mac(pMedium);
16780 if (FAILED(mac.rc())) return mac.rc();
16781 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
16782 DeviceType_T devType = pMedium->i_getDeviceType();
16783 if (devType == DeviceType_HardDisk)
16784 {
16785 /*
16786 * We need to move to last child because the Medium::changeEncryption
16787 * encrypts all chain of specified medium with its parents.
16788 * Also we perform cheking of back reference and children for
16789 * all media in the chain to raise error before we start any action.
16790 * So, we first move into root parent and then we will move to last child
16791 * keeping latter in the list for encryption.
16792 */
16793
16794 /* move to root parent */
16795 ComObjPtr<Medium> pTmpMedium = pMedium;
16796 while (pTmpMedium.isNotNull())
16797 {
16798 AutoCaller mediumAC(pTmpMedium);
16799 if (FAILED(mediumAC.rc())) return mac.rc();
16800 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16801
16802 /* Cannot encrypt media which are attached to more than one virtual machine. */
16803 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16804 if (cBackRefs > 1)
16805 return setError(VBOX_E_INVALID_OBJECT_STATE,
16806 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16807 pTmpMedium->i_getName().c_str(), cBackRefs);
16808
16809 size_t cChildren = pTmpMedium->i_getChildren().size();
16810 if (cChildren > 1)
16811 return setError(VBOX_E_INVALID_OBJECT_STATE,
16812 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16813 pTmpMedium->i_getName().c_str(), cChildren);
16814
16815 pTmpMedium = pTmpMedium->i_getParent();
16816 }
16817 /* move to last child */
16818 pTmpMedium = pMedium;
16819 while (pTmpMedium.isNotNull() && pTmpMedium->i_getChildren().size() != 0)
16820 {
16821 AutoCaller mediumAC(pTmpMedium);
16822 if (FAILED(mediumAC.rc())) return mac.rc();
16823 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16824
16825 /* Cannot encrypt media which are attached to more than one virtual machine. */
16826 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16827 if (cBackRefs > 1)
16828 return setError(VBOX_E_INVALID_OBJECT_STATE,
16829 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16830 pTmpMedium->i_getName().c_str(), cBackRefs);
16831
16832 size_t cChildren = pTmpMedium->i_getChildren().size();
16833 if (cChildren > 1)
16834 return setError(VBOX_E_INVALID_OBJECT_STATE,
16835 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16836 pTmpMedium->i_getName().c_str(), cChildren);
16837
16838 pTmpMedium = pTmpMedium->i_getChildren().front();
16839 }
16840 llMedia.push_back(pTmpMedium);
16841 }
16842 }
16843 }
16844
16845 ComObjPtr<Progress> pProgress;
16846 pProgress.createObject();
16847 HRESULT rc = pProgress->init(i_getVirtualBox(),
16848 static_cast<IMachine*>(this) /* aInitiator */,
16849 tr("Change encryption"),
16850 TRUE /* fCancellable */,
16851 (ULONG)(4 + + llMedia.size()), // cOperations
16852 tr("Change encryption of the mediuma"));
16853 if (FAILED(rc))
16854 return rc;
16855
16856 /* create and start the task on a separate thread (note that it will not
16857 * start working until we release alock) */
16858 ChangeEncryptionTask *pTask = new ChangeEncryptionTask(this, pProgress, "VM encryption",
16859 aCurrentPassword, aCipher, aNewPassword,
16860 aNewPasswordId, aForce, llMedia);
16861 rc = pTask->createThread();
16862 pTask = NULL;
16863 if (FAILED(rc))
16864 return rc;
16865
16866 pProgress.queryInterfaceTo(aProgress.asOutParam());
16867
16868 LogFlowFuncLeave();
16869
16870 return S_OK;
16871#endif
16872}
16873
16874HRESULT Machine::getEncryptionSettings(com::Utf8Str &aCipher,
16875 com::Utf8Str &aPasswordId)
16876{
16877#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16878 RT_NOREF(aCipher, aPasswordId);
16879 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16880#else
16881 AutoLimitedCaller autoCaller(this);
16882 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16883
16884 PCVBOXCRYPTOIF pCryptoIf = NULL;
16885 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16886 if (FAILED(hrc)) return hrc; /* Error is set */
16887
16888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16889
16890 if (mData->mstrKeyStore.isNotEmpty())
16891 {
16892 char *pszCipher = NULL;
16893 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), NULL /*pszPassword*/,
16894 NULL /*ppbKey*/, NULL /*pcbKey*/, &pszCipher);
16895 if (RT_SUCCESS(vrc))
16896 {
16897 aCipher = getCipherStringWithoutMode(pszCipher);
16898 RTStrFree(pszCipher);
16899 aPasswordId = mData->mstrKeyId;
16900 }
16901 else
16902 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
16903 tr("Failed to query the encryption settings with %Rrc"),
16904 vrc);
16905 }
16906 else
16907 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16908
16909 mParent->i_releaseCryptoIf(pCryptoIf);
16910
16911 return hrc;
16912#endif
16913}
16914
16915HRESULT Machine::checkEncryptionPassword(const com::Utf8Str &aPassword)
16916{
16917#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16918 RT_NOREF(aPassword);
16919 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16920#else
16921 AutoLimitedCaller autoCaller(this);
16922 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16923
16924 PCVBOXCRYPTOIF pCryptoIf = NULL;
16925 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16926 if (FAILED(hrc)) return hrc; /* Error is set */
16927
16928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16929
16930 if (mData->mstrKeyStore.isNotEmpty())
16931 {
16932 char *pszCipher = NULL;
16933 uint8_t *pbDek = NULL;
16934 size_t cbDek = 0;
16935 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), aPassword.c_str(),
16936 &pbDek, &cbDek, &pszCipher);
16937 if (RT_SUCCESS(vrc))
16938 {
16939 RTStrFree(pszCipher);
16940 RTMemSaferFree(pbDek, cbDek);
16941 }
16942 else
16943 hrc = setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc,
16944 tr("The password supplied for the encrypted machine is incorrect"));
16945 }
16946 else
16947 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16948
16949 mParent->i_releaseCryptoIf(pCryptoIf);
16950
16951 return hrc;
16952#endif
16953}
16954
16955HRESULT Machine::addEncryptionPassword(const com::Utf8Str &aId,
16956 const com::Utf8Str &aPassword)
16957{
16958#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16959 RT_NOREF(aId, aPassword);
16960 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16961#else
16962 AutoLimitedCaller autoCaller(this);
16963 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16964
16965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16966
16967 size_t cbPassword = aPassword.length() + 1;
16968 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
16969
16970 mData->mpKeyStore->addSecretKey(aId, pbPassword, cbPassword);
16971
16972 if ( mData->mAccessible
16973 && mData->mSession.mState == SessionState_Locked
16974 && mData->mSession.mLockType == LockType_VM
16975 && mData->mSession.mDirectControl != NULL)
16976 {
16977 /* get the console from the direct session */
16978 ComPtr<IConsole> console;
16979 HRESULT rc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16980 ComAssertComRC(rc);
16981 /* send passsword to console */
16982 console->AddEncryptionPassword(Bstr(aId).raw(),
16983 Bstr(aPassword).raw(),
16984 TRUE);
16985 }
16986
16987 if (mData->mstrKeyId == aId)
16988 {
16989 HRESULT hrc = checkEncryptionPassword(aPassword);
16990 if (FAILED(hrc))
16991 return hrc;
16992
16993 if (SUCCEEDED(hrc))
16994 {
16995 /*
16996 * Encryption is used and password is correct,
16997 * Reinit the machine if required.
16998 */
16999 BOOL fAccessible;
17000 alock.release();
17001 getAccessible(&fAccessible);
17002 alock.acquire();
17003 }
17004 }
17005
17006 /*
17007 * Add the password into the NvramStore only after
17008 * the machine becomes accessible and the NvramStore
17009 * contains key id and key store.
17010 */
17011 if (mNvramStore.isNotNull())
17012 mNvramStore->i_addPassword(aId, aPassword);
17013
17014 return S_OK;
17015#endif
17016}
17017
17018HRESULT Machine::addEncryptionPasswords(const std::vector<com::Utf8Str> &aIds,
17019 const std::vector<com::Utf8Str> &aPasswords)
17020{
17021#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
17022 RT_NOREF(aIds, aPasswords);
17023 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
17024#else
17025 if (aIds.size() != aPasswords.size())
17026 return setError(E_INVALIDARG, tr("Id and passwords arrays must have the same size"));
17027
17028 HRESULT hrc = S_OK;
17029 for (size_t i = 0; i < aIds.size() && SUCCEEDED(hrc); ++i)
17030 hrc = addEncryptionPassword(aIds[i], aPasswords[i]);
17031
17032 return hrc;
17033#endif
17034}
17035
17036HRESULT Machine::removeEncryptionPassword(AutoCaller &autoCaller, const com::Utf8Str &aId)
17037{
17038#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
17039 RT_NOREF(autoCaller, aId);
17040 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
17041#else
17042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
17043
17044 if ( mData->mAccessible
17045 && mData->mSession.mState == SessionState_Locked
17046 && mData->mSession.mLockType == LockType_VM
17047 && mData->mSession.mDirectControl != NULL)
17048 {
17049 /* get the console from the direct session */
17050 ComPtr<IConsole> console;
17051 HRESULT rc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
17052 ComAssertComRC(rc);
17053 /* send passsword to console */
17054 console->RemoveEncryptionPassword(Bstr(aId).raw());
17055 }
17056
17057 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty() && mData->mstrKeyId == aId)
17058 {
17059 if (Global::IsOnlineOrTransient(mData->mMachineState))
17060 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
17061 alock.release();
17062 autoCaller.release();
17063 /* return because all passwords are purged when machine becomes inaccessible; */
17064 return i_setInaccessible();
17065 }
17066
17067 if (mNvramStore.isNotNull())
17068 mNvramStore->i_removePassword(aId);
17069 mData->mpKeyStore->deleteSecretKey(aId);
17070 return S_OK;
17071#endif
17072}
17073
17074HRESULT Machine::clearAllEncryptionPasswords(AutoCaller &autoCaller)
17075{
17076#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
17077 RT_NOREF(autoCaller);
17078 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
17079#else
17080 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
17081
17082 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty())
17083 {
17084 if (Global::IsOnlineOrTransient(mData->mMachineState))
17085 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
17086 alock.release();
17087 autoCaller.release();
17088 /* return because all passwords are purged when machine becomes inaccessible; */
17089 return i_setInaccessible();
17090 }
17091
17092 mNvramStore->i_removeAllPasswords();
17093 mData->mpKeyStore->deleteAllSecretKeys(false, true);
17094 return S_OK;
17095#endif
17096}
17097
17098#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
17099HRESULT Machine::i_setInaccessible()
17100{
17101 if (!mData->mAccessible)
17102 return S_OK;
17103
17104 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
17105 VirtualBox *pParent = mParent;
17106 com::Utf8Str strConfigFile = mData->m_strConfigFile;
17107 Guid id(i_getId());
17108
17109 alock.release();
17110
17111 uninit();
17112 HRESULT rc = initFromSettings(pParent, strConfigFile, &id, com::Utf8Str());
17113
17114 alock.acquire();
17115 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
17116 return rc;
17117}
17118#endif
17119
17120/* This isn't handled entirely by the wrapper generator yet. */
17121#ifdef VBOX_WITH_XPCOM
17122NS_DECL_CLASSINFO(SessionMachine)
17123NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
17124
17125NS_DECL_CLASSINFO(SnapshotMachine)
17126NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
17127#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