VirtualBox

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

Last change on this file since 99599 was 98837, checked in by vboxsync, 20 months ago

Main/Machine: Machine::changeEncryption() can return success if
AutoCaller.cpp:ObjectState::addCaller() fails due to a copy-and-paste
error.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 601.6 KB
Line 
1/* $Id: MachineImpl.cpp 98837 2023-03-03 22:23:31Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
29
30/* Make sure all the stdint.h macros are included - must come first! */
31#ifndef __STDC_LIMIT_MACROS
32# define __STDC_LIMIT_MACROS
33#endif
34#ifndef __STDC_CONSTANT_MACROS
35# define __STDC_CONSTANT_MACROS
36#endif
37
38#include "LoggingNew.h"
39#include "VirtualBoxImpl.h"
40#include "MachineImpl.h"
41#include "SnapshotImpl.h"
42#include "ClientToken.h"
43#include "ProgressImpl.h"
44#include "ProgressProxyImpl.h"
45#include "MediumAttachmentImpl.h"
46#include "MediumImpl.h"
47#include "MediumLock.h"
48#include "USBControllerImpl.h"
49#include "USBDeviceFiltersImpl.h"
50#include "HostImpl.h"
51#include "SharedFolderImpl.h"
52#include "GuestOSTypeImpl.h"
53#include "VirtualBoxErrorInfoImpl.h"
54#include "StorageControllerImpl.h"
55#include "DisplayImpl.h"
56#include "DisplayUtils.h"
57#include "MachineImplCloneVM.h"
58#include "AutostartDb.h"
59#include "SystemPropertiesImpl.h"
60#include "MachineImplMoveVM.h"
61#include "ExtPackManagerImpl.h"
62#include "MachineLaunchVMCommonWorker.h"
63#include "CryptoUtils.h"
64
65// generated header
66#include "VBoxEvents.h"
67
68#ifdef VBOX_WITH_USB
69# include "USBProxyService.h"
70#endif
71
72#include "AutoCaller.h"
73#include "HashedPw.h"
74#include "Performance.h"
75#include "StringifyEnums.h"
76
77#include <iprt/asm.h>
78#include <iprt/path.h>
79#include <iprt/dir.h>
80#include <iprt/env.h>
81#include <iprt/lockvalidator.h>
82#include <iprt/memsafer.h>
83#include <iprt/process.h>
84#include <iprt/cpp/utils.h>
85#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
86#include <iprt/sha.h>
87#include <iprt/string.h>
88#include <iprt/ctype.h>
89
90#include <VBox/com/array.h>
91#include <VBox/com/list.h>
92#include <VBox/VBoxCryptoIf.h>
93
94#include <VBox/err.h>
95#include <VBox/param.h>
96#include <VBox/settings.h>
97#include <VBox/VMMDev.h>
98#include <VBox/vmm/ssm.h>
99
100#ifdef VBOX_WITH_GUEST_PROPS
101# include <VBox/HostServices/GuestPropertySvc.h>
102# include <VBox/com/array.h>
103#endif
104
105#ifdef VBOX_WITH_SHARED_CLIPBOARD
106# include <VBox/HostServices/VBoxClipboardSvc.h>
107#endif
108
109#include "VBox/com/MultiResult.h"
110
111#include <algorithm>
112
113#ifdef VBOX_WITH_DTRACE_R3_MAIN
114# include "dtrace/VBoxAPI.h"
115#endif
116
117#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
118# define HOSTSUFF_EXE ".exe"
119#else /* !RT_OS_WINDOWS */
120# define HOSTSUFF_EXE ""
121#endif /* !RT_OS_WINDOWS */
122
123// defines / prototypes
124/////////////////////////////////////////////////////////////////////////////
125
126#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
127# define BUF_DATA_SIZE _64K
128
129enum CipherMode
130{
131 CipherModeGcm = 0,
132 CipherModeCtr,
133 CipherModeXts,
134 CipherModeMax
135};
136
137enum AesSize
138{
139 Aes128 = 0,
140 Aes256,
141 AesMax
142};
143
144const char *g_apszCipher[AesMax][CipherModeMax] =
145{
146 {"AES-GCM128", "AES-CTR128", "AES-XTS128-PLAIN64"},
147 {"AES-GCM256", "AES-CTR256", "AES-XTS256-PLAIN64"}
148};
149const char *g_apszCipherAlgo[AesMax] = {"AES-128", "AES-256"};
150
151static const char *getCipherString(const char *pszAlgo, const int iMode)
152{
153 if (iMode >= CipherModeMax)
154 return pszAlgo;
155
156 for (int i = 0; i < AesMax; i++)
157 {
158 if (strcmp(pszAlgo, g_apszCipherAlgo[i]) == 0)
159 return g_apszCipher[i][iMode];
160 }
161 return pszAlgo;
162}
163
164static const char *getCipherStringWithoutMode(const char *pszAlgo)
165{
166 for (int i = 0; i < AesMax; i++)
167 {
168 for (int j = 0; j < CipherModeMax; j++)
169 {
170 if (strcmp(pszAlgo, g_apszCipher[i][j]) == 0)
171 return g_apszCipherAlgo[i];
172 }
173 }
174 return pszAlgo;
175}
176#endif
177
178/////////////////////////////////////////////////////////////////////////////
179// Machine::Data structure
180/////////////////////////////////////////////////////////////////////////////
181
182Machine::Data::Data()
183{
184 mRegistered = FALSE;
185 pMachineConfigFile = NULL;
186 /* Contains hints on what has changed when the user is using the VM (config
187 * changes, running the VM, ...). This is used to decide if a config needs
188 * to be written to disk. */
189 flModifications = 0;
190 /* VM modification usually also trigger setting the current state to
191 * "Modified". Although this is not always the case. An e.g. is the VM
192 * initialization phase or when snapshot related data is changed. The
193 * actually behavior is controlled by the following flag. */
194 m_fAllowStateModification = false;
195 mAccessible = FALSE;
196 /* mUuid is initialized in Machine::init() */
197
198 mMachineState = MachineState_PoweredOff;
199 RTTimeNow(&mLastStateChange);
200
201 mMachineStateDeps = 0;
202 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
203 mMachineStateChangePending = 0;
204
205 mCurrentStateModified = TRUE;
206 mGuestPropertiesModified = FALSE;
207
208 mSession.mPID = NIL_RTPROCESS;
209 mSession.mLockType = LockType_Null;
210 mSession.mState = SessionState_Unlocked;
211
212#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
213 mpKeyStore = NULL;
214#endif
215}
216
217Machine::Data::~Data()
218{
219 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
220 {
221 RTSemEventMultiDestroy(mMachineStateDepsSem);
222 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
223 }
224 if (pMachineConfigFile)
225 {
226 delete pMachineConfigFile;
227 pMachineConfigFile = NULL;
228 }
229}
230
231/////////////////////////////////////////////////////////////////////////////
232// Machine::HWData structure
233/////////////////////////////////////////////////////////////////////////////
234
235Machine::HWData::HWData()
236{
237 /* default values for a newly created machine */
238 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
239 mMemorySize = 128;
240 mCPUCount = 1;
241 mCPUHotPlugEnabled = false;
242 mMemoryBalloonSize = 0;
243 mPageFusionEnabled = false;
244 mHWVirtExEnabled = true;
245 mHWVirtExNestedPagingEnabled = true;
246 mHWVirtExLargePagesEnabled = HC_ARCH_BITS == 64; /* Not supported on 32 bits hosts. */
247 mHWVirtExVPIDEnabled = true;
248 mHWVirtExUXEnabled = true;
249 mHWVirtExForceEnabled = false;
250 mHWVirtExUseNativeApi = false;
251 mHWVirtExVirtVmsaveVmload = true;
252#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
253 mPAEEnabled = true;
254#else
255 mPAEEnabled = false;
256#endif
257 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
258 mTripleFaultReset = false;
259 mAPIC = true;
260 mX2APIC = false;
261 mIBPBOnVMExit = false;
262 mIBPBOnVMEntry = false;
263 mSpecCtrl = false;
264 mSpecCtrlByHost = false;
265 mL1DFlushOnSched = true;
266 mL1DFlushOnVMEntry = false;
267 mMDSClearOnSched = true;
268 mMDSClearOnVMEntry = false;
269 mNestedHWVirt = false;
270 mHPETEnabled = false;
271 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
272 mCpuIdPortabilityLevel = 0;
273 mCpuProfile = "host";
274
275 /* default boot order: floppy - DVD - HDD */
276 mBootOrder[0] = DeviceType_Floppy;
277 mBootOrder[1] = DeviceType_DVD;
278 mBootOrder[2] = DeviceType_HardDisk;
279 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
280 mBootOrder[i] = DeviceType_Null;
281
282 mClipboardMode = ClipboardMode_Disabled;
283 mClipboardFileTransfersEnabled = FALSE;
284
285 mDnDMode = DnDMode_Disabled;
286
287 mFirmwareType = FirmwareType_BIOS;
288 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
289 mPointingHIDType = PointingHIDType_PS2Mouse;
290 mChipsetType = ChipsetType_PIIX3;
291 mIommuType = IommuType_None;
292 mParavirtProvider = ParavirtProvider_Default;
293 mEmulatedUSBCardReaderEnabled = FALSE;
294
295 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
296 mCPUAttached[i] = false;
297
298 mIOCacheEnabled = true;
299 mIOCacheSize = 5; /* 5MB */
300}
301
302Machine::HWData::~HWData()
303{
304}
305
306/////////////////////////////////////////////////////////////////////////////
307// Machine class
308/////////////////////////////////////////////////////////////////////////////
309
310// constructor / destructor
311/////////////////////////////////////////////////////////////////////////////
312
313Machine::Machine() :
314#ifdef VBOX_WITH_RESOURCE_USAGE_API
315 mCollectorGuest(NULL),
316#endif
317 mPeer(NULL),
318 mParent(NULL),
319 mSerialPorts(),
320 mParallelPorts(),
321 uRegistryNeedsSaving(0)
322{}
323
324Machine::~Machine()
325{}
326
327HRESULT Machine::FinalConstruct()
328{
329 LogFlowThisFunc(("\n"));
330 return BaseFinalConstruct();
331}
332
333void Machine::FinalRelease()
334{
335 LogFlowThisFunc(("\n"));
336 uninit();
337 BaseFinalRelease();
338}
339
340/**
341 * Initializes a new machine instance; this init() variant creates a new, empty machine.
342 * This gets called from VirtualBox::CreateMachine().
343 *
344 * @param aParent Associated parent object
345 * @param strConfigFile Local file system path to the VM settings file (can
346 * be relative to the VirtualBox config directory).
347 * @param strName name for the machine
348 * @param llGroups list of groups for the machine
349 * @param strOsType OS Type string (stored as is if aOsType is NULL).
350 * @param aOsType OS Type of this machine or NULL.
351 * @param aId UUID for the new machine.
352 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
353 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
354 * scheme (includes the UUID).
355 * @param aCipher The cipher to encrypt the VM with.
356 * @param aPasswordId The password ID, empty if the VM should not be encrypted.
357 * @param aPassword The password to encrypt the VM with.
358 *
359 * @return Success indicator. if not S_OK, the machine object is invalid
360 */
361HRESULT Machine::init(VirtualBox *aParent,
362 const Utf8Str &strConfigFile,
363 const Utf8Str &strName,
364 const StringsList &llGroups,
365 const Utf8Str &strOsType,
366 GuestOSType *aOsType,
367 const Guid &aId,
368 bool fForceOverwrite,
369 bool fDirectoryIncludesUUID,
370 const com::Utf8Str &aCipher,
371 const com::Utf8Str &aPasswordId,
372 const com::Utf8Str &aPassword)
373{
374 LogFlowThisFuncEnter();
375 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
376
377#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
378 RT_NOREF(aCipher);
379 if (aPassword.isNotEmpty() || aPasswordId.isNotEmpty())
380 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
381#endif
382
383 /* Enclose the state transition NotReady->InInit->Ready */
384 AutoInitSpan autoInitSpan(this);
385 AssertReturn(autoInitSpan.isOk(), E_FAIL);
386
387 HRESULT hrc = initImpl(aParent, strConfigFile);
388 if (FAILED(hrc)) return hrc;
389
390#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
391 com::Utf8Str strSsmKeyId;
392 com::Utf8Str strSsmKeyStore;
393 com::Utf8Str strNVRAMKeyId;
394 com::Utf8Str strNVRAMKeyStore;
395
396 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
397 {
398 /* Resolve the cryptographic interface. */
399 PCVBOXCRYPTOIF pCryptoIf = NULL;
400 hrc = aParent->i_retainCryptoIf(&pCryptoIf);
401 if (SUCCEEDED(hrc))
402 {
403 CipherMode aenmMode[] = {CipherModeGcm, CipherModeGcm, CipherModeGcm, CipherModeCtr};
404 com::Utf8Str *astrKeyId[] = {&mData->mstrKeyId, &strSsmKeyId, &strNVRAMKeyId, &mData->mstrLogKeyId};
405 com::Utf8Str *astrKeyStore[] = {&mData->mstrKeyStore, &strSsmKeyStore, &strNVRAMKeyStore, &mData->mstrLogKeyStore};
406
407 for (uint32_t i = 0; i < RT_ELEMENTS(astrKeyId); i++)
408 {
409 const char *pszCipher = getCipherString(aCipher.c_str(), aenmMode[i]);
410 if (!pszCipher)
411 {
412 hrc = setError(VBOX_E_NOT_SUPPORTED,
413 tr("The cipher '%s' is not supported"), aCipher.c_str());
414 break;
415 }
416
417 VBOXCRYPTOCTX hCryptoCtx;
418 int vrc = pCryptoIf->pfnCryptoCtxCreate(pszCipher, aPassword.c_str(), &hCryptoCtx);
419 if (RT_FAILURE(vrc))
420 {
421 hrc = setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
422 break;
423 }
424
425 char *pszKeyStore;
426 vrc = pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
427 int vrc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
428 AssertRC(vrc2);
429
430 if (RT_FAILURE(vrc))
431 {
432 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
433 break;
434 }
435
436 *(astrKeyStore[i]) = pszKeyStore;
437 RTMemFree(pszKeyStore);
438 *(astrKeyId[i]) = aPasswordId;
439 }
440
441 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
442 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
443
444 if (FAILED(hrc))
445 return hrc; /* Error is set. */
446 }
447 else
448 return hrc; /* Error is set. */
449 }
450#endif
451
452 hrc = i_tryCreateMachineConfigFile(fForceOverwrite);
453 if (FAILED(hrc)) return hrc;
454
455 if (SUCCEEDED(hrc))
456 {
457 // create an empty machine config
458 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
459
460 hrc = initDataAndChildObjects();
461 }
462
463 if (SUCCEEDED(hrc))
464 {
465#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
466 mSSData->strStateKeyId = strSsmKeyId;
467 mSSData->strStateKeyStore = strSsmKeyStore;
468#endif
469
470 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
471 mData->mAccessible = TRUE;
472
473 unconst(mData->mUuid) = aId;
474
475 mUserData->s.strName = strName;
476
477 if (llGroups.size())
478 mUserData->s.llGroups = llGroups;
479
480 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
481 // the "name sync" flag determines whether the machine directory gets renamed along
482 // with the machine file; say so if the settings file name is the same as the
483 // settings file parent directory (machine directory)
484 mUserData->s.fNameSync = i_isInOwnDir();
485
486 // initialize the default snapshots folder
487 hrc = COMSETTER(SnapshotFolder)(NULL);
488 AssertComRC(hrc);
489
490 if (aOsType)
491 {
492 /* Store OS type */
493 mUserData->s.strOsType = aOsType->i_id();
494
495 /* Let the OS type select 64-bit ness. */
496 mHWData->mLongMode = aOsType->i_is64Bit()
497 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
498
499 /* Let the OS type enable the X2APIC */
500 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
501
502 hrc = aOsType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
503 AssertComRC(hrc);
504 }
505 else if (!strOsType.isEmpty())
506 {
507 /* Store OS type */
508 mUserData->s.strOsType = strOsType;
509
510 /* No guest OS type object. Pick some plausible defaults which the
511 * host can handle. There's no way to know or validate anything. */
512 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
513 mHWData->mX2APIC = false;
514 }
515
516 /* Apply BIOS defaults. */
517 mBIOSSettings->i_applyDefaults(aOsType);
518
519 /* Apply TPM defaults. */
520 mTrustedPlatformModule->i_applyDefaults(aOsType);
521
522 /* Apply recording defaults. */
523 mRecordingSettings->i_applyDefaults();
524
525 /* Apply network adapters defaults */
526 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
527 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
528
529 /* Apply serial port defaults */
530 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
531 mSerialPorts[slot]->i_applyDefaults(aOsType);
532
533 /* Apply parallel port defaults */
534 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
535 mParallelPorts[slot]->i_applyDefaults();
536
537 /* Enable the VMMDev testing feature for bootsector VMs: */
538 if (aOsType && aOsType->i_id() == "VBoxBS_64")
539 mData->pMachineConfigFile->mapExtraDataItems["VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled"] = "1";
540
541#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
542 hrc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
543#endif
544 if (SUCCEEDED(hrc))
545 {
546 /* At this point the changing of the current state modification
547 * flag is allowed. */
548 i_allowStateModification();
549
550 /* commit all changes made during the initialization */
551 i_commit();
552 }
553 }
554
555 /* Confirm a successful initialization when it's the case */
556 if (SUCCEEDED(hrc))
557 {
558#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
559 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
560 {
561 size_t cbPassword = aPassword.length() + 1;
562 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
563 mData->mpKeyStore->addSecretKey(aPasswordId, pbPassword, cbPassword);
564 }
565#endif
566
567 if (mData->mAccessible)
568 autoInitSpan.setSucceeded();
569 else
570 autoInitSpan.setLimited();
571 }
572
573 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, hrc=%08X\n",
574 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
575 mData->mRegistered,
576 mData->mAccessible,
577 hrc));
578
579 LogFlowThisFuncLeave();
580
581 return hrc;
582}
583
584/**
585 * Initializes a new instance with data from machine XML (formerly Init_Registered).
586 * Gets called in two modes:
587 *
588 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
589 * UUID is specified and we mark the machine as "registered";
590 *
591 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
592 * and the machine remains unregistered until RegisterMachine() is called.
593 *
594 * @param aParent Associated parent object
595 * @param strConfigFile Local file system path to the VM settings file (can
596 * be relative to the VirtualBox config directory).
597 * @param aId UUID of the machine or NULL (see above).
598 * @param strPassword Password for decrypting the config
599 *
600 * @return Success indicator. if not S_OK, the machine object is invalid
601 */
602HRESULT Machine::initFromSettings(VirtualBox *aParent,
603 const Utf8Str &strConfigFile,
604 const Guid *aId,
605 const com::Utf8Str &strPassword)
606{
607 LogFlowThisFuncEnter();
608 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
609
610 PCVBOXCRYPTOIF pCryptoIf = NULL;
611#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
612 if (strPassword.isNotEmpty())
613 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
614#else
615 if (strPassword.isNotEmpty())
616 {
617 /* Get at the crpytographic interface. */
618 HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
619 if (FAILED(hrc))
620 return hrc; /* Error is set. */
621 }
622#endif
623
624 /* Enclose the state transition NotReady->InInit->Ready */
625 AutoInitSpan autoInitSpan(this);
626 AssertReturn(autoInitSpan.isOk(), E_FAIL);
627
628 HRESULT hrc = initImpl(aParent, strConfigFile);
629 if (FAILED(hrc)) return hrc;
630
631 if (aId)
632 {
633 // loading a registered VM:
634 unconst(mData->mUuid) = *aId;
635 mData->mRegistered = TRUE;
636 // now load the settings from XML:
637 hrc = i_registeredInit();
638 // this calls initDataAndChildObjects() and loadSettings()
639 }
640 else
641 {
642 // opening an unregistered VM (VirtualBox::OpenMachine()):
643 hrc = initDataAndChildObjects();
644 if (SUCCEEDED(hrc))
645 {
646 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
647 mData->mAccessible = TRUE;
648
649 try
650 {
651 // load and parse machine XML; this will throw on XML or logic errors
652 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
653 pCryptoIf,
654 strPassword.c_str());
655
656 // reject VM UUID duplicates, they can happen if someone
657 // tries to register an already known VM config again
658 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
659 true /* fPermitInaccessible */,
660 false /* aDoSetError */,
661 NULL) != VBOX_E_OBJECT_NOT_FOUND)
662 {
663 throw setError(E_FAIL,
664 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
665 mData->m_strConfigFile.c_str());
666 }
667
668 // use UUID from machine config
669 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
670
671#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
672 // No exception is thrown if config is encrypted, allowing us to get the uuid and the encryption fields.
673 // We fill in the encryptions fields, and the rest will be filled in if all data parsed.
674 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
675 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
676#endif
677
678 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
679 {
680 // We just set the inaccessible state and fill the error info allowing the caller
681 // to register the machine with encrypted config even if the password is incorrect
682 mData->mAccessible = FALSE;
683
684 /* fetch the current error info */
685 mData->mAccessError = com::ErrorInfo();
686
687 setError(VBOX_E_PASSWORD_INCORRECT,
688 tr("Decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
689 mData->pMachineConfigFile->uuid.raw());
690 }
691 else
692 {
693#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
694 if (strPassword.isNotEmpty())
695 {
696 size_t cbKey = strPassword.length() + 1; /* Include terminator */
697 const uint8_t *pbKey = (const uint8_t *)strPassword.c_str();
698 mData->mpKeyStore->addSecretKey(mData->mstrKeyId, pbKey, cbKey);
699 }
700#endif
701
702 hrc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile, NULL /* puuidRegistry */);
703 if (FAILED(hrc)) throw hrc;
704
705 /* At this point the changing of the current state modification
706 * flag is allowed. */
707 i_allowStateModification();
708
709 i_commit();
710 }
711 }
712 catch (HRESULT err)
713 {
714 /* we assume that error info is set by the thrower */
715 hrc = err;
716 }
717 catch (...)
718 {
719 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
720 }
721 }
722 }
723
724 /* Confirm a successful initialization when it's the case */
725 if (SUCCEEDED(hrc))
726 {
727 if (mData->mAccessible)
728 autoInitSpan.setSucceeded();
729 else
730 {
731 autoInitSpan.setLimited();
732
733 // uninit media from this machine's media registry, or else
734 // reloading the settings will fail
735 mParent->i_unregisterMachineMedia(i_getId());
736 }
737 }
738
739#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
740 if (pCryptoIf)
741 {
742 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
743 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
744 }
745#endif
746
747 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool hrc=%08X\n",
748 !!mUserData ? mUserData->s.strName.c_str() : "NULL", mData->mRegistered, mData->mAccessible, hrc));
749
750 LogFlowThisFuncLeave();
751
752 return hrc;
753}
754
755/**
756 * Initializes a new instance from a machine config that is already in memory
757 * (import OVF case). Since we are importing, the UUID in the machine
758 * config is ignored and we always generate a fresh one.
759 *
760 * @param aParent Associated parent object.
761 * @param strName Name for the new machine; this overrides what is specified in config.
762 * @param strSettingsFilename File name of .vbox file.
763 * @param config Machine configuration loaded and parsed from XML.
764 *
765 * @return Success indicator. if not S_OK, the machine object is invalid
766 */
767HRESULT Machine::init(VirtualBox *aParent,
768 const Utf8Str &strName,
769 const Utf8Str &strSettingsFilename,
770 const settings::MachineConfigFile &config)
771{
772 LogFlowThisFuncEnter();
773
774 /* Enclose the state transition NotReady->InInit->Ready */
775 AutoInitSpan autoInitSpan(this);
776 AssertReturn(autoInitSpan.isOk(), E_FAIL);
777
778 HRESULT hrc = initImpl(aParent, strSettingsFilename);
779 if (FAILED(hrc)) return hrc;
780
781 hrc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
782 if (FAILED(hrc)) return hrc;
783
784 hrc = initDataAndChildObjects();
785 if (SUCCEEDED(hrc))
786 {
787 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
788 mData->mAccessible = TRUE;
789
790 // create empty machine config for instance data
791 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
792
793 // generate fresh UUID, ignore machine config
794 unconst(mData->mUuid).create();
795
796 hrc = i_loadMachineDataFromSettings(config, &mData->mUuid); // puuidRegistry: initialize media with this registry ID
797
798 // override VM name as well, it may be different
799 mUserData->s.strName = strName;
800
801 if (SUCCEEDED(hrc))
802 {
803 /* At this point the changing of the current state modification
804 * flag is allowed. */
805 i_allowStateModification();
806
807 /* commit all changes made during the initialization */
808 i_commit();
809 }
810 }
811
812 /* Confirm a successful initialization when it's the case */
813 if (SUCCEEDED(hrc))
814 {
815 if (mData->mAccessible)
816 autoInitSpan.setSucceeded();
817 else
818 {
819 /* Ignore all errors from unregistering, they would destroy
820- * the more interesting error information we already have,
821- * pinpointing the issue with the VM config. */
822 ErrorInfoKeeper eik;
823
824 autoInitSpan.setLimited();
825
826 // uninit media from this machine's media registry, or else
827 // reloading the settings will fail
828 mParent->i_unregisterMachineMedia(i_getId());
829 }
830 }
831
832 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool hrc=%08X\n",
833 !!mUserData ? mUserData->s.strName.c_str() : "NULL", mData->mRegistered, mData->mAccessible, hrc));
834
835 LogFlowThisFuncLeave();
836
837 return hrc;
838}
839
840/**
841 * Shared code between the various init() implementations.
842 * @param aParent The VirtualBox object.
843 * @param strConfigFile Settings file.
844 * @return
845 */
846HRESULT Machine::initImpl(VirtualBox *aParent,
847 const Utf8Str &strConfigFile)
848{
849 LogFlowThisFuncEnter();
850
851 AssertReturn(aParent, E_INVALIDARG);
852 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
853
854 HRESULT hrc = S_OK;
855
856 /* share the parent weakly */
857 unconst(mParent) = aParent;
858
859 /* allocate the essential machine data structure (the rest will be
860 * allocated later by initDataAndChildObjects() */
861 mData.allocate();
862
863 /* memorize the config file name (as provided) */
864 mData->m_strConfigFile = strConfigFile;
865
866 /* get the full file name */
867 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
868 if (RT_FAILURE(vrc1))
869 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
870 tr("Invalid machine settings file name '%s' (%Rrc)"),
871 strConfigFile.c_str(),
872 vrc1);
873
874#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
875 /** @todo Only create when the machine is going to be encrypted. */
876 /* Non-pageable memory is not accessible for non-VM process */
877 mData->mpKeyStore = new SecretKeyStore(false /* fKeyBufNonPageable */);
878 AssertReturn(mData->mpKeyStore, E_OUTOFMEMORY);
879#endif
880
881 LogFlowThisFuncLeave();
882
883 return hrc;
884}
885
886/**
887 * Tries to create a machine settings file in the path stored in the machine
888 * instance data. Used when a new machine is created to fail gracefully if
889 * the settings file could not be written (e.g. because machine dir is read-only).
890 * @return
891 */
892HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
893{
894 HRESULT hrc = S_OK;
895
896 // when we create a new machine, we must be able to create the settings file
897 RTFILE f = NIL_RTFILE;
898 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
899 if ( RT_SUCCESS(vrc)
900 || vrc == VERR_SHARING_VIOLATION
901 )
902 {
903 if (RT_SUCCESS(vrc))
904 RTFileClose(f);
905 if (!fForceOverwrite)
906 hrc = setError(VBOX_E_FILE_ERROR, tr("Machine settings file '%s' already exists"), mData->m_strConfigFileFull.c_str());
907 else
908 {
909 /* try to delete the config file, as otherwise the creation
910 * of a new settings file will fail. */
911 i_deleteFile(mData->m_strConfigFileFull.c_str(), false /* fIgnoreFailures */, tr("existing settings file"));
912 }
913 }
914 else if ( vrc != VERR_FILE_NOT_FOUND
915 && vrc != VERR_PATH_NOT_FOUND
916 )
917 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Invalid machine settings file name '%s' (%Rrc)"),
918 mData->m_strConfigFileFull.c_str(), vrc);
919 return hrc;
920}
921
922/**
923 * Initializes the registered machine by loading the settings file.
924 * This method is separated from #init() in order to make it possible to
925 * retry the operation after VirtualBox startup instead of refusing to
926 * startup the whole VirtualBox server in case if the settings file of some
927 * registered VM is invalid or inaccessible.
928 *
929 * @note Must be always called from this object's write lock
930 * (unless called from #init() that doesn't need any locking).
931 * @note Locks the mUSBController method for writing.
932 * @note Subclasses must not call this method.
933 */
934HRESULT Machine::i_registeredInit()
935{
936 AssertReturn(!i_isSessionMachine(), E_FAIL);
937 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
938 AssertReturn(mData->mUuid.isValid(), E_FAIL);
939 AssertReturn(!mData->mAccessible, E_FAIL);
940
941 HRESULT hrc = initDataAndChildObjects();
942 if (SUCCEEDED(hrc))
943 {
944 /* Temporarily reset the registered flag in order to let setters
945 * potentially called from loadSettings() succeed (isMutable() used in
946 * all setters will return FALSE for a Machine instance if mRegistered
947 * is TRUE). */
948 mData->mRegistered = FALSE;
949
950 PCVBOXCRYPTOIF pCryptoIf = NULL;
951 SecretKey *pKey = NULL;
952 const char *pszPassword = NULL;
953#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
954 /* Resolve password and cryptographic support interface if machine is encrypted. */
955 if (mData->mstrKeyId.isNotEmpty())
956 {
957 /* Get at the crpytographic interface. */
958 hrc = mParent->i_retainCryptoIf(&pCryptoIf);
959 if (SUCCEEDED(hrc))
960 {
961 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
962 if (RT_SUCCESS(vrc))
963 pszPassword = (const char *)pKey->getKeyBuffer();
964 else
965 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to retain key for key ID '%s' with %Rrc"),
966 mData->mstrKeyId.c_str(), vrc);
967 }
968 }
969#else
970 RT_NOREF(pKey);
971#endif
972
973 if (SUCCEEDED(hrc))
974 {
975 try
976 {
977 // load and parse machine XML; this will throw on XML or logic errors
978 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
979 pCryptoIf, pszPassword);
980
981 if (mData->mUuid != mData->pMachineConfigFile->uuid)
982 throw setError(E_FAIL,
983 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
984 mData->pMachineConfigFile->uuid.raw(),
985 mData->m_strConfigFileFull.c_str(),
986 mData->mUuid.toString().c_str(),
987 mParent->i_settingsFilePath().c_str());
988
989#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
990 // If config is encrypted, no exception is thrown allowing us to get the uuid and the encryption fields.
991 // We fill in the encryptions fields, and the rest will be filled in if all data parsed
992 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
993 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
994
995 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
996 hrc = setError(VBOX_E_PASSWORD_INCORRECT,
997 tr("Config decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
998 mData->pMachineConfigFile->uuid.raw());
999 else
1000#endif
1001 hrc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile, NULL /* const Guid *puuidRegistry */);
1002 if (FAILED(hrc)) throw hrc;
1003 }
1004 catch (HRESULT err)
1005 {
1006 /* we assume that error info is set by the thrower */
1007 hrc = err;
1008 }
1009 catch (...)
1010 {
1011 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
1012 }
1013
1014 /* Restore the registered flag (even on failure) */
1015 mData->mRegistered = TRUE;
1016 }
1017
1018#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1019 if (pCryptoIf)
1020 mParent->i_releaseCryptoIf(pCryptoIf);
1021 if (pKey)
1022 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
1023#endif
1024 }
1025
1026 if (SUCCEEDED(hrc))
1027 {
1028 /* Set mAccessible to TRUE only if we successfully locked and loaded
1029 * the settings file */
1030 mData->mAccessible = TRUE;
1031
1032 /* commit all changes made during loading the settings file */
1033 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
1034 /// @todo r=klaus for some reason the settings loading logic backs up
1035 // the settings, and therefore a commit is needed. Should probably be changed.
1036 }
1037 else
1038 {
1039 /* If the machine is registered, then, instead of returning a
1040 * failure, we mark it as inaccessible and set the result to
1041 * success to give it a try later */
1042
1043 /* fetch the current error info */
1044 mData->mAccessError = com::ErrorInfo();
1045 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
1046
1047 /* rollback all changes */
1048 i_rollback(false /* aNotify */);
1049
1050 // uninit media from this machine's media registry, or else
1051 // reloading the settings will fail
1052 mParent->i_unregisterMachineMedia(i_getId());
1053
1054 /* uninitialize the common part to make sure all data is reset to
1055 * default (null) values */
1056 uninitDataAndChildObjects();
1057
1058 hrc = S_OK;
1059 }
1060
1061 return hrc;
1062}
1063
1064/**
1065 * Uninitializes the instance.
1066 * Called either from FinalRelease() or by the parent when it gets destroyed.
1067 *
1068 * @note The caller of this method must make sure that this object
1069 * a) doesn't have active callers on the current thread and b) is not locked
1070 * by the current thread; otherwise uninit() will hang either a) due to
1071 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
1072 * a dead-lock caused by this thread waiting for all callers on the other
1073 * threads are done but preventing them from doing so by holding a lock.
1074 */
1075void Machine::uninit()
1076{
1077 LogFlowThisFuncEnter();
1078
1079 Assert(!isWriteLockOnCurrentThread());
1080
1081 Assert(!uRegistryNeedsSaving);
1082 if (uRegistryNeedsSaving)
1083 {
1084 AutoCaller autoCaller(this);
1085 if (SUCCEEDED(autoCaller.hrc()))
1086 {
1087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1088 i_saveSettings(NULL, alock, Machine::SaveS_Force);
1089 }
1090 }
1091
1092 /* Enclose the state transition Ready->InUninit->NotReady */
1093 AutoUninitSpan autoUninitSpan(this);
1094 if (autoUninitSpan.uninitDone())
1095 return;
1096
1097 Assert(!i_isSnapshotMachine());
1098 Assert(!i_isSessionMachine());
1099 Assert(!!mData);
1100
1101 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
1102 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
1103
1104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1105
1106 if (!mData->mSession.mMachine.isNull())
1107 {
1108 /* Theoretically, this can only happen if the VirtualBox server has been
1109 * terminated while there were clients running that owned open direct
1110 * sessions. Since in this case we are definitely called by
1111 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
1112 * won't happen on the client watcher thread (because it has a
1113 * VirtualBox caller for the duration of the
1114 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
1115 * cannot happen until the VirtualBox caller is released). This is
1116 * important, because SessionMachine::uninit() cannot correctly operate
1117 * after we return from this method (it expects the Machine instance is
1118 * still valid). We'll call it ourselves below.
1119 */
1120 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
1121 (SessionMachine*)mData->mSession.mMachine));
1122
1123 if (Global::IsOnlineOrTransient(mData->mMachineState))
1124 {
1125 Log1WarningThisFunc(("Setting state to Aborted!\n"));
1126 /* set machine state using SessionMachine reimplementation */
1127 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
1128 }
1129
1130 /*
1131 * Uninitialize SessionMachine using public uninit() to indicate
1132 * an unexpected uninitialization.
1133 */
1134 mData->mSession.mMachine->uninit();
1135 /* SessionMachine::uninit() must set mSession.mMachine to null */
1136 Assert(mData->mSession.mMachine.isNull());
1137 }
1138
1139 // uninit media from this machine's media registry, if they're still there
1140 Guid uuidMachine(i_getId());
1141
1142 /* the lock is no more necessary (SessionMachine is uninitialized) */
1143 alock.release();
1144
1145 /* XXX This will fail with
1146 * "cannot be closed because it is still attached to 1 virtual machines"
1147 * because at this point we did not call uninitDataAndChildObjects() yet
1148 * and therefore also removeBackReference() for all these media was not called! */
1149
1150 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
1151 mParent->i_unregisterMachineMedia(uuidMachine);
1152
1153 // has machine been modified?
1154 if (mData->flModifications)
1155 {
1156 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
1157 i_rollback(false /* aNotify */);
1158 }
1159
1160 if (mData->mAccessible)
1161 uninitDataAndChildObjects();
1162
1163#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1164 if (mData->mpKeyStore != NULL)
1165 delete mData->mpKeyStore;
1166#endif
1167
1168 /* free the essential data structure last */
1169 mData.free();
1170
1171 LogFlowThisFuncLeave();
1172}
1173
1174// Wrapped IMachine properties
1175/////////////////////////////////////////////////////////////////////////////
1176HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
1177{
1178 /* mParent is constant during life time, no need to lock */
1179 ComObjPtr<VirtualBox> pVirtualBox(mParent);
1180 aParent = pVirtualBox;
1181
1182 return S_OK;
1183}
1184
1185
1186HRESULT Machine::getAccessible(BOOL *aAccessible)
1187{
1188 /* In some cases (medium registry related), it is necessary to be able to
1189 * go through the list of all machines. Happens when an inaccessible VM
1190 * has a sensible medium registry. */
1191 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
1192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1193
1194 HRESULT hrc = S_OK;
1195
1196 if (!mData->mAccessible)
1197 {
1198 /* try to initialize the VM once more if not accessible */
1199
1200 AutoReinitSpan autoReinitSpan(this);
1201 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
1202
1203#ifdef DEBUG
1204 LogFlowThisFunc(("Dumping media backreferences\n"));
1205 mParent->i_dumpAllBackRefs();
1206#endif
1207
1208 if (mData->pMachineConfigFile)
1209 {
1210 // reset the XML file to force loadSettings() (called from i_registeredInit())
1211 // to parse it again; the file might have changed
1212 delete mData->pMachineConfigFile;
1213 mData->pMachineConfigFile = NULL;
1214 }
1215
1216 hrc = i_registeredInit();
1217
1218 if (SUCCEEDED(hrc) && mData->mAccessible)
1219 {
1220 autoReinitSpan.setSucceeded();
1221
1222 /* make sure interesting parties will notice the accessibility
1223 * state change */
1224 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
1225 mParent->i_onMachineDataChanged(mData->mUuid);
1226 }
1227 }
1228
1229 if (SUCCEEDED(hrc))
1230 *aAccessible = mData->mAccessible;
1231
1232 LogFlowThisFuncLeave();
1233
1234 return hrc;
1235}
1236
1237HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
1238{
1239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1240
1241 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
1242 {
1243 /* return shortly */
1244 aAccessError = NULL;
1245 return S_OK;
1246 }
1247
1248 HRESULT hrc = S_OK;
1249
1250 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1251 hrc = errorInfo.createObject();
1252 if (SUCCEEDED(hrc))
1253 {
1254 errorInfo->init(mData->mAccessError.getResultCode(),
1255 mData->mAccessError.getInterfaceID().ref(),
1256 Utf8Str(mData->mAccessError.getComponent()).c_str(),
1257 Utf8Str(mData->mAccessError.getText()));
1258 aAccessError = errorInfo;
1259 }
1260
1261 return hrc;
1262}
1263
1264HRESULT Machine::getName(com::Utf8Str &aName)
1265{
1266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1267
1268 aName = mUserData->s.strName;
1269
1270 return S_OK;
1271}
1272
1273HRESULT Machine::setName(const com::Utf8Str &aName)
1274{
1275 // prohibit setting a UUID only as the machine name, or else it can
1276 // never be found by findMachine()
1277 Guid test(aName);
1278
1279 if (test.isValid())
1280 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1281
1282 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1283
1284 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
1285 if (FAILED(hrc)) return hrc;
1286
1287 i_setModified(IsModified_MachineData);
1288 mUserData.backup();
1289 mUserData->s.strName = aName;
1290
1291 return S_OK;
1292}
1293
1294HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1295{
1296 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1297
1298 aDescription = mUserData->s.strDescription;
1299
1300 return S_OK;
1301}
1302
1303HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1304{
1305 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1306
1307 // this can be done in principle in any state as it doesn't affect the VM
1308 // significantly, but play safe by not messing around while complex
1309 // activities are going on
1310 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1311 if (FAILED(hrc)) return hrc;
1312
1313 i_setModified(IsModified_MachineData);
1314 mUserData.backup();
1315 mUserData->s.strDescription = aDescription;
1316
1317 return S_OK;
1318}
1319
1320HRESULT Machine::getId(com::Guid &aId)
1321{
1322 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1323
1324 aId = mData->mUuid;
1325
1326 return S_OK;
1327}
1328
1329HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1330{
1331 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1332 aGroups.resize(mUserData->s.llGroups.size());
1333 size_t i = 0;
1334 for (StringsList::const_iterator
1335 it = mUserData->s.llGroups.begin();
1336 it != mUserData->s.llGroups.end();
1337 ++it, ++i)
1338 aGroups[i] = (*it);
1339
1340 return S_OK;
1341}
1342
1343HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1344{
1345 StringsList llGroups;
1346 HRESULT hrc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1347 if (FAILED(hrc))
1348 return hrc;
1349
1350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1351
1352 hrc = i_checkStateDependency(MutableOrSavedStateDep);
1353 if (FAILED(hrc)) return hrc;
1354
1355 i_setModified(IsModified_MachineData);
1356 mUserData.backup();
1357 mUserData->s.llGroups = llGroups;
1358
1359 mParent->i_onMachineGroupsChanged(mData->mUuid);
1360 return S_OK;
1361}
1362
1363HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1364{
1365 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1366
1367 aOSTypeId = mUserData->s.strOsType;
1368
1369 return S_OK;
1370}
1371
1372HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1373{
1374 /* look up the object by Id to check it is valid */
1375 ComObjPtr<GuestOSType> pGuestOSType;
1376 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1377
1378 /* when setting, always use the "etalon" value for consistency -- lookup
1379 * by ID is case-insensitive and the input value may have different case */
1380 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1381
1382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1383
1384 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1385 if (FAILED(hrc)) return hrc;
1386
1387 i_setModified(IsModified_MachineData);
1388 mUserData.backup();
1389 mUserData->s.strOsType = osTypeId;
1390
1391 return S_OK;
1392}
1393
1394HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1395{
1396 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1397
1398 *aFirmwareType = mHWData->mFirmwareType;
1399
1400 return S_OK;
1401}
1402
1403HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1404{
1405 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1406
1407 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1408 if (FAILED(hrc)) return hrc;
1409
1410 i_setModified(IsModified_MachineData);
1411 mHWData.backup();
1412 mHWData->mFirmwareType = aFirmwareType;
1413 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1414 alock.release();
1415
1416 mNvramStore->i_updateNonVolatileStorageFile(strNVRAM);
1417
1418 return S_OK;
1419}
1420
1421HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1422{
1423 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1424
1425 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1426
1427 return S_OK;
1428}
1429
1430HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1431{
1432 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1433
1434 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1435 if (FAILED(hrc)) return hrc;
1436
1437 i_setModified(IsModified_MachineData);
1438 mHWData.backup();
1439 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1440
1441 return S_OK;
1442}
1443
1444HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1445{
1446 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1447
1448 *aPointingHIDType = mHWData->mPointingHIDType;
1449
1450 return S_OK;
1451}
1452
1453HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1454{
1455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1456
1457 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1458 if (FAILED(hrc)) return hrc;
1459
1460 i_setModified(IsModified_MachineData);
1461 mHWData.backup();
1462 mHWData->mPointingHIDType = aPointingHIDType;
1463
1464 return S_OK;
1465}
1466
1467HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1468{
1469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1470
1471 *aChipsetType = mHWData->mChipsetType;
1472
1473 return S_OK;
1474}
1475
1476HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1477{
1478 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1479
1480 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1481 if (FAILED(hrc)) return hrc;
1482
1483 if (aChipsetType != mHWData->mChipsetType)
1484 {
1485 i_setModified(IsModified_MachineData);
1486 mHWData.backup();
1487 mHWData->mChipsetType = aChipsetType;
1488
1489 // Resize network adapter array, to be finalized on commit/rollback.
1490 // We must not throw away entries yet, otherwise settings are lost
1491 // without a way to roll back.
1492 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1493 size_t oldCount = mNetworkAdapters.size();
1494 if (newCount > oldCount)
1495 {
1496 mNetworkAdapters.resize(newCount);
1497 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1498 {
1499 unconst(mNetworkAdapters[slot]).createObject();
1500 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1501 }
1502 }
1503 }
1504
1505 return S_OK;
1506}
1507
1508HRESULT Machine::getIommuType(IommuType_T *aIommuType)
1509{
1510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1511
1512 *aIommuType = mHWData->mIommuType;
1513
1514 return S_OK;
1515}
1516
1517HRESULT Machine::setIommuType(IommuType_T aIommuType)
1518{
1519 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1520
1521 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1522 if (FAILED(hrc)) return hrc;
1523
1524 if (aIommuType != mHWData->mIommuType)
1525 {
1526 if (aIommuType == IommuType_Intel)
1527 {
1528#ifndef VBOX_WITH_IOMMU_INTEL
1529 LogRelFunc(("Setting Intel IOMMU when Intel IOMMU support not available!\n"));
1530 return E_UNEXPECTED;
1531#endif
1532 }
1533
1534 i_setModified(IsModified_MachineData);
1535 mHWData.backup();
1536 mHWData->mIommuType = aIommuType;
1537 }
1538
1539 return S_OK;
1540}
1541
1542HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1543{
1544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1545
1546 aParavirtDebug = mHWData->mParavirtDebug;
1547 return S_OK;
1548}
1549
1550HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1551{
1552 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1553
1554 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1555 if (FAILED(hrc)) return hrc;
1556
1557 /** @todo Parse/validate options? */
1558 if (aParavirtDebug != mHWData->mParavirtDebug)
1559 {
1560 i_setModified(IsModified_MachineData);
1561 mHWData.backup();
1562 mHWData->mParavirtDebug = aParavirtDebug;
1563 }
1564
1565 return S_OK;
1566}
1567
1568HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1569{
1570 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1571
1572 *aParavirtProvider = mHWData->mParavirtProvider;
1573
1574 return S_OK;
1575}
1576
1577HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1578{
1579 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1580
1581 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1582 if (FAILED(hrc)) return hrc;
1583
1584 if (aParavirtProvider != mHWData->mParavirtProvider)
1585 {
1586 i_setModified(IsModified_MachineData);
1587 mHWData.backup();
1588 mHWData->mParavirtProvider = aParavirtProvider;
1589 }
1590
1591 return S_OK;
1592}
1593
1594HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1595{
1596 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1597
1598 *aParavirtProvider = mHWData->mParavirtProvider;
1599 switch (mHWData->mParavirtProvider)
1600 {
1601 case ParavirtProvider_None:
1602 case ParavirtProvider_HyperV:
1603 case ParavirtProvider_KVM:
1604 case ParavirtProvider_Minimal:
1605 break;
1606
1607 /* Resolve dynamic provider types to the effective types. */
1608 default:
1609 {
1610 ComObjPtr<GuestOSType> pGuestOSType;
1611 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1612 pGuestOSType);
1613 if (FAILED(hrc2) || pGuestOSType.isNull())
1614 {
1615 *aParavirtProvider = ParavirtProvider_None;
1616 break;
1617 }
1618
1619 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1620 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1621
1622 switch (mHWData->mParavirtProvider)
1623 {
1624 case ParavirtProvider_Legacy:
1625 {
1626 if (fOsXGuest)
1627 *aParavirtProvider = ParavirtProvider_Minimal;
1628 else
1629 *aParavirtProvider = ParavirtProvider_None;
1630 break;
1631 }
1632
1633 case ParavirtProvider_Default:
1634 {
1635 if (fOsXGuest)
1636 *aParavirtProvider = ParavirtProvider_Minimal;
1637 else if ( mUserData->s.strOsType == "Windows11_64"
1638 || mUserData->s.strOsType == "Windows10"
1639 || mUserData->s.strOsType == "Windows10_64"
1640 || mUserData->s.strOsType == "Windows81"
1641 || mUserData->s.strOsType == "Windows81_64"
1642 || mUserData->s.strOsType == "Windows8"
1643 || mUserData->s.strOsType == "Windows8_64"
1644 || mUserData->s.strOsType == "Windows7"
1645 || mUserData->s.strOsType == "Windows7_64"
1646 || mUserData->s.strOsType == "WindowsVista"
1647 || mUserData->s.strOsType == "WindowsVista_64"
1648 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1649 || mUserData->s.strOsType.startsWith("Windows201"))
1650 && mUserData->s.strOsType.endsWith("_64"))
1651 || mUserData->s.strOsType == "Windows2012"
1652 || mUserData->s.strOsType == "Windows2012_64"
1653 || mUserData->s.strOsType == "Windows2008"
1654 || mUserData->s.strOsType == "Windows2008_64")
1655 {
1656 *aParavirtProvider = ParavirtProvider_HyperV;
1657 }
1658 else if (guestTypeFamilyId == "Linux" &&
1659 mUserData->s.strOsType != "Linux22" && // Linux22 and Linux24{_64} excluded as they're too old
1660 mUserData->s.strOsType != "Linux24" && // to have any KVM paravirtualization support.
1661 mUserData->s.strOsType != "Linux24_64")
1662 {
1663 *aParavirtProvider = ParavirtProvider_KVM;
1664 }
1665 else
1666 *aParavirtProvider = ParavirtProvider_None;
1667 break;
1668 }
1669
1670 default: AssertFailedBreak(); /* Shut up MSC. */
1671 }
1672 break;
1673 }
1674 }
1675
1676 Assert( *aParavirtProvider == ParavirtProvider_None
1677 || *aParavirtProvider == ParavirtProvider_Minimal
1678 || *aParavirtProvider == ParavirtProvider_HyperV
1679 || *aParavirtProvider == ParavirtProvider_KVM);
1680 return S_OK;
1681}
1682
1683HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1684{
1685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1686
1687 aHardwareVersion = mHWData->mHWVersion;
1688
1689 return S_OK;
1690}
1691
1692HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1693{
1694 /* check known version */
1695 Utf8Str hwVersion = aHardwareVersion;
1696 if ( hwVersion.compare("1") != 0
1697 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1698 return setError(E_INVALIDARG,
1699 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1700
1701 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1702
1703 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1704 if (FAILED(hrc)) return hrc;
1705
1706 i_setModified(IsModified_MachineData);
1707 mHWData.backup();
1708 mHWData->mHWVersion = aHardwareVersion;
1709
1710 return S_OK;
1711}
1712
1713HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1714{
1715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1716
1717 if (!mHWData->mHardwareUUID.isZero())
1718 aHardwareUUID = mHWData->mHardwareUUID;
1719 else
1720 aHardwareUUID = mData->mUuid;
1721
1722 return S_OK;
1723}
1724
1725HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1726{
1727 if (!aHardwareUUID.isValid())
1728 return E_INVALIDARG;
1729
1730 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1731
1732 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1733 if (FAILED(hrc)) return hrc;
1734
1735 i_setModified(IsModified_MachineData);
1736 mHWData.backup();
1737 if (aHardwareUUID == mData->mUuid)
1738 mHWData->mHardwareUUID.clear();
1739 else
1740 mHWData->mHardwareUUID = aHardwareUUID;
1741
1742 return S_OK;
1743}
1744
1745HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1746{
1747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1748
1749 *aMemorySize = mHWData->mMemorySize;
1750
1751 return S_OK;
1752}
1753
1754HRESULT Machine::setMemorySize(ULONG aMemorySize)
1755{
1756 /* check RAM limits */
1757 if ( aMemorySize < MM_RAM_MIN_IN_MB
1758 || aMemorySize > MM_RAM_MAX_IN_MB
1759 )
1760 return setError(E_INVALIDARG,
1761 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1762 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1763
1764 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1765
1766 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1767 if (FAILED(hrc)) return hrc;
1768
1769 i_setModified(IsModified_MachineData);
1770 mHWData.backup();
1771 mHWData->mMemorySize = aMemorySize;
1772
1773 return S_OK;
1774}
1775
1776HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1777{
1778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1779
1780 *aCPUCount = mHWData->mCPUCount;
1781
1782 return S_OK;
1783}
1784
1785HRESULT Machine::setCPUCount(ULONG aCPUCount)
1786{
1787 /* check CPU limits */
1788 if ( aCPUCount < SchemaDefs::MinCPUCount
1789 || aCPUCount > SchemaDefs::MaxCPUCount
1790 )
1791 return setError(E_INVALIDARG,
1792 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1793 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1794
1795 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1796
1797 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1798 if (mHWData->mCPUHotPlugEnabled)
1799 {
1800 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1801 {
1802 if (mHWData->mCPUAttached[idx])
1803 return setError(E_INVALIDARG,
1804 tr("There is still a CPU attached to socket %lu."
1805 "Detach the CPU before removing the socket"),
1806 aCPUCount, idx+1);
1807 }
1808 }
1809
1810 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1811 if (FAILED(hrc)) return hrc;
1812
1813 i_setModified(IsModified_MachineData);
1814 mHWData.backup();
1815 mHWData->mCPUCount = aCPUCount;
1816
1817 return S_OK;
1818}
1819
1820HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1821{
1822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1823
1824 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1825
1826 return S_OK;
1827}
1828
1829HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1830{
1831 /* check throttle limits */
1832 if ( aCPUExecutionCap < 1
1833 || aCPUExecutionCap > 100
1834 )
1835 return setError(E_INVALIDARG,
1836 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1837 aCPUExecutionCap, 1, 100);
1838
1839 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1840
1841 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
1842 if (FAILED(hrc)) return hrc;
1843
1844 alock.release();
1845 hrc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1846 alock.acquire();
1847 if (FAILED(hrc)) return hrc;
1848
1849 i_setModified(IsModified_MachineData);
1850 mHWData.backup();
1851 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1852
1853 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1854 if (Global::IsOnline(mData->mMachineState))
1855 i_saveSettings(NULL, alock);
1856
1857 return S_OK;
1858}
1859
1860HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1861{
1862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1863
1864 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1865
1866 return S_OK;
1867}
1868
1869HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1870{
1871 HRESULT hrc = S_OK;
1872
1873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1874
1875 hrc = i_checkStateDependency(MutableStateDep);
1876 if (FAILED(hrc)) return hrc;
1877
1878 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1879 {
1880 if (aCPUHotPlugEnabled)
1881 {
1882 i_setModified(IsModified_MachineData);
1883 mHWData.backup();
1884
1885 /* Add the amount of CPUs currently attached */
1886 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1887 mHWData->mCPUAttached[i] = true;
1888 }
1889 else
1890 {
1891 /*
1892 * We can disable hotplug only if the amount of maximum CPUs is equal
1893 * to the amount of attached CPUs
1894 */
1895 unsigned cCpusAttached = 0;
1896 unsigned iHighestId = 0;
1897
1898 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1899 {
1900 if (mHWData->mCPUAttached[i])
1901 {
1902 cCpusAttached++;
1903 iHighestId = i;
1904 }
1905 }
1906
1907 if ( (cCpusAttached != mHWData->mCPUCount)
1908 || (iHighestId >= mHWData->mCPUCount))
1909 return setError(E_INVALIDARG,
1910 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1911
1912 i_setModified(IsModified_MachineData);
1913 mHWData.backup();
1914 }
1915 }
1916
1917 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1918
1919 return hrc;
1920}
1921
1922HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1923{
1924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1925
1926 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1927
1928 return S_OK;
1929}
1930
1931HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1932{
1933 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1934
1935 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1936 if (SUCCEEDED(hrc))
1937 {
1938 i_setModified(IsModified_MachineData);
1939 mHWData.backup();
1940 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1941 }
1942 return hrc;
1943}
1944
1945HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1946{
1947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1948 aCPUProfile = mHWData->mCpuProfile;
1949 return S_OK;
1950}
1951
1952HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1953{
1954 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1955 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1956 if (SUCCEEDED(hrc))
1957 {
1958 i_setModified(IsModified_MachineData);
1959 mHWData.backup();
1960 /* Empty equals 'host'. */
1961 if (aCPUProfile.isNotEmpty())
1962 mHWData->mCpuProfile = aCPUProfile;
1963 else
1964 mHWData->mCpuProfile = "host";
1965 }
1966 return hrc;
1967}
1968
1969HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1970{
1971#ifdef VBOX_WITH_USB_CARDREADER
1972 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1973
1974 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1975
1976 return S_OK;
1977#else
1978 NOREF(aEmulatedUSBCardReaderEnabled);
1979 return E_NOTIMPL;
1980#endif
1981}
1982
1983HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1984{
1985#ifdef VBOX_WITH_USB_CARDREADER
1986 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1987
1988 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
1989 if (FAILED(hrc)) return hrc;
1990
1991 i_setModified(IsModified_MachineData);
1992 mHWData.backup();
1993 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1994
1995 return S_OK;
1996#else
1997 NOREF(aEmulatedUSBCardReaderEnabled);
1998 return E_NOTIMPL;
1999#endif
2000}
2001
2002HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
2003{
2004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2005
2006 *aHPETEnabled = mHWData->mHPETEnabled;
2007
2008 return S_OK;
2009}
2010
2011HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
2012{
2013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2014
2015 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2016 if (FAILED(hrc)) return hrc;
2017
2018 i_setModified(IsModified_MachineData);
2019 mHWData.backup();
2020
2021 mHWData->mHPETEnabled = aHPETEnabled;
2022
2023 return hrc;
2024}
2025
2026/** @todo this method should not be public */
2027HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2028{
2029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2030
2031 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2032
2033 return S_OK;
2034}
2035
2036/**
2037 * Set the memory balloon size.
2038 *
2039 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2040 * we have to make sure that we never call IGuest from here.
2041 */
2042HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2043{
2044 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2045#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2046 /* check limits */
2047 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2048 return setError(E_INVALIDARG,
2049 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2050 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2051
2052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2053
2054 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
2055 if (FAILED(hrc)) return hrc;
2056
2057 i_setModified(IsModified_MachineData);
2058 mHWData.backup();
2059 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2060
2061 return S_OK;
2062#else
2063 NOREF(aMemoryBalloonSize);
2064 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2065#endif
2066}
2067
2068HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2069{
2070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2071
2072 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2073 return S_OK;
2074}
2075
2076HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2077{
2078#ifdef VBOX_WITH_PAGE_SHARING
2079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2080
2081 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2082 if (FAILED(hrc)) return hrc;
2083
2084 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2085 i_setModified(IsModified_MachineData);
2086 mHWData.backup();
2087 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2088 return S_OK;
2089#else
2090 NOREF(aPageFusionEnabled);
2091 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2092#endif
2093}
2094
2095HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2096{
2097 /* mBIOSSettings is constant during life time, no need to lock */
2098 aBIOSSettings = mBIOSSettings;
2099
2100 return S_OK;
2101}
2102
2103HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
2104{
2105 /* mTrustedPlatformModule is constant during life time, no need to lock */
2106 aTrustedPlatformModule = mTrustedPlatformModule;
2107
2108 return S_OK;
2109}
2110
2111HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
2112{
2113 /* mNvramStore is constant during life time, no need to lock */
2114 aNvramStore = mNvramStore;
2115
2116 return S_OK;
2117}
2118
2119HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
2120{
2121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2122
2123 aRecordingSettings = mRecordingSettings;
2124
2125 return S_OK;
2126}
2127
2128HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
2129{
2130 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2131
2132 aGraphicsAdapter = mGraphicsAdapter;
2133
2134 return S_OK;
2135}
2136
2137HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2138{
2139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2140
2141 switch (aProperty)
2142 {
2143 case CPUPropertyType_PAE:
2144 *aValue = mHWData->mPAEEnabled;
2145 break;
2146
2147 case CPUPropertyType_LongMode:
2148 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2149 *aValue = TRUE;
2150 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2151 *aValue = FALSE;
2152#if HC_ARCH_BITS == 64
2153 else
2154 *aValue = TRUE;
2155#else
2156 else
2157 {
2158 *aValue = FALSE;
2159
2160 ComObjPtr<GuestOSType> pGuestOSType;
2161 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2162 pGuestOSType);
2163 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
2164 {
2165 if (pGuestOSType->i_is64Bit())
2166 {
2167 ComObjPtr<Host> pHost = mParent->i_host();
2168 alock.release();
2169
2170 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2171 if (FAILED(hrc2))
2172 *aValue = FALSE;
2173 }
2174 }
2175 }
2176#endif
2177 break;
2178
2179 case CPUPropertyType_TripleFaultReset:
2180 *aValue = mHWData->mTripleFaultReset;
2181 break;
2182
2183 case CPUPropertyType_APIC:
2184 *aValue = mHWData->mAPIC;
2185 break;
2186
2187 case CPUPropertyType_X2APIC:
2188 *aValue = mHWData->mX2APIC;
2189 break;
2190
2191 case CPUPropertyType_IBPBOnVMExit:
2192 *aValue = mHWData->mIBPBOnVMExit;
2193 break;
2194
2195 case CPUPropertyType_IBPBOnVMEntry:
2196 *aValue = mHWData->mIBPBOnVMEntry;
2197 break;
2198
2199 case CPUPropertyType_SpecCtrl:
2200 *aValue = mHWData->mSpecCtrl;
2201 break;
2202
2203 case CPUPropertyType_SpecCtrlByHost:
2204 *aValue = mHWData->mSpecCtrlByHost;
2205 break;
2206
2207 case CPUPropertyType_HWVirt:
2208 *aValue = mHWData->mNestedHWVirt;
2209 break;
2210
2211 case CPUPropertyType_L1DFlushOnEMTScheduling:
2212 *aValue = mHWData->mL1DFlushOnSched;
2213 break;
2214
2215 case CPUPropertyType_L1DFlushOnVMEntry:
2216 *aValue = mHWData->mL1DFlushOnVMEntry;
2217 break;
2218
2219 case CPUPropertyType_MDSClearOnEMTScheduling:
2220 *aValue = mHWData->mMDSClearOnSched;
2221 break;
2222
2223 case CPUPropertyType_MDSClearOnVMEntry:
2224 *aValue = mHWData->mMDSClearOnVMEntry;
2225 break;
2226
2227 default:
2228 return E_INVALIDARG;
2229 }
2230 return S_OK;
2231}
2232
2233HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2234{
2235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2236
2237 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2238 if (FAILED(hrc)) return hrc;
2239
2240 switch (aProperty)
2241 {
2242 case CPUPropertyType_PAE:
2243 i_setModified(IsModified_MachineData);
2244 mHWData.backup();
2245 mHWData->mPAEEnabled = !!aValue;
2246 break;
2247
2248 case CPUPropertyType_LongMode:
2249 i_setModified(IsModified_MachineData);
2250 mHWData.backup();
2251 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2252 break;
2253
2254 case CPUPropertyType_TripleFaultReset:
2255 i_setModified(IsModified_MachineData);
2256 mHWData.backup();
2257 mHWData->mTripleFaultReset = !!aValue;
2258 break;
2259
2260 case CPUPropertyType_APIC:
2261 if (mHWData->mX2APIC)
2262 aValue = TRUE;
2263 i_setModified(IsModified_MachineData);
2264 mHWData.backup();
2265 mHWData->mAPIC = !!aValue;
2266 break;
2267
2268 case CPUPropertyType_X2APIC:
2269 i_setModified(IsModified_MachineData);
2270 mHWData.backup();
2271 mHWData->mX2APIC = !!aValue;
2272 if (aValue)
2273 mHWData->mAPIC = !!aValue;
2274 break;
2275
2276 case CPUPropertyType_IBPBOnVMExit:
2277 i_setModified(IsModified_MachineData);
2278 mHWData.backup();
2279 mHWData->mIBPBOnVMExit = !!aValue;
2280 break;
2281
2282 case CPUPropertyType_IBPBOnVMEntry:
2283 i_setModified(IsModified_MachineData);
2284 mHWData.backup();
2285 mHWData->mIBPBOnVMEntry = !!aValue;
2286 break;
2287
2288 case CPUPropertyType_SpecCtrl:
2289 i_setModified(IsModified_MachineData);
2290 mHWData.backup();
2291 mHWData->mSpecCtrl = !!aValue;
2292 break;
2293
2294 case CPUPropertyType_SpecCtrlByHost:
2295 i_setModified(IsModified_MachineData);
2296 mHWData.backup();
2297 mHWData->mSpecCtrlByHost = !!aValue;
2298 break;
2299
2300 case CPUPropertyType_HWVirt:
2301 i_setModified(IsModified_MachineData);
2302 mHWData.backup();
2303 mHWData->mNestedHWVirt = !!aValue;
2304 break;
2305
2306 case CPUPropertyType_L1DFlushOnEMTScheduling:
2307 i_setModified(IsModified_MachineData);
2308 mHWData.backup();
2309 mHWData->mL1DFlushOnSched = !!aValue;
2310 break;
2311
2312 case CPUPropertyType_L1DFlushOnVMEntry:
2313 i_setModified(IsModified_MachineData);
2314 mHWData.backup();
2315 mHWData->mL1DFlushOnVMEntry = !!aValue;
2316 break;
2317
2318 case CPUPropertyType_MDSClearOnEMTScheduling:
2319 i_setModified(IsModified_MachineData);
2320 mHWData.backup();
2321 mHWData->mMDSClearOnSched = !!aValue;
2322 break;
2323
2324 case CPUPropertyType_MDSClearOnVMEntry:
2325 i_setModified(IsModified_MachineData);
2326 mHWData.backup();
2327 mHWData->mMDSClearOnVMEntry = !!aValue;
2328 break;
2329
2330 default:
2331 return E_INVALIDARG;
2332 }
2333 return S_OK;
2334}
2335
2336HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2337 ULONG *aValEcx, ULONG *aValEdx)
2338{
2339 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2340 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2341 {
2342 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2343 it != mHWData->mCpuIdLeafList.end();
2344 ++it)
2345 {
2346 if (aOrdinal == 0)
2347 {
2348 const settings::CpuIdLeaf &rLeaf= *it;
2349 *aIdx = rLeaf.idx;
2350 *aSubIdx = rLeaf.idxSub;
2351 *aValEax = rLeaf.uEax;
2352 *aValEbx = rLeaf.uEbx;
2353 *aValEcx = rLeaf.uEcx;
2354 *aValEdx = rLeaf.uEdx;
2355 return S_OK;
2356 }
2357 aOrdinal--;
2358 }
2359 }
2360 return E_INVALIDARG;
2361}
2362
2363HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2364{
2365 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2366
2367 /*
2368 * Search the list.
2369 */
2370 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2371 {
2372 const settings::CpuIdLeaf &rLeaf= *it;
2373 if ( rLeaf.idx == aIdx
2374 && ( aSubIdx == UINT32_MAX
2375 || rLeaf.idxSub == aSubIdx) )
2376 {
2377 *aValEax = rLeaf.uEax;
2378 *aValEbx = rLeaf.uEbx;
2379 *aValEcx = rLeaf.uEcx;
2380 *aValEdx = rLeaf.uEdx;
2381 return S_OK;
2382 }
2383 }
2384
2385 return E_INVALIDARG;
2386}
2387
2388
2389HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2390{
2391 /*
2392 * Validate input before taking locks and checking state.
2393 */
2394 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2395 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2396 if ( aIdx >= UINT32_C(0x20)
2397 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2398 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2399 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2400
2401 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2402 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2403 if (FAILED(hrc)) return hrc;
2404
2405 /*
2406 * Impose a maximum number of leaves.
2407 */
2408 if (mHWData->mCpuIdLeafList.size() > 256)
2409 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2410
2411 /*
2412 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2413 */
2414 i_setModified(IsModified_MachineData);
2415 mHWData.backup();
2416
2417 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2418 {
2419 settings::CpuIdLeaf &rLeaf= *it;
2420 if ( rLeaf.idx == aIdx
2421 && ( aSubIdx == UINT32_MAX
2422 || rLeaf.idxSub == aSubIdx) )
2423 it = mHWData->mCpuIdLeafList.erase(it);
2424 else
2425 ++it;
2426 }
2427
2428 settings::CpuIdLeaf NewLeaf;
2429 NewLeaf.idx = aIdx;
2430 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2431 NewLeaf.uEax = aValEax;
2432 NewLeaf.uEbx = aValEbx;
2433 NewLeaf.uEcx = aValEcx;
2434 NewLeaf.uEdx = aValEdx;
2435 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2436 return S_OK;
2437}
2438
2439HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2440{
2441 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2442
2443 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2444 if (FAILED(hrc)) return hrc;
2445
2446 /*
2447 * Do the removal.
2448 */
2449 bool fModified = mHWData.isBackedUp();
2450 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2451 {
2452 settings::CpuIdLeaf &rLeaf= *it;
2453 if ( rLeaf.idx == aIdx
2454 && ( aSubIdx == UINT32_MAX
2455 || rLeaf.idxSub == aSubIdx) )
2456 {
2457 if (!fModified)
2458 {
2459 fModified = true;
2460 i_setModified(IsModified_MachineData);
2461 mHWData.backup();
2462 // Start from the beginning, since mHWData.backup() creates
2463 // a new list, causing iterator mixup. This makes sure that
2464 // the settings are not unnecessarily marked as modified,
2465 // at the price of extra list walking.
2466 it = mHWData->mCpuIdLeafList.begin();
2467 }
2468 else
2469 it = mHWData->mCpuIdLeafList.erase(it);
2470 }
2471 else
2472 ++it;
2473 }
2474
2475 return S_OK;
2476}
2477
2478HRESULT Machine::removeAllCPUIDLeaves()
2479{
2480 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2481
2482 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2483 if (FAILED(hrc)) return hrc;
2484
2485 if (mHWData->mCpuIdLeafList.size() > 0)
2486 {
2487 i_setModified(IsModified_MachineData);
2488 mHWData.backup();
2489
2490 mHWData->mCpuIdLeafList.clear();
2491 }
2492
2493 return S_OK;
2494}
2495HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2496{
2497 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2498
2499 switch(aProperty)
2500 {
2501 case HWVirtExPropertyType_Enabled:
2502 *aValue = mHWData->mHWVirtExEnabled;
2503 break;
2504
2505 case HWVirtExPropertyType_VPID:
2506 *aValue = mHWData->mHWVirtExVPIDEnabled;
2507 break;
2508
2509 case HWVirtExPropertyType_NestedPaging:
2510 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2511 break;
2512
2513 case HWVirtExPropertyType_UnrestrictedExecution:
2514 *aValue = mHWData->mHWVirtExUXEnabled;
2515 break;
2516
2517 case HWVirtExPropertyType_LargePages:
2518 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2519 break;
2520
2521 case HWVirtExPropertyType_Force:
2522 *aValue = mHWData->mHWVirtExForceEnabled;
2523 break;
2524
2525 case HWVirtExPropertyType_UseNativeApi:
2526 *aValue = mHWData->mHWVirtExUseNativeApi;
2527 break;
2528
2529 case HWVirtExPropertyType_VirtVmsaveVmload:
2530 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2531 break;
2532
2533 default:
2534 return E_INVALIDARG;
2535 }
2536 return S_OK;
2537}
2538
2539HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2540{
2541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2542
2543 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2544 if (FAILED(hrc)) return hrc;
2545
2546 switch (aProperty)
2547 {
2548 case HWVirtExPropertyType_Enabled:
2549 i_setModified(IsModified_MachineData);
2550 mHWData.backup();
2551 mHWData->mHWVirtExEnabled = !!aValue;
2552 break;
2553
2554 case HWVirtExPropertyType_VPID:
2555 i_setModified(IsModified_MachineData);
2556 mHWData.backup();
2557 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2558 break;
2559
2560 case HWVirtExPropertyType_NestedPaging:
2561 i_setModified(IsModified_MachineData);
2562 mHWData.backup();
2563 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2564 break;
2565
2566 case HWVirtExPropertyType_UnrestrictedExecution:
2567 i_setModified(IsModified_MachineData);
2568 mHWData.backup();
2569 mHWData->mHWVirtExUXEnabled = !!aValue;
2570 break;
2571
2572 case HWVirtExPropertyType_LargePages:
2573 i_setModified(IsModified_MachineData);
2574 mHWData.backup();
2575 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2576 break;
2577
2578 case HWVirtExPropertyType_Force:
2579 i_setModified(IsModified_MachineData);
2580 mHWData.backup();
2581 mHWData->mHWVirtExForceEnabled = !!aValue;
2582 break;
2583
2584 case HWVirtExPropertyType_UseNativeApi:
2585 i_setModified(IsModified_MachineData);
2586 mHWData.backup();
2587 mHWData->mHWVirtExUseNativeApi = !!aValue;
2588 break;
2589
2590 case HWVirtExPropertyType_VirtVmsaveVmload:
2591 i_setModified(IsModified_MachineData);
2592 mHWData.backup();
2593 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2594 break;
2595
2596 default:
2597 return E_INVALIDARG;
2598 }
2599
2600 return S_OK;
2601}
2602
2603HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2604{
2605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2606
2607 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2608
2609 return S_OK;
2610}
2611
2612HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2613{
2614 /** @todo (r=dmik):
2615 * 1. Allow to change the name of the snapshot folder containing snapshots
2616 * 2. Rename the folder on disk instead of just changing the property
2617 * value (to be smart and not to leave garbage). Note that it cannot be
2618 * done here because the change may be rolled back. Thus, the right
2619 * place is #saveSettings().
2620 */
2621
2622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2623
2624 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2625 if (FAILED(hrc)) return hrc;
2626
2627 if (!mData->mCurrentSnapshot.isNull())
2628 return setError(E_FAIL,
2629 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2630
2631 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2632
2633 if (strSnapshotFolder.isEmpty())
2634 strSnapshotFolder = "Snapshots";
2635 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2636 if (RT_FAILURE(vrc))
2637 return setErrorBoth(E_FAIL, vrc,
2638 tr("Invalid snapshot folder '%s' (%Rrc)"),
2639 strSnapshotFolder.c_str(), vrc);
2640
2641 i_setModified(IsModified_MachineData);
2642 mUserData.backup();
2643
2644 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2645
2646 return S_OK;
2647}
2648
2649HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2650{
2651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2652
2653 aMediumAttachments.resize(mMediumAttachments->size());
2654 size_t i = 0;
2655 for (MediumAttachmentList::const_iterator
2656 it = mMediumAttachments->begin();
2657 it != mMediumAttachments->end();
2658 ++it, ++i)
2659 aMediumAttachments[i] = *it;
2660
2661 return S_OK;
2662}
2663
2664HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2665{
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 Assert(!!mVRDEServer);
2669
2670 aVRDEServer = mVRDEServer;
2671
2672 return S_OK;
2673}
2674
2675HRESULT Machine::getAudioSettings(ComPtr<IAudioSettings> &aAudioSettings)
2676{
2677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2678
2679 aAudioSettings = mAudioSettings;
2680
2681 return S_OK;
2682}
2683
2684HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2685{
2686#ifdef VBOX_WITH_VUSB
2687 clearError();
2688 MultiResult hrcMult(S_OK);
2689
2690# ifdef VBOX_WITH_USB
2691 hrcMult = mParent->i_host()->i_checkUSBProxyService();
2692 if (FAILED(hrcMult)) return hrcMult;
2693# endif
2694
2695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2696
2697 aUSBControllers.resize(mUSBControllers->size());
2698 size_t i = 0;
2699 for (USBControllerList::const_iterator
2700 it = mUSBControllers->begin();
2701 it != mUSBControllers->end();
2702 ++it, ++i)
2703 aUSBControllers[i] = *it;
2704
2705 return S_OK;
2706#else
2707 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2708 * extended error info to indicate that USB is simply not available
2709 * (w/o treating it as a failure), for example, as in OSE */
2710 NOREF(aUSBControllers);
2711 ReturnComNotImplemented();
2712#endif /* VBOX_WITH_VUSB */
2713}
2714
2715HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2716{
2717#ifdef VBOX_WITH_VUSB
2718 clearError();
2719 MultiResult hrcMult(S_OK);
2720
2721# ifdef VBOX_WITH_USB
2722 hrcMult = mParent->i_host()->i_checkUSBProxyService();
2723 if (FAILED(hrcMult)) return hrcMult;
2724# endif
2725
2726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2727
2728 aUSBDeviceFilters = mUSBDeviceFilters;
2729 return hrcMult;
2730#else
2731 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2732 * extended error info to indicate that USB is simply not available
2733 * (w/o treating it as a failure), for example, as in OSE */
2734 NOREF(aUSBDeviceFilters);
2735 ReturnComNotImplemented();
2736#endif /* VBOX_WITH_VUSB */
2737}
2738
2739HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2740{
2741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2742
2743 aSettingsFilePath = mData->m_strConfigFileFull;
2744
2745 return S_OK;
2746}
2747
2748HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2749{
2750 RT_NOREF(aSettingsFilePath);
2751 ReturnComNotImplemented();
2752}
2753
2754HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2755{
2756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2757
2758 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2759 if (FAILED(hrc)) return hrc;
2760
2761 if (!mData->pMachineConfigFile->fileExists())
2762 // this is a new machine, and no config file exists yet:
2763 *aSettingsModified = TRUE;
2764 else
2765 *aSettingsModified = (mData->flModifications != 0);
2766
2767 return S_OK;
2768}
2769
2770HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2771{
2772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2773
2774 *aSessionState = mData->mSession.mState;
2775
2776 return S_OK;
2777}
2778
2779HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2780{
2781 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2782
2783 aSessionName = mData->mSession.mName;
2784
2785 return S_OK;
2786}
2787
2788HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2789{
2790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2791
2792 *aSessionPID = mData->mSession.mPID;
2793
2794 return S_OK;
2795}
2796
2797HRESULT Machine::getState(MachineState_T *aState)
2798{
2799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2800
2801 *aState = mData->mMachineState;
2802 Assert(mData->mMachineState != MachineState_Null);
2803
2804 return S_OK;
2805}
2806
2807HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2808{
2809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2810
2811 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2812
2813 return S_OK;
2814}
2815
2816HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2817{
2818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2819
2820 aStateFilePath = mSSData->strStateFilePath;
2821
2822 return S_OK;
2823}
2824
2825HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2826{
2827 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2828
2829 i_getLogFolder(aLogFolder);
2830
2831 return S_OK;
2832}
2833
2834HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2835{
2836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2837
2838 aCurrentSnapshot = mData->mCurrentSnapshot;
2839
2840 return S_OK;
2841}
2842
2843HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2844{
2845 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2846
2847 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2848 ? 0
2849 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2850
2851 return S_OK;
2852}
2853
2854HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2855{
2856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2857
2858 /* Note: for machines with no snapshots, we always return FALSE
2859 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2860 * reasons :) */
2861
2862 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2863 ? FALSE
2864 : mData->mCurrentStateModified;
2865
2866 return S_OK;
2867}
2868
2869HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2870{
2871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2872
2873 aSharedFolders.resize(mHWData->mSharedFolders.size());
2874 size_t i = 0;
2875 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2876 it = mHWData->mSharedFolders.begin();
2877 it != mHWData->mSharedFolders.end();
2878 ++it, ++i)
2879 aSharedFolders[i] = *it;
2880
2881 return S_OK;
2882}
2883
2884HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2885{
2886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2887
2888 *aClipboardMode = mHWData->mClipboardMode;
2889
2890 return S_OK;
2891}
2892
2893HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2894{
2895 HRESULT hrc = S_OK;
2896
2897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2898
2899 hrc = i_checkStateDependency(MutableOrRunningStateDep);
2900 if (FAILED(hrc)) return hrc;
2901
2902 alock.release();
2903 hrc = i_onClipboardModeChange(aClipboardMode);
2904 alock.acquire();
2905 if (FAILED(hrc)) return hrc;
2906
2907 i_setModified(IsModified_MachineData);
2908 mHWData.backup();
2909 mHWData->mClipboardMode = aClipboardMode;
2910
2911 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2912 if (Global::IsOnline(mData->mMachineState))
2913 i_saveSettings(NULL, alock);
2914
2915 return S_OK;
2916}
2917
2918HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2919{
2920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2921
2922 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2923
2924 return S_OK;
2925}
2926
2927HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2928{
2929 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2930
2931 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
2932 if (FAILED(hrc)) return hrc;
2933
2934 alock.release();
2935 hrc = i_onClipboardFileTransferModeChange(aEnabled);
2936 alock.acquire();
2937 if (FAILED(hrc)) return hrc;
2938
2939 i_setModified(IsModified_MachineData);
2940 mHWData.backup();
2941 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2942
2943 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2944 if (Global::IsOnline(mData->mMachineState))
2945 i_saveSettings(NULL, alock);
2946
2947 return S_OK;
2948}
2949
2950HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2951{
2952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2953
2954 *aDnDMode = mHWData->mDnDMode;
2955
2956 return S_OK;
2957}
2958
2959HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2960{
2961 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2962
2963 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
2964 if (FAILED(hrc)) return hrc;
2965
2966 alock.release();
2967 hrc = i_onDnDModeChange(aDnDMode);
2968
2969 alock.acquire();
2970 if (FAILED(hrc)) return hrc;
2971
2972 i_setModified(IsModified_MachineData);
2973 mHWData.backup();
2974 mHWData->mDnDMode = aDnDMode;
2975
2976 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2977 if (Global::IsOnline(mData->mMachineState))
2978 i_saveSettings(NULL, alock);
2979
2980 return S_OK;
2981}
2982
2983HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2984{
2985 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2986
2987 aStorageControllers.resize(mStorageControllers->size());
2988 size_t i = 0;
2989 for (StorageControllerList::const_iterator
2990 it = mStorageControllers->begin();
2991 it != mStorageControllers->end();
2992 ++it, ++i)
2993 aStorageControllers[i] = *it;
2994
2995 return S_OK;
2996}
2997
2998HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2999{
3000 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3001
3002 *aEnabled = mUserData->s.fTeleporterEnabled;
3003
3004 return S_OK;
3005}
3006
3007HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
3008{
3009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3010
3011 /* Only allow it to be set to true when PoweredOff or Aborted.
3012 (Clearing it is always permitted.) */
3013 if ( aTeleporterEnabled
3014 && mData->mRegistered
3015 && ( !i_isSessionMachine()
3016 || ( mData->mMachineState != MachineState_PoweredOff
3017 && mData->mMachineState != MachineState_Teleported
3018 && mData->mMachineState != MachineState_Aborted
3019 )
3020 )
3021 )
3022 return setError(VBOX_E_INVALID_VM_STATE,
3023 tr("The machine is not powered off (state is %s)"),
3024 Global::stringifyMachineState(mData->mMachineState));
3025
3026 i_setModified(IsModified_MachineData);
3027 mUserData.backup();
3028 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
3029
3030 return S_OK;
3031}
3032
3033HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
3034{
3035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3036
3037 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
3038
3039 return S_OK;
3040}
3041
3042HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
3043{
3044 if (aTeleporterPort >= _64K)
3045 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
3046
3047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3048
3049 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3050 if (FAILED(hrc)) return hrc;
3051
3052 i_setModified(IsModified_MachineData);
3053 mUserData.backup();
3054 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3055
3056 return S_OK;
3057}
3058
3059HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3060{
3061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3062
3063 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3064
3065 return S_OK;
3066}
3067
3068HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3069{
3070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3071
3072 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3073 if (FAILED(hrc)) return hrc;
3074
3075 i_setModified(IsModified_MachineData);
3076 mUserData.backup();
3077 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3078
3079 return S_OK;
3080}
3081
3082HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3083{
3084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3085 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3086
3087 return S_OK;
3088}
3089
3090HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3091{
3092 /*
3093 * Hash the password first.
3094 */
3095 com::Utf8Str aT = aTeleporterPassword;
3096
3097 if (!aT.isEmpty())
3098 {
3099 if (VBoxIsPasswordHashed(&aT))
3100 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3101 VBoxHashPassword(&aT);
3102 }
3103
3104 /*
3105 * Do the update.
3106 */
3107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3108 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3109 if (SUCCEEDED(hrc))
3110 {
3111 i_setModified(IsModified_MachineData);
3112 mUserData.backup();
3113 mUserData->s.strTeleporterPassword = aT;
3114 }
3115
3116 return hrc;
3117}
3118
3119HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3120{
3121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3122
3123 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3124
3125 return S_OK;
3126}
3127
3128HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3129{
3130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3131
3132 /* Only allow it to be set to true when PoweredOff or Aborted.
3133 (Clearing it is always permitted.) */
3134 if ( aRTCUseUTC
3135 && mData->mRegistered
3136 && ( !i_isSessionMachine()
3137 || ( mData->mMachineState != MachineState_PoweredOff
3138 && mData->mMachineState != MachineState_Teleported
3139 && mData->mMachineState != MachineState_Aborted
3140 )
3141 )
3142 )
3143 return setError(VBOX_E_INVALID_VM_STATE,
3144 tr("The machine is not powered off (state is %s)"),
3145 Global::stringifyMachineState(mData->mMachineState));
3146
3147 i_setModified(IsModified_MachineData);
3148 mUserData.backup();
3149 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3150
3151 return S_OK;
3152}
3153
3154HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3155{
3156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3157
3158 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3159
3160 return S_OK;
3161}
3162
3163HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3164{
3165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3166
3167 HRESULT hrc = i_checkStateDependency(MutableStateDep);
3168 if (FAILED(hrc)) return hrc;
3169
3170 i_setModified(IsModified_MachineData);
3171 mHWData.backup();
3172 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3173
3174 return S_OK;
3175}
3176
3177HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3178{
3179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3180
3181 *aIOCacheSize = mHWData->mIOCacheSize;
3182
3183 return S_OK;
3184}
3185
3186HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3187{
3188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3189
3190 HRESULT hrc = i_checkStateDependency(MutableStateDep);
3191 if (FAILED(hrc)) return hrc;
3192
3193 i_setModified(IsModified_MachineData);
3194 mHWData.backup();
3195 mHWData->mIOCacheSize = aIOCacheSize;
3196
3197 return S_OK;
3198}
3199
3200HRESULT Machine::getStateKeyId(com::Utf8Str &aKeyId)
3201{
3202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3203
3204#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3205 aKeyId = mSSData->strStateKeyId;
3206#else
3207 aKeyId = com::Utf8Str::Empty;
3208#endif
3209
3210 return S_OK;
3211}
3212
3213HRESULT Machine::getStateKeyStore(com::Utf8Str &aKeyStore)
3214{
3215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3216
3217#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3218 aKeyStore = mSSData->strStateKeyStore;
3219#else
3220 aKeyStore = com::Utf8Str::Empty;
3221#endif
3222
3223 return S_OK;
3224}
3225
3226HRESULT Machine::getLogKeyId(com::Utf8Str &aKeyId)
3227{
3228 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3229
3230#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3231 aKeyId = mData->mstrLogKeyId;
3232#else
3233 aKeyId = com::Utf8Str::Empty;
3234#endif
3235
3236 return S_OK;
3237}
3238
3239HRESULT Machine::getLogKeyStore(com::Utf8Str &aKeyStore)
3240{
3241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3242
3243#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3244 aKeyStore = mData->mstrLogKeyStore;
3245#else
3246 aKeyStore = com::Utf8Str::Empty;
3247#endif
3248
3249 return S_OK;
3250}
3251
3252HRESULT Machine::getGuestDebugControl(ComPtr<IGuestDebugControl> &aGuestDebugControl)
3253{
3254 mGuestDebugControl.queryInterfaceTo(aGuestDebugControl.asOutParam());
3255
3256 return S_OK;
3257}
3258
3259
3260/**
3261 * @note Locks objects!
3262 */
3263HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3264 LockType_T aLockType)
3265{
3266 /* check the session state */
3267 SessionState_T state;
3268 HRESULT hrc = aSession->COMGETTER(State)(&state);
3269 if (FAILED(hrc)) return hrc;
3270
3271 if (state != SessionState_Unlocked)
3272 return setError(VBOX_E_INVALID_OBJECT_STATE,
3273 tr("The given session is busy"));
3274
3275 // get the client's IInternalSessionControl interface
3276 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3277 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
3278 E_INVALIDARG);
3279
3280 // session name (only used in some code paths)
3281 Utf8Str strSessionName;
3282
3283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3284
3285 if (!mData->mRegistered)
3286 return setError(E_UNEXPECTED,
3287 tr("The machine '%s' is not registered"),
3288 mUserData->s.strName.c_str());
3289
3290 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3291
3292 SessionState_T oldState = mData->mSession.mState;
3293 /* Hack: in case the session is closing and there is a progress object
3294 * which allows waiting for the session to be closed, take the opportunity
3295 * and do a limited wait (max. 1 second). This helps a lot when the system
3296 * is busy and thus session closing can take a little while. */
3297 if ( mData->mSession.mState == SessionState_Unlocking
3298 && mData->mSession.mProgress)
3299 {
3300 alock.release();
3301 mData->mSession.mProgress->WaitForCompletion(1000);
3302 alock.acquire();
3303 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3304 }
3305
3306 // try again now
3307 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3308 // (i.e. session machine exists)
3309 && (aLockType == LockType_Shared) // caller wants a shared link to the
3310 // existing session that holds the write lock:
3311 )
3312 {
3313 // OK, share the session... we are now dealing with three processes:
3314 // 1) VBoxSVC (where this code runs);
3315 // 2) process C: the caller's client process (who wants a shared session);
3316 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3317
3318 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3319 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3320 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3321 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3322 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3323
3324 /*
3325 * Release the lock before calling the client process. It's safe here
3326 * since the only thing to do after we get the lock again is to add
3327 * the remote control to the list (which doesn't directly influence
3328 * anything).
3329 */
3330 alock.release();
3331
3332 // get the console of the session holding the write lock (this is a remote call)
3333 ComPtr<IConsole> pConsoleW;
3334 if (mData->mSession.mLockType == LockType_VM)
3335 {
3336 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3337 hrc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3338 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", hrc));
3339 if (FAILED(hrc))
3340 // the failure may occur w/o any error info (from RPC), so provide one
3341 return setError(VBOX_E_VM_ERROR, tr("Failed to get a console object from the direct session (%Rhrc)"), hrc);
3342 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3343 }
3344
3345 // share the session machine and W's console with the caller's session
3346 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3347 hrc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3348 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", hrc));
3349
3350 if (FAILED(hrc))
3351 // the failure may occur w/o any error info (from RPC), so provide one
3352 return setError(VBOX_E_VM_ERROR, tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
3353 alock.acquire();
3354
3355 // need to revalidate the state after acquiring the lock again
3356 if (mData->mSession.mState != SessionState_Locked)
3357 {
3358 pSessionControl->Uninitialize();
3359 return setError(VBOX_E_INVALID_SESSION_STATE,
3360 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3361 mUserData->s.strName.c_str());
3362 }
3363
3364 // add the caller's session to the list
3365 mData->mSession.mRemoteControls.push_back(pSessionControl);
3366 }
3367 else if ( mData->mSession.mState == SessionState_Locked
3368 || mData->mSession.mState == SessionState_Unlocking
3369 )
3370 {
3371 // sharing not permitted, or machine still unlocking:
3372 return setError(VBOX_E_INVALID_OBJECT_STATE,
3373 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3374 mUserData->s.strName.c_str());
3375 }
3376 else
3377 {
3378 // machine is not locked: then write-lock the machine (create the session machine)
3379
3380 // must not be busy
3381 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3382
3383 // get the caller's session PID
3384 RTPROCESS pid = NIL_RTPROCESS;
3385 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3386 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3387 Assert(pid != NIL_RTPROCESS);
3388
3389 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3390
3391 if (fLaunchingVMProcess)
3392 {
3393 if (mData->mSession.mPID == NIL_RTPROCESS)
3394 {
3395 // two or more clients racing for a lock, the one which set the
3396 // session state to Spawning will win, the others will get an
3397 // error as we can't decide here if waiting a little would help
3398 // (only for shared locks this would avoid an error)
3399 return setError(VBOX_E_INVALID_OBJECT_STATE,
3400 tr("The machine '%s' already has a lock request pending"),
3401 mUserData->s.strName.c_str());
3402 }
3403
3404 // this machine is awaiting for a spawning session to be opened:
3405 // then the calling process must be the one that got started by
3406 // LaunchVMProcess()
3407
3408 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3409 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3410
3411#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3412 /* Hardened windows builds spawns three processes when a VM is
3413 launched, the 3rd one is the one that will end up here. */
3414 RTPROCESS pidParent;
3415 int vrc = RTProcQueryParent(pid, &pidParent);
3416 if (RT_SUCCESS(vrc))
3417 vrc = RTProcQueryParent(pidParent, &pidParent);
3418 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3419 || vrc == VERR_ACCESS_DENIED)
3420 {
3421 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3422 mData->mSession.mPID = pid;
3423 }
3424#endif
3425
3426 if (mData->mSession.mPID != pid)
3427 return setError(E_ACCESSDENIED,
3428 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3429 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3430 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3431 }
3432
3433 // create the mutable SessionMachine from the current machine
3434 ComObjPtr<SessionMachine> sessionMachine;
3435 sessionMachine.createObject();
3436 hrc = sessionMachine->init(this);
3437 AssertComRC(hrc);
3438
3439 /* NOTE: doing return from this function after this point but
3440 * before the end is forbidden since it may call SessionMachine::uninit()
3441 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3442 * lock while still holding the Machine lock in alock so that a deadlock
3443 * is possible due to the wrong lock order. */
3444
3445 if (SUCCEEDED(hrc))
3446 {
3447 /*
3448 * Set the session state to Spawning to protect against subsequent
3449 * attempts to open a session and to unregister the machine after
3450 * we release the lock.
3451 */
3452 SessionState_T origState = mData->mSession.mState;
3453 mData->mSession.mState = SessionState_Spawning;
3454
3455#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3456 /* Get the client token ID to be passed to the client process */
3457 Utf8Str strTokenId;
3458 sessionMachine->i_getTokenId(strTokenId);
3459 Assert(!strTokenId.isEmpty());
3460#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3461 /* Get the client token to be passed to the client process */
3462 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3463 /* The token is now "owned" by pToken, fix refcount */
3464 if (!pToken.isNull())
3465 pToken->Release();
3466#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3467
3468 /*
3469 * Release the lock before calling the client process -- it will call
3470 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3471 * because the state is Spawning, so that LaunchVMProcess() and
3472 * LockMachine() calls will fail. This method, called before we
3473 * acquire the lock again, will fail because of the wrong PID.
3474 *
3475 * Note that mData->mSession.mRemoteControls accessed outside
3476 * the lock may not be modified when state is Spawning, so it's safe.
3477 */
3478 alock.release();
3479
3480 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3481#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3482 hrc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3483#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3484 hrc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3485 /* Now the token is owned by the client process. */
3486 pToken.setNull();
3487#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3488 LogFlowThisFunc(("AssignMachine() returned %08X\n", hrc));
3489
3490 /* The failure may occur w/o any error info (from RPC), so provide one */
3491 if (FAILED(hrc))
3492 setError(VBOX_E_VM_ERROR, tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
3493
3494 // get session name, either to remember or to compare against
3495 // the already known session name.
3496 {
3497 Bstr bstrSessionName;
3498 HRESULT hrc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3499 if (SUCCEEDED(hrc2))
3500 strSessionName = bstrSessionName;
3501 }
3502
3503 if ( SUCCEEDED(hrc)
3504 && fLaunchingVMProcess
3505 )
3506 {
3507 /* complete the remote session initialization */
3508
3509 /* get the console from the direct session */
3510 ComPtr<IConsole> console;
3511 hrc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3512 ComAssertComRC(hrc);
3513
3514 if (SUCCEEDED(hrc) && !console)
3515 {
3516 ComAssert(!!console);
3517 hrc = E_FAIL;
3518 }
3519
3520 /* assign machine & console to the remote session */
3521 if (SUCCEEDED(hrc))
3522 {
3523 /*
3524 * after LaunchVMProcess(), the first and the only
3525 * entry in remoteControls is that remote session
3526 */
3527 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3528 hrc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3529 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", hrc));
3530
3531 /* The failure may occur w/o any error info (from RPC), so provide one */
3532 if (FAILED(hrc))
3533 setError(VBOX_E_VM_ERROR,
3534 tr("Failed to assign the machine to the remote session (%Rhrc)"), hrc);
3535 }
3536
3537 if (FAILED(hrc))
3538 pSessionControl->Uninitialize();
3539 }
3540
3541 /* acquire the lock again */
3542 alock.acquire();
3543
3544 /* Restore the session state */
3545 mData->mSession.mState = origState;
3546 }
3547
3548 // finalize spawning anyway (this is why we don't return on errors above)
3549 if (fLaunchingVMProcess)
3550 {
3551 Assert(mData->mSession.mName == strSessionName || FAILED(hrc));
3552 /* Note that the progress object is finalized later */
3553 /** @todo Consider checking mData->mSession.mProgress for cancellation
3554 * around here. */
3555
3556 /* We don't reset mSession.mPID here because it is necessary for
3557 * SessionMachine::uninit() to reap the child process later. */
3558
3559 if (FAILED(hrc))
3560 {
3561 /* Close the remote session, remove the remote control from the list
3562 * and reset session state to Closed (@note keep the code in sync
3563 * with the relevant part in checkForSpawnFailure()). */
3564
3565 Assert(mData->mSession.mRemoteControls.size() == 1);
3566 if (mData->mSession.mRemoteControls.size() == 1)
3567 {
3568 ErrorInfoKeeper eik;
3569 mData->mSession.mRemoteControls.front()->Uninitialize();
3570 }
3571
3572 mData->mSession.mRemoteControls.clear();
3573 mData->mSession.mState = SessionState_Unlocked;
3574 }
3575 }
3576 else
3577 {
3578 /* memorize PID of the directly opened session */
3579 if (SUCCEEDED(hrc))
3580 mData->mSession.mPID = pid;
3581 }
3582
3583 if (SUCCEEDED(hrc))
3584 {
3585 mData->mSession.mLockType = aLockType;
3586 /* memorize the direct session control and cache IUnknown for it */
3587 mData->mSession.mDirectControl = pSessionControl;
3588 mData->mSession.mState = SessionState_Locked;
3589 if (!fLaunchingVMProcess)
3590 mData->mSession.mName = strSessionName;
3591 /* associate the SessionMachine with this Machine */
3592 mData->mSession.mMachine = sessionMachine;
3593
3594 /* request an IUnknown pointer early from the remote party for later
3595 * identity checks (it will be internally cached within mDirectControl
3596 * at least on XPCOM) */
3597 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3598 NOREF(unk);
3599
3600#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3601 if (aLockType == LockType_VM)
3602 {
3603 /* get the console from the direct session */
3604 ComPtr<IConsole> console;
3605 hrc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3606 ComAssertComRC(hrc);
3607 /* send passswords to console */
3608 for (SecretKeyStore::SecretKeyMap::iterator it = mData->mpKeyStore->begin();
3609 it != mData->mpKeyStore->end();
3610 ++it)
3611 {
3612 SecretKey *pKey = it->second;
3613 pKey->retain();
3614 console->AddEncryptionPassword(Bstr(it->first).raw(),
3615 Bstr((const char*)pKey->getKeyBuffer()).raw(),
3616 TRUE);
3617 pKey->release();
3618 }
3619
3620 }
3621#endif
3622 }
3623
3624 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3625 * would break the lock order */
3626 alock.release();
3627
3628 /* uninitialize the created session machine on failure */
3629 if (FAILED(hrc))
3630 sessionMachine->uninit();
3631 }
3632
3633 if (SUCCEEDED(hrc))
3634 {
3635 /*
3636 * tell the client watcher thread to update the set of
3637 * machines that have open sessions
3638 */
3639 mParent->i_updateClientWatcher();
3640
3641 if (oldState != SessionState_Locked)
3642 /* fire an event */
3643 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3644 }
3645
3646 return hrc;
3647}
3648
3649/**
3650 * @note Locks objects!
3651 */
3652HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3653 const com::Utf8Str &aName,
3654 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3655 ComPtr<IProgress> &aProgress)
3656{
3657 Utf8Str strFrontend(aName);
3658 /* "emergencystop" doesn't need the session, so skip the checks/interface
3659 * retrieval. This code doesn't quite fit in here, but introducing a
3660 * special API method would be even more effort, and would require explicit
3661 * support by every API client. It's better to hide the feature a bit. */
3662 if (strFrontend != "emergencystop")
3663 CheckComArgNotNull(aSession);
3664
3665 HRESULT hrc = S_OK;
3666 if (strFrontend.isEmpty())
3667 {
3668 Bstr bstrFrontend;
3669 hrc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3670 if (FAILED(hrc))
3671 return hrc;
3672 strFrontend = bstrFrontend;
3673 if (strFrontend.isEmpty())
3674 {
3675 ComPtr<ISystemProperties> systemProperties;
3676 hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3677 if (FAILED(hrc))
3678 return hrc;
3679 hrc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3680 if (FAILED(hrc))
3681 return hrc;
3682 strFrontend = bstrFrontend;
3683 }
3684 /* paranoia - emergencystop is not a valid default */
3685 if (strFrontend == "emergencystop")
3686 strFrontend = Utf8Str::Empty;
3687 }
3688 /* default frontend: Qt GUI */
3689 if (strFrontend.isEmpty())
3690 strFrontend = "GUI/Qt";
3691
3692 if (strFrontend != "emergencystop")
3693 {
3694 /* check the session state */
3695 SessionState_T state;
3696 hrc = aSession->COMGETTER(State)(&state);
3697 if (FAILED(hrc))
3698 return hrc;
3699
3700 if (state != SessionState_Unlocked)
3701 return setError(VBOX_E_INVALID_OBJECT_STATE,
3702 tr("The given session is busy"));
3703
3704 /* get the IInternalSessionControl interface */
3705 ComPtr<IInternalSessionControl> control(aSession);
3706 ComAssertMsgRet(!control.isNull(),
3707 ("No IInternalSessionControl interface"),
3708 E_INVALIDARG);
3709
3710 /* get the teleporter enable state for the progress object init. */
3711 BOOL fTeleporterEnabled;
3712 hrc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3713 if (FAILED(hrc))
3714 return hrc;
3715
3716 /* create a progress object */
3717 ComObjPtr<ProgressProxy> progress;
3718 progress.createObject();
3719 hrc = progress->init(mParent,
3720 static_cast<IMachine*>(this),
3721 Bstr(tr("Starting VM")).raw(),
3722 TRUE /* aCancelable */,
3723 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3724 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3725 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3726 2 /* uFirstOperationWeight */,
3727 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3728 if (SUCCEEDED(hrc))
3729 {
3730 hrc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3731 if (SUCCEEDED(hrc))
3732 {
3733 aProgress = progress;
3734
3735 /* signal the client watcher thread */
3736 mParent->i_updateClientWatcher();
3737
3738 /* fire an event */
3739 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3740 }
3741 }
3742 }
3743 else
3744 {
3745 /* no progress object - either instant success or failure */
3746 aProgress = NULL;
3747
3748 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3749
3750 if (mData->mSession.mState != SessionState_Locked)
3751 return setError(VBOX_E_INVALID_OBJECT_STATE,
3752 tr("The machine '%s' is not locked by a session"),
3753 mUserData->s.strName.c_str());
3754
3755 /* must have a VM process associated - do not kill normal API clients
3756 * with an open session */
3757 if (!Global::IsOnline(mData->mMachineState))
3758 return setError(VBOX_E_INVALID_OBJECT_STATE,
3759 tr("The machine '%s' does not have a VM process"),
3760 mUserData->s.strName.c_str());
3761
3762 /* forcibly terminate the VM process */
3763 if (mData->mSession.mPID != NIL_RTPROCESS)
3764 RTProcTerminate(mData->mSession.mPID);
3765
3766 /* signal the client watcher thread, as most likely the client has
3767 * been terminated */
3768 mParent->i_updateClientWatcher();
3769 }
3770
3771 return hrc;
3772}
3773
3774HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3775{
3776 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3777 return setError(E_INVALIDARG,
3778 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3779 aPosition, SchemaDefs::MaxBootPosition);
3780
3781 if (aDevice == DeviceType_USB)
3782 return setError(E_NOTIMPL,
3783 tr("Booting from USB device is currently not supported"));
3784
3785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3786
3787 HRESULT hrc = i_checkStateDependency(MutableStateDep);
3788 if (FAILED(hrc)) return hrc;
3789
3790 i_setModified(IsModified_MachineData);
3791 mHWData.backup();
3792 mHWData->mBootOrder[aPosition - 1] = aDevice;
3793
3794 return S_OK;
3795}
3796
3797HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3798{
3799 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3800 return setError(E_INVALIDARG,
3801 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3802 aPosition, SchemaDefs::MaxBootPosition);
3803
3804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3805
3806 *aDevice = mHWData->mBootOrder[aPosition - 1];
3807
3808 return S_OK;
3809}
3810
3811HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3812 LONG aControllerPort,
3813 LONG aDevice,
3814 DeviceType_T aType,
3815 const ComPtr<IMedium> &aMedium)
3816{
3817 IMedium *aM = aMedium;
3818 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3819 aName.c_str(), aControllerPort, aDevice, aType, aM));
3820
3821 // request the host lock first, since might be calling Host methods for getting host drives;
3822 // next, protect the media tree all the while we're in here, as well as our member variables
3823 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3824 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3825
3826 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
3827 if (FAILED(hrc)) return hrc;
3828
3829 /// @todo NEWMEDIA implicit machine registration
3830 if (!mData->mRegistered)
3831 return setError(VBOX_E_INVALID_OBJECT_STATE,
3832 tr("Cannot attach storage devices to an unregistered machine"));
3833
3834 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3835
3836 /* Check for an existing controller. */
3837 ComObjPtr<StorageController> ctl;
3838 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3839 if (FAILED(hrc)) return hrc;
3840
3841 StorageControllerType_T ctrlType;
3842 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
3843 if (FAILED(hrc))
3844 return setError(E_FAIL, tr("Could not get type of controller '%s'"), aName.c_str());
3845
3846 bool fSilent = false;
3847
3848 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3849 Utf8Str const strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3850 if ( mData->mMachineState == MachineState_Paused
3851 && strReconfig == "1")
3852 fSilent = true;
3853
3854 /* Check that the controller can do hot-plugging if we attach the device while the VM is running. */
3855 bool fHotplug = false;
3856 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3857 fHotplug = true;
3858
3859 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3860 return setError(VBOX_E_INVALID_VM_STATE,
3861 tr("Controller '%s' does not support hot-plugging"),
3862 aName.c_str());
3863
3864 // check that the port and device are not out of range
3865 hrc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3866 if (FAILED(hrc)) return hrc;
3867
3868 /* check if the device slot is already busy */
3869 MediumAttachment *pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3870 aName,
3871 aControllerPort,
3872 aDevice);
3873 if (pAttachTemp)
3874 {
3875 Medium *pMedium = pAttachTemp->i_getMedium();
3876 if (pMedium)
3877 {
3878 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3879 return setError(VBOX_E_OBJECT_IN_USE,
3880 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3881 pMedium->i_getLocationFull().c_str(),
3882 aControllerPort,
3883 aDevice,
3884 aName.c_str());
3885 }
3886 else
3887 return setError(VBOX_E_OBJECT_IN_USE,
3888 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3889 aControllerPort, aDevice, aName.c_str());
3890 }
3891
3892 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3893 if (aMedium && medium.isNull())
3894 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3895
3896 AutoCaller mediumCaller(medium);
3897 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
3898
3899 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3900
3901 pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium);
3902 if ( pAttachTemp
3903 && !medium.isNull()
3904 && ( medium->i_getType() != MediumType_Readonly
3905 || medium->i_getDeviceType() != DeviceType_DVD)
3906 )
3907 return setError(VBOX_E_OBJECT_IN_USE,
3908 tr("Medium '%s' is already attached to this virtual machine"),
3909 medium->i_getLocationFull().c_str());
3910
3911 if (!medium.isNull())
3912 {
3913 MediumType_T mtype = medium->i_getType();
3914 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3915 // For DVDs it's not written to the config file, so needs no global config
3916 // version bump. For floppies it's a new attribute "type", which is ignored
3917 // by older VirtualBox version, so needs no global config version bump either.
3918 // For hard disks this type is not accepted.
3919 if (mtype == MediumType_MultiAttach)
3920 {
3921 // This type is new with VirtualBox 4.0 and therefore requires settings
3922 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3923 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3924 // two reasons: The medium type is a property of the media registry tree, which
3925 // can reside in the global config file (for pre-4.0 media); we would therefore
3926 // possibly need to bump the global config version. We don't want to do that though
3927 // because that might make downgrading to pre-4.0 impossible.
3928 // As a result, we can only use these two new types if the medium is NOT in the
3929 // global registry:
3930 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3931 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3932 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3933 )
3934 return setError(VBOX_E_INVALID_OBJECT_STATE,
3935 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3936 "to machines that were created with VirtualBox 4.0 or later"),
3937 medium->i_getLocationFull().c_str());
3938 }
3939 }
3940
3941 bool fIndirect = false;
3942 if (!medium.isNull())
3943 fIndirect = medium->i_isReadOnly();
3944 bool associate = true;
3945
3946 do
3947 {
3948 if ( aType == DeviceType_HardDisk
3949 && mMediumAttachments.isBackedUp())
3950 {
3951 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3952
3953 /* check if the medium was attached to the VM before we started
3954 * changing attachments in which case the attachment just needs to
3955 * be restored */
3956 pAttachTemp = i_findAttachment(oldAtts, medium);
3957 if (pAttachTemp)
3958 {
3959 AssertReturn(!fIndirect, E_FAIL);
3960
3961 /* see if it's the same bus/channel/device */
3962 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3963 {
3964 /* the simplest case: restore the whole attachment
3965 * and return, nothing else to do */
3966 mMediumAttachments->push_back(pAttachTemp);
3967
3968 /* Reattach the medium to the VM. */
3969 if (fHotplug || fSilent)
3970 {
3971 mediumLock.release();
3972 treeLock.release();
3973 alock.release();
3974
3975 MediumLockList *pMediumLockList(new MediumLockList());
3976
3977 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3978 medium /* pToLockWrite */,
3979 false /* fMediumLockWriteAll */,
3980 NULL,
3981 *pMediumLockList);
3982 alock.acquire();
3983 if (FAILED(hrc))
3984 delete pMediumLockList;
3985 else
3986 {
3987 Assert(mData->mSession.mLockedMedia.IsLocked());
3988 mData->mSession.mLockedMedia.Unlock();
3989 alock.release();
3990 hrc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3991 mData->mSession.mLockedMedia.Lock();
3992 alock.acquire();
3993 }
3994 alock.release();
3995
3996 if (SUCCEEDED(hrc))
3997 {
3998 hrc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3999 /* Remove lock list in case of error. */
4000 if (FAILED(hrc))
4001 {
4002 mData->mSession.mLockedMedia.Unlock();
4003 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4004 mData->mSession.mLockedMedia.Lock();
4005 }
4006 }
4007 }
4008
4009 return S_OK;
4010 }
4011
4012 /* bus/channel/device differ; we need a new attachment object,
4013 * but don't try to associate it again */
4014 associate = false;
4015 break;
4016 }
4017 }
4018
4019 /* go further only if the attachment is to be indirect */
4020 if (!fIndirect)
4021 break;
4022
4023 /* perform the so called smart attachment logic for indirect
4024 * attachments. Note that smart attachment is only applicable to base
4025 * hard disks. */
4026
4027 if (medium->i_getParent().isNull())
4028 {
4029 /* first, investigate the backup copy of the current hard disk
4030 * attachments to make it possible to re-attach existing diffs to
4031 * another device slot w/o losing their contents */
4032 if (mMediumAttachments.isBackedUp())
4033 {
4034 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4035
4036 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4037 uint32_t foundLevel = 0;
4038
4039 for (MediumAttachmentList::const_iterator
4040 it = oldAtts.begin();
4041 it != oldAtts.end();
4042 ++it)
4043 {
4044 uint32_t level = 0;
4045 MediumAttachment *pAttach = *it;
4046 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4047 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4048 if (pMedium.isNull())
4049 continue;
4050
4051 if (pMedium->i_getBase(&level) == medium)
4052 {
4053 /* skip the hard disk if its currently attached (we
4054 * cannot attach the same hard disk twice) */
4055 if (i_findAttachment(*mMediumAttachments.data(),
4056 pMedium))
4057 continue;
4058
4059 /* matched device, channel and bus (i.e. attached to the
4060 * same place) will win and immediately stop the search;
4061 * otherwise the attachment that has the youngest
4062 * descendant of medium will be used
4063 */
4064 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4065 {
4066 /* the simplest case: restore the whole attachment
4067 * and return, nothing else to do */
4068 mMediumAttachments->push_back(*it);
4069
4070 /* Reattach the medium to the VM. */
4071 if (fHotplug || fSilent)
4072 {
4073 mediumLock.release();
4074 treeLock.release();
4075 alock.release();
4076
4077 MediumLockList *pMediumLockList(new MediumLockList());
4078
4079 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4080 medium /* pToLockWrite */,
4081 false /* fMediumLockWriteAll */,
4082 NULL,
4083 *pMediumLockList);
4084 alock.acquire();
4085 if (FAILED(hrc))
4086 delete pMediumLockList;
4087 else
4088 {
4089 Assert(mData->mSession.mLockedMedia.IsLocked());
4090 mData->mSession.mLockedMedia.Unlock();
4091 alock.release();
4092 hrc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4093 mData->mSession.mLockedMedia.Lock();
4094 alock.acquire();
4095 }
4096 alock.release();
4097
4098 if (SUCCEEDED(hrc))
4099 {
4100 hrc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4101 /* Remove lock list in case of error. */
4102 if (FAILED(hrc))
4103 {
4104 mData->mSession.mLockedMedia.Unlock();
4105 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4106 mData->mSession.mLockedMedia.Lock();
4107 }
4108 }
4109 }
4110
4111 return S_OK;
4112 }
4113 else if ( foundIt == oldAtts.end()
4114 || level > foundLevel /* prefer younger */
4115 )
4116 {
4117 foundIt = it;
4118 foundLevel = level;
4119 }
4120 }
4121 }
4122
4123 if (foundIt != oldAtts.end())
4124 {
4125 /* use the previously attached hard disk */
4126 medium = (*foundIt)->i_getMedium();
4127 mediumCaller.attach(medium);
4128 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
4129 mediumLock.attach(medium);
4130 /* not implicit, doesn't require association with this VM */
4131 fIndirect = false;
4132 associate = false;
4133 /* go right to the MediumAttachment creation */
4134 break;
4135 }
4136 }
4137
4138 /* must give up the medium lock and medium tree lock as below we
4139 * go over snapshots, which needs a lock with higher lock order. */
4140 mediumLock.release();
4141 treeLock.release();
4142
4143 /* then, search through snapshots for the best diff in the given
4144 * hard disk's chain to base the new diff on */
4145
4146 ComObjPtr<Medium> base;
4147 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4148 while (snap)
4149 {
4150 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4151
4152 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4153
4154 MediumAttachment *pAttachFound = NULL;
4155 uint32_t foundLevel = 0;
4156
4157 for (MediumAttachmentList::const_iterator
4158 it = snapAtts.begin();
4159 it != snapAtts.end();
4160 ++it)
4161 {
4162 MediumAttachment *pAttach = *it;
4163 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4164 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4165 if (pMedium.isNull())
4166 continue;
4167
4168 uint32_t level = 0;
4169 if (pMedium->i_getBase(&level) == medium)
4170 {
4171 /* matched device, channel and bus (i.e. attached to the
4172 * same place) will win and immediately stop the search;
4173 * otherwise the attachment that has the youngest
4174 * descendant of medium will be used
4175 */
4176 if ( pAttach->i_getDevice() == aDevice
4177 && pAttach->i_getPort() == aControllerPort
4178 && pAttach->i_getControllerName() == aName
4179 )
4180 {
4181 pAttachFound = pAttach;
4182 break;
4183 }
4184 else if ( !pAttachFound
4185 || level > foundLevel /* prefer younger */
4186 )
4187 {
4188 pAttachFound = pAttach;
4189 foundLevel = level;
4190 }
4191 }
4192 }
4193
4194 if (pAttachFound)
4195 {
4196 base = pAttachFound->i_getMedium();
4197 break;
4198 }
4199
4200 snap = snap->i_getParent();
4201 }
4202
4203 /* re-lock medium tree and the medium, as we need it below */
4204 treeLock.acquire();
4205 mediumLock.acquire();
4206
4207 /* found a suitable diff, use it as a base */
4208 if (!base.isNull())
4209 {
4210 medium = base;
4211 mediumCaller.attach(medium);
4212 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
4213 mediumLock.attach(medium);
4214 }
4215 }
4216
4217 Utf8Str strFullSnapshotFolder;
4218 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4219
4220 ComObjPtr<Medium> diff;
4221 diff.createObject();
4222 // store this diff in the same registry as the parent
4223 Guid uuidRegistryParent;
4224 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4225 {
4226 // parent image has no registry: this can happen if we're attaching a new immutable
4227 // image that has not yet been attached (medium then points to the base and we're
4228 // creating the diff image for the immutable, and the parent is not yet registered);
4229 // put the parent in the machine registry then
4230 mediumLock.release();
4231 treeLock.release();
4232 alock.release();
4233 i_addMediumToRegistry(medium);
4234 alock.acquire();
4235 treeLock.acquire();
4236 mediumLock.acquire();
4237 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4238 }
4239 hrc = diff->init(mParent,
4240 medium->i_getPreferredDiffFormat(),
4241 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4242 uuidRegistryParent,
4243 DeviceType_HardDisk);
4244 if (FAILED(hrc)) return hrc;
4245
4246 /* Apply the normal locking logic to the entire chain. */
4247 MediumLockList *pMediumLockList(new MediumLockList());
4248 mediumLock.release();
4249 treeLock.release();
4250 hrc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4251 diff /* pToLockWrite */,
4252 false /* fMediumLockWriteAll */,
4253 medium,
4254 *pMediumLockList);
4255 treeLock.acquire();
4256 mediumLock.acquire();
4257 if (SUCCEEDED(hrc))
4258 {
4259 mediumLock.release();
4260 treeLock.release();
4261 hrc = pMediumLockList->Lock();
4262 treeLock.acquire();
4263 mediumLock.acquire();
4264 if (FAILED(hrc))
4265 setError(hrc,
4266 tr("Could not lock medium when creating diff '%s'"),
4267 diff->i_getLocationFull().c_str());
4268 else
4269 {
4270 /* will release the lock before the potentially lengthy
4271 * operation, so protect with the special state */
4272 MachineState_T oldState = mData->mMachineState;
4273 i_setMachineState(MachineState_SettingUp);
4274
4275 mediumLock.release();
4276 treeLock.release();
4277 alock.release();
4278
4279 hrc = medium->i_createDiffStorage(diff,
4280 medium->i_getPreferredDiffVariant(),
4281 pMediumLockList,
4282 NULL /* aProgress */,
4283 true /* aWait */,
4284 false /* aNotify */);
4285
4286 alock.acquire();
4287 treeLock.acquire();
4288 mediumLock.acquire();
4289
4290 i_setMachineState(oldState);
4291 }
4292 }
4293
4294 /* Unlock the media and free the associated memory. */
4295 delete pMediumLockList;
4296
4297 if (FAILED(hrc)) return hrc;
4298
4299 /* use the created diff for the actual attachment */
4300 medium = diff;
4301 mediumCaller.attach(medium);
4302 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
4303 mediumLock.attach(medium);
4304 }
4305 while (0);
4306
4307 ComObjPtr<MediumAttachment> attachment;
4308 attachment.createObject();
4309 hrc = attachment->init(this,
4310 medium,
4311 aName,
4312 aControllerPort,
4313 aDevice,
4314 aType,
4315 fIndirect,
4316 false /* fPassthrough */,
4317 false /* fTempEject */,
4318 false /* fNonRotational */,
4319 false /* fDiscard */,
4320 fHotplug || ctrlType == StorageControllerType_USB /* fHotPluggable */,
4321 Utf8Str::Empty);
4322 if (FAILED(hrc)) return hrc;
4323
4324 if (associate && !medium.isNull())
4325 {
4326 // as the last step, associate the medium to the VM
4327 hrc = medium->i_addBackReference(mData->mUuid);
4328 // here we can fail because of Deleting, or being in process of creating a Diff
4329 if (FAILED(hrc)) return hrc;
4330
4331 mediumLock.release();
4332 treeLock.release();
4333 alock.release();
4334 i_addMediumToRegistry(medium);
4335 alock.acquire();
4336 treeLock.acquire();
4337 mediumLock.acquire();
4338 }
4339
4340 /* success: finally remember the attachment */
4341 i_setModified(IsModified_Storage);
4342 mMediumAttachments.backup();
4343 mMediumAttachments->push_back(attachment);
4344
4345 mediumLock.release();
4346 treeLock.release();
4347 alock.release();
4348
4349 if (fHotplug || fSilent)
4350 {
4351 if (!medium.isNull())
4352 {
4353 MediumLockList *pMediumLockList(new MediumLockList());
4354
4355 hrc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4356 medium /* pToLockWrite */,
4357 false /* fMediumLockWriteAll */,
4358 NULL,
4359 *pMediumLockList);
4360 alock.acquire();
4361 if (FAILED(hrc))
4362 delete pMediumLockList;
4363 else
4364 {
4365 Assert(mData->mSession.mLockedMedia.IsLocked());
4366 mData->mSession.mLockedMedia.Unlock();
4367 alock.release();
4368 hrc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4369 mData->mSession.mLockedMedia.Lock();
4370 alock.acquire();
4371 }
4372 alock.release();
4373 }
4374
4375 if (SUCCEEDED(hrc))
4376 {
4377 hrc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4378 /* Remove lock list in case of error. */
4379 if (FAILED(hrc))
4380 {
4381 mData->mSession.mLockedMedia.Unlock();
4382 mData->mSession.mLockedMedia.Remove(attachment);
4383 mData->mSession.mLockedMedia.Lock();
4384 }
4385 }
4386 }
4387
4388 /* Save modified registries, but skip this machine as it's the caller's
4389 * job to save its settings like all other settings changes. */
4390 mParent->i_unmarkRegistryModified(i_getId());
4391 mParent->i_saveModifiedRegistries();
4392
4393 if (SUCCEEDED(hrc))
4394 {
4395 if (fIndirect && medium != aM)
4396 mParent->i_onMediumConfigChanged(medium);
4397 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4398 }
4399
4400 return hrc;
4401}
4402
4403HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4404 LONG aDevice)
4405{
4406 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n", aName.c_str(), aControllerPort, aDevice));
4407
4408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4409
4410 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4411 if (FAILED(hrc)) return hrc;
4412
4413 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4414
4415 /* Check for an existing controller. */
4416 ComObjPtr<StorageController> ctl;
4417 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4418 if (FAILED(hrc)) return hrc;
4419
4420 StorageControllerType_T ctrlType;
4421 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
4422 if (FAILED(hrc))
4423 return setError(E_FAIL, tr("Could not get type of controller '%s'"), aName.c_str());
4424
4425 bool fSilent = false;
4426 Utf8Str strReconfig;
4427
4428 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4429 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4430 if ( mData->mMachineState == MachineState_Paused
4431 && strReconfig == "1")
4432 fSilent = true;
4433
4434 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4435 bool fHotplug = false;
4436 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4437 fHotplug = true;
4438
4439 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4440 return setError(VBOX_E_INVALID_VM_STATE,
4441 tr("Controller '%s' does not support hot-plugging"),
4442 aName.c_str());
4443
4444 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4445 aName,
4446 aControllerPort,
4447 aDevice);
4448 if (!pAttach)
4449 return setError(VBOX_E_OBJECT_NOT_FOUND,
4450 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4451 aDevice, aControllerPort, aName.c_str());
4452
4453 if (fHotplug && !pAttach->i_getHotPluggable())
4454 return setError(VBOX_E_NOT_SUPPORTED,
4455 tr("The device slot %d on port %d of controller '%s' does not support hot-plugging"),
4456 aDevice, aControllerPort, aName.c_str());
4457
4458 /*
4459 * The VM has to detach the device before we delete any implicit diffs.
4460 * If this fails we can roll back without loosing data.
4461 */
4462 if (fHotplug || fSilent)
4463 {
4464 alock.release();
4465 hrc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4466 alock.acquire();
4467 }
4468 if (FAILED(hrc)) return hrc;
4469
4470 /* If we are here everything went well and we can delete the implicit now. */
4471 hrc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4472
4473 alock.release();
4474
4475 /* Save modified registries, but skip this machine as it's the caller's
4476 * job to save its settings like all other settings changes. */
4477 mParent->i_unmarkRegistryModified(i_getId());
4478 mParent->i_saveModifiedRegistries();
4479
4480 if (SUCCEEDED(hrc))
4481 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4482
4483 return hrc;
4484}
4485
4486HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4487 LONG aDevice, BOOL aPassthrough)
4488{
4489 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4490 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4491
4492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4493
4494 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4495 if (FAILED(hrc)) return hrc;
4496
4497 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4498
4499 /* Check for an existing controller. */
4500 ComObjPtr<StorageController> ctl;
4501 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4502 if (FAILED(hrc)) return hrc;
4503
4504 StorageControllerType_T ctrlType;
4505 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
4506 if (FAILED(hrc))
4507 return setError(E_FAIL,
4508 tr("Could not get type of controller '%s'"),
4509 aName.c_str());
4510
4511 bool fSilent = false;
4512 Utf8Str strReconfig;
4513
4514 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4515 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4516 if ( mData->mMachineState == MachineState_Paused
4517 && strReconfig == "1")
4518 fSilent = true;
4519
4520 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4521 bool fHotplug = false;
4522 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4523 fHotplug = true;
4524
4525 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4526 return setError(VBOX_E_INVALID_VM_STATE,
4527 tr("Controller '%s' does not support hot-plugging which is required to change the passthrough setting while the VM is running"),
4528 aName.c_str());
4529
4530 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4531 aName,
4532 aControllerPort,
4533 aDevice);
4534 if (!pAttach)
4535 return setError(VBOX_E_OBJECT_NOT_FOUND,
4536 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4537 aDevice, aControllerPort, aName.c_str());
4538
4539
4540 i_setModified(IsModified_Storage);
4541 mMediumAttachments.backup();
4542
4543 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4544
4545 if (pAttach->i_getType() != DeviceType_DVD)
4546 return setError(E_INVALIDARG,
4547 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4548 aDevice, aControllerPort, aName.c_str());
4549
4550 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4551
4552 pAttach->i_updatePassthrough(!!aPassthrough);
4553
4554 attLock.release();
4555 alock.release();
4556 hrc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4557 if (SUCCEEDED(hrc) && fValueChanged)
4558 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4559
4560 return hrc;
4561}
4562
4563HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4564 LONG aDevice, BOOL aTemporaryEject)
4565{
4566
4567 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4568 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4569
4570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4571
4572 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4573 if (FAILED(hrc)) return hrc;
4574
4575 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4576 aName,
4577 aControllerPort,
4578 aDevice);
4579 if (!pAttach)
4580 return setError(VBOX_E_OBJECT_NOT_FOUND,
4581 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4582 aDevice, aControllerPort, aName.c_str());
4583
4584
4585 i_setModified(IsModified_Storage);
4586 mMediumAttachments.backup();
4587
4588 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4589
4590 if (pAttach->i_getType() != DeviceType_DVD)
4591 return setError(E_INVALIDARG,
4592 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4593 aDevice, aControllerPort, aName.c_str());
4594 pAttach->i_updateTempEject(!!aTemporaryEject);
4595
4596 return S_OK;
4597}
4598
4599HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4600 LONG aDevice, BOOL aNonRotational)
4601{
4602
4603 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4604 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4605
4606 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4607
4608 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4609 if (FAILED(hrc)) return hrc;
4610
4611 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4612
4613 if (Global::IsOnlineOrTransient(mData->mMachineState))
4614 return setError(VBOX_E_INVALID_VM_STATE,
4615 tr("Invalid machine state: %s"),
4616 Global::stringifyMachineState(mData->mMachineState));
4617
4618 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4619 aName,
4620 aControllerPort,
4621 aDevice);
4622 if (!pAttach)
4623 return setError(VBOX_E_OBJECT_NOT_FOUND,
4624 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4625 aDevice, aControllerPort, aName.c_str());
4626
4627
4628 i_setModified(IsModified_Storage);
4629 mMediumAttachments.backup();
4630
4631 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4632
4633 if (pAttach->i_getType() != DeviceType_HardDisk)
4634 return setError(E_INVALIDARG,
4635 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4636 aDevice, aControllerPort, aName.c_str());
4637 pAttach->i_updateNonRotational(!!aNonRotational);
4638
4639 return S_OK;
4640}
4641
4642HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4643 LONG aDevice, BOOL aDiscard)
4644{
4645
4646 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4647 aName.c_str(), aControllerPort, aDevice, aDiscard));
4648
4649 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4650
4651 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4652 if (FAILED(hrc)) return hrc;
4653
4654 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4655
4656 if (Global::IsOnlineOrTransient(mData->mMachineState))
4657 return setError(VBOX_E_INVALID_VM_STATE,
4658 tr("Invalid machine state: %s"),
4659 Global::stringifyMachineState(mData->mMachineState));
4660
4661 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4662 aName,
4663 aControllerPort,
4664 aDevice);
4665 if (!pAttach)
4666 return setError(VBOX_E_OBJECT_NOT_FOUND,
4667 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4668 aDevice, aControllerPort, aName.c_str());
4669
4670
4671 i_setModified(IsModified_Storage);
4672 mMediumAttachments.backup();
4673
4674 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4675
4676 if (pAttach->i_getType() != DeviceType_HardDisk)
4677 return setError(E_INVALIDARG,
4678 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4679 aDevice, aControllerPort, aName.c_str());
4680 pAttach->i_updateDiscard(!!aDiscard);
4681
4682 return S_OK;
4683}
4684
4685HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4686 LONG aDevice, BOOL aHotPluggable)
4687{
4688 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4689 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4690
4691 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4692
4693 HRESULT hrc = i_checkStateDependency(MutableStateDep);
4694 if (FAILED(hrc)) return hrc;
4695
4696 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4697
4698 if (Global::IsOnlineOrTransient(mData->mMachineState))
4699 return setError(VBOX_E_INVALID_VM_STATE,
4700 tr("Invalid machine state: %s"),
4701 Global::stringifyMachineState(mData->mMachineState));
4702
4703 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4704 aName,
4705 aControllerPort,
4706 aDevice);
4707 if (!pAttach)
4708 return setError(VBOX_E_OBJECT_NOT_FOUND,
4709 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4710 aDevice, aControllerPort, aName.c_str());
4711
4712 /* Check for an existing controller. */
4713 ComObjPtr<StorageController> ctl;
4714 hrc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4715 if (FAILED(hrc)) return hrc;
4716
4717 StorageControllerType_T ctrlType;
4718 hrc = ctl->COMGETTER(ControllerType)(&ctrlType);
4719 if (FAILED(hrc))
4720 return setError(E_FAIL,
4721 tr("Could not get type of controller '%s'"),
4722 aName.c_str());
4723
4724 if (!i_isControllerHotplugCapable(ctrlType))
4725 return setError(VBOX_E_NOT_SUPPORTED,
4726 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4727 aName.c_str());
4728
4729 /* silently ignore attempts to modify the hot-plug status of USB devices */
4730 if (ctrlType == StorageControllerType_USB)
4731 return S_OK;
4732
4733 i_setModified(IsModified_Storage);
4734 mMediumAttachments.backup();
4735
4736 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4737
4738 if (pAttach->i_getType() == DeviceType_Floppy)
4739 return setError(E_INVALIDARG,
4740 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%s' is a floppy drive"),
4741 aDevice, aControllerPort, aName.c_str());
4742 pAttach->i_updateHotPluggable(!!aHotPluggable);
4743
4744 return S_OK;
4745}
4746
4747HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4748 LONG aDevice)
4749{
4750 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4751 aName.c_str(), aControllerPort, aDevice));
4752
4753 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4754}
4755
4756HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4757 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4758{
4759 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4760 aName.c_str(), aControllerPort, aDevice));
4761
4762 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4763
4764 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
4765 if (FAILED(hrc)) return hrc;
4766
4767 if (Global::IsOnlineOrTransient(mData->mMachineState))
4768 return setError(VBOX_E_INVALID_VM_STATE,
4769 tr("Invalid machine state: %s"),
4770 Global::stringifyMachineState(mData->mMachineState));
4771
4772 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4773 aName,
4774 aControllerPort,
4775 aDevice);
4776 if (!pAttach)
4777 return setError(VBOX_E_OBJECT_NOT_FOUND,
4778 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4779 aDevice, aControllerPort, aName.c_str());
4780
4781
4782 i_setModified(IsModified_Storage);
4783 mMediumAttachments.backup();
4784
4785 IBandwidthGroup *iB = aBandwidthGroup;
4786 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4787 if (aBandwidthGroup && group.isNull())
4788 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4789
4790 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4791
4792 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4793 if (strBandwidthGroupOld.isNotEmpty())
4794 {
4795 /* Get the bandwidth group object and release it - this must not fail. */
4796 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4797 hrc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4798 Assert(SUCCEEDED(hrc));
4799
4800 pBandwidthGroupOld->i_release();
4801 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4802 }
4803
4804 if (!group.isNull())
4805 {
4806 group->i_reference();
4807 pAttach->i_updateBandwidthGroup(group->i_getName());
4808 }
4809
4810 return S_OK;
4811}
4812
4813HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4814 LONG aControllerPort,
4815 LONG aDevice,
4816 DeviceType_T aType)
4817{
4818 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4819 aName.c_str(), aControllerPort, aDevice, aType));
4820
4821 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4822}
4823
4824
4825HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4826 LONG aControllerPort,
4827 LONG aDevice,
4828 BOOL aForce)
4829{
4830 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4831 aName.c_str(), aControllerPort, aForce));
4832
4833 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4834}
4835
4836HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4837 LONG aControllerPort,
4838 LONG aDevice,
4839 const ComPtr<IMedium> &aMedium,
4840 BOOL aForce)
4841{
4842 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4843 aName.c_str(), aControllerPort, aDevice, aForce));
4844
4845 // request the host lock first, since might be calling Host methods for getting host drives;
4846 // next, protect the media tree all the while we're in here, as well as our member variables
4847 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4848 this->lockHandle(),
4849 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4850
4851 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4852 if (FAILED(hrc)) return hrc;
4853
4854 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4855 aName,
4856 aControllerPort,
4857 aDevice);
4858 if (pAttach.isNull())
4859 return setError(VBOX_E_OBJECT_NOT_FOUND,
4860 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4861 aDevice, aControllerPort, aName.c_str());
4862
4863 /* Remember previously mounted medium. The medium before taking the
4864 * backup is not necessarily the same thing. */
4865 ComObjPtr<Medium> oldmedium;
4866 oldmedium = pAttach->i_getMedium();
4867
4868 IMedium *iM = aMedium;
4869 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4870 if (aMedium && pMedium.isNull())
4871 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4872
4873 /* Check if potential medium is already mounted */
4874 if (pMedium == oldmedium)
4875 return S_OK;
4876
4877 AutoCaller mediumCaller(pMedium);
4878 if (FAILED(mediumCaller.hrc())) return mediumCaller.hrc();
4879
4880 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4881 if (pMedium)
4882 {
4883 DeviceType_T mediumType = pAttach->i_getType();
4884 switch (mediumType)
4885 {
4886 case DeviceType_DVD:
4887 case DeviceType_Floppy:
4888 break;
4889
4890 default:
4891 return setError(VBOX_E_INVALID_OBJECT_STATE,
4892 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4893 aControllerPort,
4894 aDevice,
4895 aName.c_str());
4896 }
4897 }
4898
4899 i_setModified(IsModified_Storage);
4900 mMediumAttachments.backup();
4901
4902 {
4903 // The backup operation makes the pAttach reference point to the
4904 // old settings. Re-get the correct reference.
4905 pAttach = i_findAttachment(*mMediumAttachments.data(),
4906 aName,
4907 aControllerPort,
4908 aDevice);
4909 if (!oldmedium.isNull())
4910 oldmedium->i_removeBackReference(mData->mUuid);
4911 if (!pMedium.isNull())
4912 {
4913 pMedium->i_addBackReference(mData->mUuid);
4914
4915 mediumLock.release();
4916 multiLock.release();
4917 i_addMediumToRegistry(pMedium);
4918 multiLock.acquire();
4919 mediumLock.acquire();
4920 }
4921
4922 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4923 pAttach->i_updateMedium(pMedium);
4924 }
4925
4926 i_setModified(IsModified_Storage);
4927
4928 mediumLock.release();
4929 multiLock.release();
4930 hrc = i_onMediumChange(pAttach, aForce);
4931 multiLock.acquire();
4932 mediumLock.acquire();
4933
4934 /* On error roll back this change only. */
4935 if (FAILED(hrc))
4936 {
4937 if (!pMedium.isNull())
4938 pMedium->i_removeBackReference(mData->mUuid);
4939 pAttach = i_findAttachment(*mMediumAttachments.data(),
4940 aName,
4941 aControllerPort,
4942 aDevice);
4943 /* If the attachment is gone in the meantime, bail out. */
4944 if (pAttach.isNull())
4945 return hrc;
4946 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4947 if (!oldmedium.isNull())
4948 oldmedium->i_addBackReference(mData->mUuid);
4949 pAttach->i_updateMedium(oldmedium);
4950 }
4951
4952 mediumLock.release();
4953 multiLock.release();
4954
4955 /* Save modified registries, but skip this machine as it's the caller's
4956 * job to save its settings like all other settings changes. */
4957 mParent->i_unmarkRegistryModified(i_getId());
4958 mParent->i_saveModifiedRegistries();
4959
4960 return hrc;
4961}
4962HRESULT Machine::getMedium(const com::Utf8Str &aName,
4963 LONG aControllerPort,
4964 LONG aDevice,
4965 ComPtr<IMedium> &aMedium)
4966{
4967 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4968 aName.c_str(), aControllerPort, aDevice));
4969
4970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4971
4972 aMedium = NULL;
4973
4974 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4975 aName,
4976 aControllerPort,
4977 aDevice);
4978 if (pAttach.isNull())
4979 return setError(VBOX_E_OBJECT_NOT_FOUND,
4980 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4981 aDevice, aControllerPort, aName.c_str());
4982
4983 aMedium = pAttach->i_getMedium();
4984
4985 return S_OK;
4986}
4987
4988HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4989{
4990 if (aSlot < RT_ELEMENTS(mSerialPorts))
4991 {
4992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4993 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4994 return S_OK;
4995 }
4996 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4997}
4998
4999HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
5000{
5001 if (aSlot < RT_ELEMENTS(mParallelPorts))
5002 {
5003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5004 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5005 return S_OK;
5006 }
5007 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
5008}
5009
5010
5011HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
5012{
5013 /* Do not assert if slot is out of range, just return the advertised
5014 status. testdriver/vbox.py triggers this in logVmInfo. */
5015 if (aSlot >= mNetworkAdapters.size())
5016 return setError(E_INVALIDARG,
5017 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
5018 aSlot, mNetworkAdapters.size());
5019
5020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5021
5022 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
5023
5024 return S_OK;
5025}
5026
5027HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
5028{
5029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5030
5031 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
5032 size_t i = 0;
5033 for (settings::StringsMap::const_iterator
5034 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5035 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5036 ++it, ++i)
5037 aKeys[i] = it->first;
5038
5039 return S_OK;
5040}
5041
5042 /**
5043 * @note Locks this object for reading.
5044 */
5045HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
5046 com::Utf8Str &aValue)
5047{
5048 /* start with nothing found */
5049 aValue = "";
5050
5051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5052
5053 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5054 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5055 // found:
5056 aValue = it->second; // source is a Utf8Str
5057
5058 /* return the result to caller (may be empty) */
5059 return S_OK;
5060}
5061
5062 /**
5063 * @note Locks mParent for writing + this object for writing.
5064 */
5065HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5066{
5067 /* Because control characters in aKey have caused problems in the settings
5068 * they are rejected unless the key should be deleted. */
5069 if (!aValue.isEmpty())
5070 {
5071 for (size_t i = 0; i < aKey.length(); ++i)
5072 {
5073 char ch = aKey[i];
5074 if (RTLocCIsCntrl(ch))
5075 return E_INVALIDARG;
5076 }
5077 }
5078
5079 Utf8Str strOldValue; // empty
5080
5081 // locking note: we only hold the read lock briefly to look up the old value,
5082 // then release it and call the onExtraCanChange callbacks. There is a small
5083 // chance of a race insofar as the callback might be called twice if two callers
5084 // change the same key at the same time, but that's a much better solution
5085 // than the deadlock we had here before. The actual changing of the extradata
5086 // is then performed under the write lock and race-free.
5087
5088 // look up the old value first; if nothing has changed then we need not do anything
5089 {
5090 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5091
5092 // For snapshots don't even think about allowing changes, extradata
5093 // is global for a machine, so there is nothing snapshot specific.
5094 if (i_isSnapshotMachine())
5095 return setError(VBOX_E_INVALID_VM_STATE,
5096 tr("Cannot set extradata for a snapshot"));
5097
5098 // check if the right IMachine instance is used
5099 if (mData->mRegistered && !i_isSessionMachine())
5100 return setError(VBOX_E_INVALID_VM_STATE,
5101 tr("Cannot set extradata for an immutable machine"));
5102
5103 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5104 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5105 strOldValue = it->second;
5106 }
5107
5108 bool fChanged;
5109 if ((fChanged = (strOldValue != aValue)))
5110 {
5111 // ask for permission from all listeners outside the locks;
5112 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5113 // lock to copy the list of callbacks to invoke
5114 Bstr bstrError;
5115 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
5116 {
5117 const char *sep = bstrError.isEmpty() ? "" : ": ";
5118 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
5119 return setError(E_ACCESSDENIED,
5120 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5121 aKey.c_str(),
5122 aValue.c_str(),
5123 sep,
5124 bstrError.raw());
5125 }
5126
5127 // data is changing and change not vetoed: then write it out under the lock
5128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5129
5130 if (aValue.isEmpty())
5131 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5132 else
5133 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5134 // creates a new key if needed
5135
5136 bool fNeedsGlobalSaveSettings = false;
5137 // This saving of settings is tricky: there is no "old state" for the
5138 // extradata items at all (unlike all other settings), so the old/new
5139 // settings comparison would give a wrong result!
5140 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
5141
5142 if (fNeedsGlobalSaveSettings)
5143 {
5144 // save the global settings; for that we should hold only the VirtualBox lock
5145 alock.release();
5146 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5147 mParent->i_saveSettings();
5148 }
5149 }
5150
5151 // fire notification outside the lock
5152 if (fChanged)
5153 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
5154
5155 return S_OK;
5156}
5157
5158HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5159{
5160 aProgress = NULL;
5161 NOREF(aSettingsFilePath);
5162 ReturnComNotImplemented();
5163}
5164
5165HRESULT Machine::saveSettings()
5166{
5167 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5168
5169 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5170 if (FAILED(hrc)) return hrc;
5171
5172 /* the settings file path may never be null */
5173 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5174
5175 /* save all VM data excluding snapshots */
5176 bool fNeedsGlobalSaveSettings = false;
5177 hrc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
5178 mlock.release();
5179
5180 if (SUCCEEDED(hrc) && fNeedsGlobalSaveSettings)
5181 {
5182 // save the global settings; for that we should hold only the VirtualBox lock
5183 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5184 hrc = mParent->i_saveSettings();
5185 }
5186
5187 return hrc;
5188}
5189
5190
5191HRESULT Machine::discardSettings()
5192{
5193 /*
5194 * We need to take the machine list lock here as well as the machine one
5195 * or we'll get into trouble should any media stuff require rolling back.
5196 *
5197 * Details:
5198 *
5199 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5200 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5201 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other lock: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x]
5202 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: class=0000000000d5eb10 4-LISTOFMACHINES created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
5203 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5204 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5205 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5206 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: class=0000000000d5ecd0 5-MACHINEOBJECT created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
5207 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5208 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5209 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5210 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5211 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5212 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #00: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=2 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(11705) Machine::i_rollback 00007ff6853f6ce4} [x/r]
5213 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #01: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x] (*)
5214 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5215 * 0:005> k
5216 * # Child-SP RetAddr Call Site
5217 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5218 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5219 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5220 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5221 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5222 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5223 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5224 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5225 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5226 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5227 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5228 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5229 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5230 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5231 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5232 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5233 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5234 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5235 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5236 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5237 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5238 *
5239 */
5240 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5242
5243 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5244 if (FAILED(hrc)) return hrc;
5245
5246 /*
5247 * during this rollback, the session will be notified if data has
5248 * been actually changed
5249 */
5250 i_rollback(true /* aNotify */);
5251
5252 return S_OK;
5253}
5254
5255/** @note Locks objects! */
5256HRESULT Machine::unregister(AutoCaller &autoCaller,
5257 CleanupMode_T aCleanupMode,
5258 std::vector<ComPtr<IMedium> > &aMedia)
5259{
5260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5261
5262 Guid id(i_getId());
5263
5264 if (mData->mSession.mState != SessionState_Unlocked)
5265 return setError(VBOX_E_INVALID_OBJECT_STATE,
5266 tr("Cannot unregister the machine '%s' while it is locked"),
5267 mUserData->s.strName.c_str());
5268
5269 // wait for state dependents to drop to zero
5270 i_ensureNoStateDependencies(alock);
5271
5272 if (!mData->mAccessible)
5273 {
5274 // inaccessible machines can only be unregistered; uninitialize ourselves
5275 // here because currently there may be no unregistered that are inaccessible
5276 // (this state combination is not supported). Note releasing the caller and
5277 // leaving the lock before calling uninit()
5278 alock.release();
5279 autoCaller.release();
5280
5281 uninit();
5282
5283 mParent->i_unregisterMachine(this, CleanupMode_UnregisterOnly, id);
5284 // calls VirtualBox::i_saveSettings()
5285
5286 return S_OK;
5287 }
5288
5289 HRESULT hrc = S_OK;
5290 mData->llFilesToDelete.clear();
5291
5292 if (!mSSData->strStateFilePath.isEmpty())
5293 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5294
5295 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
5296 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
5297 mData->llFilesToDelete.push_back(strNVRAMFile);
5298
5299 // This list collects the medium objects from all medium attachments
5300 // which we will detach from the machine and its snapshots, in a specific
5301 // order which allows for closing all media without getting "media in use"
5302 // errors, simply by going through the list from the front to the back:
5303 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5304 // and must be closed before the parent media from the snapshots, or closing the parents
5305 // will fail because they still have children);
5306 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5307 // the root ("first") snapshot of the machine.
5308 MediaList llMedia;
5309
5310 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5311 && mMediumAttachments->size()
5312 )
5313 {
5314 // we have media attachments: detach them all and add the Medium objects to our list
5315 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5316 }
5317
5318 if (mData->mFirstSnapshot)
5319 {
5320 // add the media from the medium attachments of the snapshots to
5321 // llMedia as well, after the "main" machine media;
5322 // Snapshot::uninitAll() calls Machine::detachAllMedia() for each
5323 // snapshot machine, depth first.
5324
5325 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5326 MachineState_T oldState = mData->mMachineState;
5327 mData->mMachineState = MachineState_DeletingSnapshot;
5328
5329 // make a copy of the first snapshot reference so the refcount does not
5330 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
5331 // (would hang due to the AutoCaller voodoo)
5332 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5333
5334 // GO!
5335 pFirstSnapshot->i_uninitAll(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5336
5337 mData->mMachineState = oldState;
5338 }
5339
5340 if (FAILED(hrc))
5341 {
5342 i_rollbackMedia();
5343 return hrc;
5344 }
5345
5346 // commit all the media changes made above
5347 i_commitMedia();
5348
5349 mData->mRegistered = false;
5350
5351 // machine lock no longer needed
5352 alock.release();
5353
5354 /* Make sure that the settings of the current VM are not saved, because
5355 * they are rather crippled at this point to meet the cleanup expectations
5356 * and there's no point destroying the VM config on disk just because. */
5357 mParent->i_unmarkRegistryModified(id);
5358
5359 // return media to caller
5360 aMedia.resize(llMedia.size());
5361 size_t i = 0;
5362 for (MediaList::const_iterator
5363 it = llMedia.begin();
5364 it != llMedia.end();
5365 ++it, ++i)
5366 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5367
5368 mParent->i_unregisterMachine(this, aCleanupMode, id);
5369 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5370
5371 return S_OK;
5372}
5373
5374/**
5375 * Task record for deleting a machine config.
5376 */
5377class Machine::DeleteConfigTask
5378 : public Machine::Task
5379{
5380public:
5381 DeleteConfigTask(Machine *m,
5382 Progress *p,
5383 const Utf8Str &t,
5384 const RTCList<ComPtr<IMedium> > &llMedia,
5385 const StringsList &llFilesToDelete)
5386 : Task(m, p, t),
5387 m_llMedia(llMedia),
5388 m_llFilesToDelete(llFilesToDelete)
5389 {}
5390
5391private:
5392 void handler()
5393 {
5394 try
5395 {
5396 m_pMachine->i_deleteConfigHandler(*this);
5397 }
5398 catch (...)
5399 {
5400 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5401 }
5402 }
5403
5404 RTCList<ComPtr<IMedium> > m_llMedia;
5405 StringsList m_llFilesToDelete;
5406
5407 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5408};
5409
5410/**
5411 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5412 * SessionMachine::taskHandler().
5413 *
5414 * @note Locks this object for writing.
5415 *
5416 * @param task
5417 * @return
5418 */
5419void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5420{
5421 LogFlowThisFuncEnter();
5422
5423 AutoCaller autoCaller(this);
5424 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5425 if (FAILED(autoCaller.hrc()))
5426 {
5427 /* we might have been uninitialized because the session was accidentally
5428 * closed by the client, so don't assert */
5429 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
5430 task.m_pProgress->i_notifyComplete(hrc);
5431 LogFlowThisFuncLeave();
5432 return;
5433 }
5434
5435 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5436
5437 HRESULT hrc;
5438 try
5439 {
5440 ULONG uLogHistoryCount = 3;
5441 ComPtr<ISystemProperties> systemProperties;
5442 hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5443 if (FAILED(hrc)) throw hrc;
5444
5445 if (!systemProperties.isNull())
5446 {
5447 hrc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5448 if (FAILED(hrc)) throw hrc;
5449 }
5450
5451 MachineState_T oldState = mData->mMachineState;
5452 i_setMachineState(MachineState_SettingUp);
5453 alock.release();
5454 for (size_t i = 0; i < task.m_llMedia.size(); ++i)
5455 {
5456 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMedia.at(i));
5457 {
5458 AutoCaller mac(pMedium);
5459 if (FAILED(mac.hrc())) throw mac.hrc();
5460 Utf8Str strLocation = pMedium->i_getLocationFull();
5461 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5462 hrc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5463 if (FAILED(hrc)) throw hrc;
5464 }
5465 if (pMedium->i_isMediumFormatFile())
5466 {
5467 ComPtr<IProgress> pProgress2;
5468 hrc = pMedium->DeleteStorage(pProgress2.asOutParam());
5469 if (FAILED(hrc)) throw hrc;
5470 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5471 if (FAILED(hrc)) throw hrc;
5472 }
5473
5474 /* Close the medium, deliberately without checking the return
5475 * code, and without leaving any trace in the error info, as
5476 * a failure here is a very minor issue, which shouldn't happen
5477 * as above we even managed to delete the medium. */
5478 {
5479 ErrorInfoKeeper eik;
5480 pMedium->Close();
5481 }
5482 }
5483 i_setMachineState(oldState);
5484 alock.acquire();
5485
5486 // delete the files pushed on the task list by Machine::Delete()
5487 // (this includes saved states of the machine and snapshots and
5488 // medium storage files from the IMedium list passed in, and the
5489 // machine XML file)
5490 for (StringsList::const_iterator
5491 it = task.m_llFilesToDelete.begin();
5492 it != task.m_llFilesToDelete.end();
5493 ++it)
5494 {
5495 const Utf8Str &strFile = *it;
5496 LogFunc(("Deleting file %s\n", strFile.c_str()));
5497 hrc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5498 if (FAILED(hrc)) throw hrc;
5499 i_deleteFile(strFile);
5500 }
5501
5502 hrc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5503 if (FAILED(hrc)) throw hrc;
5504
5505 /* delete the settings only when the file actually exists */
5506 if (mData->pMachineConfigFile->fileExists())
5507 {
5508 /* Delete any backup or uncommitted XML files. Ignore failures.
5509 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5510 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5511 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5512 i_deleteFile(otherXml, true /* fIgnoreFailures */);
5513 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5514 i_deleteFile(otherXml, true /* fIgnoreFailures */);
5515
5516 /* delete the Logs folder, nothing important should be left
5517 * there (we don't check for errors because the user might have
5518 * some private files there that we don't want to delete) */
5519 Utf8Str logFolder;
5520 getLogFolder(logFolder);
5521 Assert(logFolder.length());
5522 if (RTDirExists(logFolder.c_str()))
5523 {
5524 /* Delete all VBox.log[.N] files from the Logs folder
5525 * (this must be in sync with the rotation logic in
5526 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5527 * files that may have been created by the GUI. */
5528 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5529 i_deleteFile(log, true /* fIgnoreFailures */);
5530 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5531 i_deleteFile(log, true /* fIgnoreFailures */);
5532 for (ULONG i = uLogHistoryCount; i > 0; i--)
5533 {
5534 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5535 i_deleteFile(log, true /* fIgnoreFailures */);
5536 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5537 i_deleteFile(log, true /* fIgnoreFailures */);
5538 }
5539 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5540 i_deleteFile(log, true /* fIgnoreFailures */);
5541#if defined(RT_OS_WINDOWS)
5542 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5543 i_deleteFile(log, true /* fIgnoreFailures */);
5544 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5545 i_deleteFile(log, true /* fIgnoreFailures */);
5546#endif
5547
5548 RTDirRemove(logFolder.c_str());
5549 }
5550
5551 /* delete the Snapshots folder, nothing important should be left
5552 * there (we don't check for errors because the user might have
5553 * some private files there that we don't want to delete) */
5554 Utf8Str strFullSnapshotFolder;
5555 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5556 Assert(!strFullSnapshotFolder.isEmpty());
5557 if (RTDirExists(strFullSnapshotFolder.c_str()))
5558 RTDirRemove(strFullSnapshotFolder.c_str());
5559
5560 // delete the directory that contains the settings file, but only
5561 // if it matches the VM name
5562 Utf8Str settingsDir;
5563 if (i_isInOwnDir(&settingsDir))
5564 RTDirRemove(settingsDir.c_str());
5565 }
5566
5567 alock.release();
5568
5569 mParent->i_saveModifiedRegistries();
5570 }
5571 catch (HRESULT hrcXcpt)
5572 {
5573 hrc = hrcXcpt;
5574 }
5575
5576 task.m_pProgress->i_notifyComplete(hrc);
5577
5578 LogFlowThisFuncLeave();
5579}
5580
5581HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5582{
5583 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5584
5585 HRESULT hrc = i_checkStateDependency(MutableStateDep);
5586 if (FAILED(hrc)) return hrc;
5587
5588 if (mData->mRegistered)
5589 return setError(VBOX_E_INVALID_VM_STATE,
5590 tr("Cannot delete settings of a registered machine"));
5591
5592 // collect files to delete
5593 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5594 // machine config file
5595 if (mData->pMachineConfigFile->fileExists())
5596 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5597 // backup of machine config file
5598 Utf8Str strTmp(mData->m_strConfigFileFull);
5599 strTmp.append("-prev");
5600 if (RTFileExists(strTmp.c_str()))
5601 llFilesToDelete.push_back(strTmp);
5602
5603 RTCList<ComPtr<IMedium> > llMedia;
5604 for (size_t i = 0; i < aMedia.size(); ++i)
5605 {
5606 IMedium *pIMedium(aMedia[i]);
5607 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5608 if (pMedium.isNull())
5609 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
5610 SafeArray<BSTR> ids;
5611 hrc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5612 if (FAILED(hrc)) return hrc;
5613 /* At this point the medium should not have any back references
5614 * anymore. If it has it is attached to another VM and *must* not
5615 * deleted. */
5616 if (ids.size() < 1)
5617 llMedia.append(pMedium);
5618 }
5619
5620 ComObjPtr<Progress> pProgress;
5621 pProgress.createObject();
5622 hrc = pProgress->init(i_getVirtualBox(),
5623 static_cast<IMachine*>(this) /* aInitiator */,
5624 tr("Deleting files"),
5625 true /* fCancellable */,
5626 (ULONG)(1 + llMedia.size() + llFilesToDelete.size() + 1), // cOperations
5627 tr("Collecting file inventory"));
5628 if (FAILED(hrc))
5629 return hrc;
5630
5631 /* create and start the task on a separate thread (note that it will not
5632 * start working until we release alock) */
5633 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMedia, llFilesToDelete);
5634 hrc = pTask->createThread();
5635 pTask = NULL;
5636 if (FAILED(hrc))
5637 return hrc;
5638
5639 pProgress.queryInterfaceTo(aProgress.asOutParam());
5640
5641 LogFlowFuncLeave();
5642
5643 return S_OK;
5644}
5645
5646HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5647{
5648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5649
5650 ComObjPtr<Snapshot> pSnapshot;
5651 HRESULT hrc;
5652
5653 if (aNameOrId.isEmpty())
5654 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5655 hrc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5656 else
5657 {
5658 Guid uuid(aNameOrId);
5659 if (uuid.isValid())
5660 hrc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5661 else
5662 hrc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5663 }
5664 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5665
5666 return hrc;
5667}
5668
5669HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5670 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5671{
5672 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5673
5674 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
5675 if (FAILED(hrc)) return hrc;
5676
5677 ComObjPtr<SharedFolder> sharedFolder;
5678 hrc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5679 if (SUCCEEDED(hrc))
5680 return setError(VBOX_E_OBJECT_IN_USE,
5681 tr("Shared folder named '%s' already exists"),
5682 aName.c_str());
5683
5684 sharedFolder.createObject();
5685 hrc = sharedFolder->init(i_getMachine(),
5686 aName,
5687 aHostPath,
5688 !!aWritable,
5689 !!aAutomount,
5690 aAutoMountPoint,
5691 true /* fFailOnError */);
5692 if (FAILED(hrc)) return hrc;
5693
5694 i_setModified(IsModified_SharedFolders);
5695 mHWData.backup();
5696 mHWData->mSharedFolders.push_back(sharedFolder);
5697
5698 /* inform the direct session if any */
5699 alock.release();
5700 i_onSharedFolderChange();
5701
5702 return S_OK;
5703}
5704
5705HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5706{
5707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5708
5709 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
5710 if (FAILED(hrc)) return hrc;
5711
5712 ComObjPtr<SharedFolder> sharedFolder;
5713 hrc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5714 if (FAILED(hrc)) return hrc;
5715
5716 i_setModified(IsModified_SharedFolders);
5717 mHWData.backup();
5718 mHWData->mSharedFolders.remove(sharedFolder);
5719
5720 /* inform the direct session if any */
5721 alock.release();
5722 i_onSharedFolderChange();
5723
5724 return S_OK;
5725}
5726
5727HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5728{
5729 /* start with No */
5730 *aCanShow = FALSE;
5731
5732 ComPtr<IInternalSessionControl> directControl;
5733 {
5734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5735
5736 if (mData->mSession.mState != SessionState_Locked)
5737 return setError(VBOX_E_INVALID_VM_STATE,
5738 tr("Machine is not locked for session (session state: %s)"),
5739 Global::stringifySessionState(mData->mSession.mState));
5740
5741 if (mData->mSession.mLockType == LockType_VM)
5742 directControl = mData->mSession.mDirectControl;
5743 }
5744
5745 /* ignore calls made after #OnSessionEnd() is called */
5746 if (!directControl)
5747 return S_OK;
5748
5749 LONG64 dummy;
5750 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5751}
5752
5753HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5754{
5755 ComPtr<IInternalSessionControl> directControl;
5756 {
5757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5758
5759 if (mData->mSession.mState != SessionState_Locked)
5760 return setError(E_FAIL,
5761 tr("Machine is not locked for session (session state: %s)"),
5762 Global::stringifySessionState(mData->mSession.mState));
5763
5764 if (mData->mSession.mLockType == LockType_VM)
5765 directControl = mData->mSession.mDirectControl;
5766 }
5767
5768 /* ignore calls made after #OnSessionEnd() is called */
5769 if (!directControl)
5770 return S_OK;
5771
5772 BOOL dummy;
5773 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5774}
5775
5776#ifdef VBOX_WITH_GUEST_PROPS
5777/**
5778 * Look up a guest property in VBoxSVC's internal structures.
5779 */
5780HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5781 com::Utf8Str &aValue,
5782 LONG64 *aTimestamp,
5783 com::Utf8Str &aFlags) const
5784{
5785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5786
5787 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5788 if (it != mHWData->mGuestProperties.end())
5789 {
5790 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5791 aValue = it->second.strValue;
5792 *aTimestamp = it->second.mTimestamp;
5793 GuestPropWriteFlags(it->second.mFlags, szFlags);
5794 aFlags = Utf8Str(szFlags);
5795 }
5796
5797 return S_OK;
5798}
5799
5800/**
5801 * Query the VM that a guest property belongs to for the property.
5802 * @returns E_ACCESSDENIED if the VM process is not available or not
5803 * currently handling queries and the lookup should then be done in
5804 * VBoxSVC.
5805 */
5806HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5807 com::Utf8Str &aValue,
5808 LONG64 *aTimestamp,
5809 com::Utf8Str &aFlags) const
5810{
5811 HRESULT hrc = S_OK;
5812 Bstr bstrValue;
5813 Bstr bstrFlags;
5814
5815 ComPtr<IInternalSessionControl> directControl;
5816 {
5817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5818 if (mData->mSession.mLockType == LockType_VM)
5819 directControl = mData->mSession.mDirectControl;
5820 }
5821
5822 /* ignore calls made after #OnSessionEnd() is called */
5823 if (!directControl)
5824 hrc = E_ACCESSDENIED;
5825 else
5826 hrc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5827 0 /* accessMode */,
5828 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5829
5830 aValue = bstrValue;
5831 aFlags = bstrFlags;
5832
5833 return hrc;
5834}
5835#endif // VBOX_WITH_GUEST_PROPS
5836
5837HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5838 com::Utf8Str &aValue,
5839 LONG64 *aTimestamp,
5840 com::Utf8Str &aFlags)
5841{
5842#ifndef VBOX_WITH_GUEST_PROPS
5843 ReturnComNotImplemented();
5844#else // VBOX_WITH_GUEST_PROPS
5845
5846 HRESULT hrc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5847
5848 if (hrc == E_ACCESSDENIED)
5849 /* The VM is not running or the service is not (yet) accessible */
5850 hrc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5851 return hrc;
5852#endif // VBOX_WITH_GUEST_PROPS
5853}
5854
5855HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5856{
5857 LONG64 dummyTimestamp;
5858 com::Utf8Str dummyFlags;
5859 return getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5860
5861}
5862HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5863{
5864 com::Utf8Str dummyFlags;
5865 com::Utf8Str dummyValue;
5866 return getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5867}
5868
5869#ifdef VBOX_WITH_GUEST_PROPS
5870/**
5871 * Set a guest property in VBoxSVC's internal structures.
5872 */
5873HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5874 const com::Utf8Str &aFlags, bool fDelete)
5875{
5876 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5877 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
5878 if (FAILED(hrc)) return hrc;
5879
5880 try
5881 {
5882 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5883 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5884 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5885
5886 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
5887 return setError(E_INVALIDARG, tr("Properties with TRANSIENT or TRANSRESET flag cannot be set or modified if VM is not running"));
5888
5889 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5890 if (it == mHWData->mGuestProperties.end())
5891 {
5892 if (!fDelete)
5893 {
5894 i_setModified(IsModified_MachineData);
5895 mHWData.backupEx();
5896
5897 RTTIMESPEC time;
5898 HWData::GuestProperty prop;
5899 prop.strValue = aValue;
5900 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5901 prop.mFlags = fFlags;
5902 mHWData->mGuestProperties[aName] = prop;
5903 }
5904 }
5905 else
5906 {
5907 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5908 {
5909 hrc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5910 }
5911 else
5912 {
5913 i_setModified(IsModified_MachineData);
5914 mHWData.backupEx();
5915
5916 /* The backupEx() operation invalidates our iterator,
5917 * so get a new one. */
5918 it = mHWData->mGuestProperties.find(aName);
5919 Assert(it != mHWData->mGuestProperties.end());
5920
5921 if (!fDelete)
5922 {
5923 RTTIMESPEC time;
5924 it->second.strValue = aValue;
5925 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5926 it->second.mFlags = fFlags;
5927 }
5928 else
5929 mHWData->mGuestProperties.erase(it);
5930 }
5931 }
5932
5933 if (SUCCEEDED(hrc))
5934 {
5935 alock.release();
5936
5937 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fDelete);
5938 }
5939 }
5940 catch (std::bad_alloc &)
5941 {
5942 hrc = E_OUTOFMEMORY;
5943 }
5944
5945 return hrc;
5946}
5947
5948/**
5949 * Set a property on the VM that that property belongs to.
5950 * @returns E_ACCESSDENIED if the VM process is not available or not
5951 * currently handling queries and the setting should then be done in
5952 * VBoxSVC.
5953 */
5954HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5955 const com::Utf8Str &aFlags, bool fDelete)
5956{
5957 HRESULT hrc;
5958
5959 try
5960 {
5961 ComPtr<IInternalSessionControl> directControl;
5962 {
5963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5964 if (mData->mSession.mLockType == LockType_VM)
5965 directControl = mData->mSession.mDirectControl;
5966 }
5967
5968 Bstr dummy1; /* will not be changed (setter) */
5969 Bstr dummy2; /* will not be changed (setter) */
5970 LONG64 dummy64;
5971 if (!directControl)
5972 hrc = E_ACCESSDENIED;
5973 else
5974 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5975 hrc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5976 fDelete ? 2 : 1 /* accessMode */,
5977 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5978 }
5979 catch (std::bad_alloc &)
5980 {
5981 hrc = E_OUTOFMEMORY;
5982 }
5983
5984 return hrc;
5985}
5986#endif // VBOX_WITH_GUEST_PROPS
5987
5988HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5989 const com::Utf8Str &aFlags)
5990{
5991#ifndef VBOX_WITH_GUEST_PROPS
5992 ReturnComNotImplemented();
5993#else // VBOX_WITH_GUEST_PROPS
5994
5995 int vrc = GuestPropValidateName(aProperty.c_str(), aProperty.length() + 1 /* '\0' */);
5996 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
5997
5998 vrc = GuestPropValidateValue(aValue.c_str(), aValue.length() + 1 /* '\0' */);
5999 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
6000
6001 HRESULT hrc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
6002 if (hrc == E_ACCESSDENIED)
6003 /* The VM is not running or the service is not (yet) accessible */
6004 hrc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
6005 return hrc;
6006#endif // VBOX_WITH_GUEST_PROPS
6007}
6008
6009HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
6010{
6011 return setGuestProperty(aProperty, aValue, "");
6012}
6013
6014HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
6015{
6016#ifndef VBOX_WITH_GUEST_PROPS
6017 ReturnComNotImplemented();
6018#else // VBOX_WITH_GUEST_PROPS
6019 HRESULT hrc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
6020 if (hrc == E_ACCESSDENIED)
6021 /* The VM is not running or the service is not (yet) accessible */
6022 hrc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
6023 return hrc;
6024#endif // VBOX_WITH_GUEST_PROPS
6025}
6026
6027#ifdef VBOX_WITH_GUEST_PROPS
6028/**
6029 * Enumerate the guest properties in VBoxSVC's internal structures.
6030 */
6031HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
6032 std::vector<com::Utf8Str> &aNames,
6033 std::vector<com::Utf8Str> &aValues,
6034 std::vector<LONG64> &aTimestamps,
6035 std::vector<com::Utf8Str> &aFlags)
6036{
6037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6038 Utf8Str strPatterns(aPatterns);
6039
6040 /*
6041 * Look for matching patterns and build up a list.
6042 */
6043 HWData::GuestPropertyMap propMap;
6044 for (HWData::GuestPropertyMap::const_iterator
6045 it = mHWData->mGuestProperties.begin();
6046 it != mHWData->mGuestProperties.end();
6047 ++it)
6048 {
6049 if ( strPatterns.isEmpty()
6050 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6051 RTSTR_MAX,
6052 it->first.c_str(),
6053 RTSTR_MAX,
6054 NULL)
6055 )
6056 propMap.insert(*it);
6057 }
6058
6059 alock.release();
6060
6061 /*
6062 * And build up the arrays for returning the property information.
6063 */
6064 size_t cEntries = propMap.size();
6065
6066 aNames.resize(cEntries);
6067 aValues.resize(cEntries);
6068 aTimestamps.resize(cEntries);
6069 aFlags.resize(cEntries);
6070
6071 size_t i = 0;
6072 for (HWData::GuestPropertyMap::const_iterator
6073 it = propMap.begin();
6074 it != propMap.end();
6075 ++it, ++i)
6076 {
6077 aNames[i] = it->first;
6078 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
6079 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
6080
6081 aValues[i] = it->second.strValue;
6082 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
6083 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
6084
6085 aTimestamps[i] = it->second.mTimestamp;
6086
6087 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6088 GuestPropWriteFlags(it->second.mFlags, szFlags);
6089 aFlags[i] = szFlags;
6090 }
6091
6092 return S_OK;
6093}
6094
6095/**
6096 * Enumerate the properties managed by a VM.
6097 * @returns E_ACCESSDENIED if the VM process is not available or not
6098 * currently handling queries and the setting should then be done in
6099 * VBoxSVC.
6100 */
6101HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6102 std::vector<com::Utf8Str> &aNames,
6103 std::vector<com::Utf8Str> &aValues,
6104 std::vector<LONG64> &aTimestamps,
6105 std::vector<com::Utf8Str> &aFlags)
6106{
6107 HRESULT hrc;
6108 ComPtr<IInternalSessionControl> directControl;
6109 {
6110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6111 if (mData->mSession.mLockType == LockType_VM)
6112 directControl = mData->mSession.mDirectControl;
6113 }
6114
6115 com::SafeArray<BSTR> bNames;
6116 com::SafeArray<BSTR> bValues;
6117 com::SafeArray<LONG64> bTimestamps;
6118 com::SafeArray<BSTR> bFlags;
6119
6120 if (!directControl)
6121 hrc = E_ACCESSDENIED;
6122 else
6123 hrc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6124 ComSafeArrayAsOutParam(bNames),
6125 ComSafeArrayAsOutParam(bValues),
6126 ComSafeArrayAsOutParam(bTimestamps),
6127 ComSafeArrayAsOutParam(bFlags));
6128 size_t i;
6129 aNames.resize(bNames.size());
6130 for (i = 0; i < bNames.size(); ++i)
6131 aNames[i] = Utf8Str(bNames[i]);
6132 aValues.resize(bValues.size());
6133 for (i = 0; i < bValues.size(); ++i)
6134 aValues[i] = Utf8Str(bValues[i]);
6135 aTimestamps.resize(bTimestamps.size());
6136 for (i = 0; i < bTimestamps.size(); ++i)
6137 aTimestamps[i] = bTimestamps[i];
6138 aFlags.resize(bFlags.size());
6139 for (i = 0; i < bFlags.size(); ++i)
6140 aFlags[i] = Utf8Str(bFlags[i]);
6141
6142 return hrc;
6143}
6144#endif // VBOX_WITH_GUEST_PROPS
6145HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6146 std::vector<com::Utf8Str> &aNames,
6147 std::vector<com::Utf8Str> &aValues,
6148 std::vector<LONG64> &aTimestamps,
6149 std::vector<com::Utf8Str> &aFlags)
6150{
6151#ifndef VBOX_WITH_GUEST_PROPS
6152 ReturnComNotImplemented();
6153#else // VBOX_WITH_GUEST_PROPS
6154
6155 HRESULT hrc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6156
6157 if (hrc == E_ACCESSDENIED)
6158 /* The VM is not running or the service is not (yet) accessible */
6159 hrc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6160 return hrc;
6161#endif // VBOX_WITH_GUEST_PROPS
6162}
6163
6164HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6165 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6166{
6167 MediumAttachmentList atts;
6168
6169 HRESULT hrc = i_getMediumAttachmentsOfController(aName, atts);
6170 if (FAILED(hrc)) return hrc;
6171
6172 aMediumAttachments.resize(atts.size());
6173 size_t i = 0;
6174 for (MediumAttachmentList::const_iterator
6175 it = atts.begin();
6176 it != atts.end();
6177 ++it, ++i)
6178 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6179
6180 return S_OK;
6181}
6182
6183HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6184 LONG aControllerPort,
6185 LONG aDevice,
6186 ComPtr<IMediumAttachment> &aAttachment)
6187{
6188 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6189 aName.c_str(), aControllerPort, aDevice));
6190
6191 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6192
6193 aAttachment = NULL;
6194
6195 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6196 aName,
6197 aControllerPort,
6198 aDevice);
6199 if (pAttach.isNull())
6200 return setError(VBOX_E_OBJECT_NOT_FOUND,
6201 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6202 aDevice, aControllerPort, aName.c_str());
6203
6204 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6205
6206 return S_OK;
6207}
6208
6209
6210HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6211 StorageBus_T aConnectionType,
6212 ComPtr<IStorageController> &aController)
6213{
6214 if ( (aConnectionType <= StorageBus_Null)
6215 || (aConnectionType > StorageBus_VirtioSCSI))
6216 return setError(E_INVALIDARG,
6217 tr("Invalid connection type: %d"),
6218 aConnectionType);
6219
6220 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6221
6222 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6223 if (FAILED(hrc)) return hrc;
6224
6225 /* try to find one with the name first. */
6226 ComObjPtr<StorageController> ctrl;
6227
6228 hrc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6229 if (SUCCEEDED(hrc))
6230 return setError(VBOX_E_OBJECT_IN_USE,
6231 tr("Storage controller named '%s' already exists"),
6232 aName.c_str());
6233
6234 ctrl.createObject();
6235
6236 /* get a new instance number for the storage controller */
6237 ULONG ulInstance = 0;
6238 bool fBootable = true;
6239 for (StorageControllerList::const_iterator
6240 it = mStorageControllers->begin();
6241 it != mStorageControllers->end();
6242 ++it)
6243 {
6244 if ((*it)->i_getStorageBus() == aConnectionType)
6245 {
6246 ULONG ulCurInst = (*it)->i_getInstance();
6247
6248 if (ulCurInst >= ulInstance)
6249 ulInstance = ulCurInst + 1;
6250
6251 /* Only one controller of each type can be marked as bootable. */
6252 if ((*it)->i_getBootable())
6253 fBootable = false;
6254 }
6255 }
6256
6257 hrc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6258 if (FAILED(hrc)) return hrc;
6259
6260 i_setModified(IsModified_Storage);
6261 mStorageControllers.backup();
6262 mStorageControllers->push_back(ctrl);
6263
6264 ctrl.queryInterfaceTo(aController.asOutParam());
6265
6266 /* inform the direct session if any */
6267 alock.release();
6268 i_onStorageControllerChange(i_getId(), aName);
6269
6270 return S_OK;
6271}
6272
6273HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6274 ComPtr<IStorageController> &aStorageController)
6275{
6276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6277
6278 ComObjPtr<StorageController> ctrl;
6279
6280 HRESULT hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6281 if (SUCCEEDED(hrc))
6282 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6283
6284 return hrc;
6285}
6286
6287HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6288 ULONG aInstance,
6289 ComPtr<IStorageController> &aStorageController)
6290{
6291 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6292
6293 for (StorageControllerList::const_iterator
6294 it = mStorageControllers->begin();
6295 it != mStorageControllers->end();
6296 ++it)
6297 {
6298 if ( (*it)->i_getStorageBus() == aConnectionType
6299 && (*it)->i_getInstance() == aInstance)
6300 {
6301 (*it).queryInterfaceTo(aStorageController.asOutParam());
6302 return S_OK;
6303 }
6304 }
6305
6306 return setError(VBOX_E_OBJECT_NOT_FOUND,
6307 tr("Could not find a storage controller with instance number '%lu'"),
6308 aInstance);
6309}
6310
6311HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6312{
6313 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6314
6315 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6316 if (FAILED(hrc)) return hrc;
6317
6318 ComObjPtr<StorageController> ctrl;
6319
6320 hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6321 if (SUCCEEDED(hrc))
6322 {
6323 /* Ensure that only one controller of each type is marked as bootable. */
6324 if (aBootable == TRUE)
6325 {
6326 for (StorageControllerList::const_iterator
6327 it = mStorageControllers->begin();
6328 it != mStorageControllers->end();
6329 ++it)
6330 {
6331 ComObjPtr<StorageController> aCtrl = (*it);
6332
6333 if ( (aCtrl->i_getName() != aName)
6334 && aCtrl->i_getBootable() == TRUE
6335 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6336 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6337 {
6338 aCtrl->i_setBootable(FALSE);
6339 break;
6340 }
6341 }
6342 }
6343
6344 if (SUCCEEDED(hrc))
6345 {
6346 ctrl->i_setBootable(aBootable);
6347 i_setModified(IsModified_Storage);
6348 }
6349 }
6350
6351 if (SUCCEEDED(hrc))
6352 {
6353 /* inform the direct session if any */
6354 alock.release();
6355 i_onStorageControllerChange(i_getId(), aName);
6356 }
6357
6358 return hrc;
6359}
6360
6361HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6362{
6363 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6364
6365 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6366 if (FAILED(hrc)) return hrc;
6367
6368 ComObjPtr<StorageController> ctrl;
6369 hrc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6370 if (FAILED(hrc)) return hrc;
6371
6372 MediumAttachmentList llDetachedAttachments;
6373 {
6374 /* find all attached devices to the appropriate storage controller and detach them all */
6375 // make a temporary list because detachDevice invalidates iterators into
6376 // mMediumAttachments
6377 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6378
6379 for (MediumAttachmentList::const_iterator
6380 it = llAttachments2.begin();
6381 it != llAttachments2.end();
6382 ++it)
6383 {
6384 MediumAttachment *pAttachTemp = *it;
6385
6386 AutoCaller localAutoCaller(pAttachTemp);
6387 if (FAILED(localAutoCaller.hrc())) return localAutoCaller.hrc();
6388
6389 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6390
6391 if (pAttachTemp->i_getControllerName() == aName)
6392 {
6393 llDetachedAttachments.push_back(pAttachTemp);
6394 hrc = i_detachDevice(pAttachTemp, alock, NULL);
6395 if (FAILED(hrc)) return hrc;
6396 }
6397 }
6398 }
6399
6400 /* send event about detached devices before removing parent controller */
6401 for (MediumAttachmentList::const_iterator
6402 it = llDetachedAttachments.begin();
6403 it != llDetachedAttachments.end();
6404 ++it)
6405 {
6406 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6407 }
6408
6409 /* We can remove it now. */
6410 i_setModified(IsModified_Storage);
6411 mStorageControllers.backup();
6412
6413 ctrl->i_unshare();
6414
6415 mStorageControllers->remove(ctrl);
6416
6417 /* inform the direct session if any */
6418 alock.release();
6419 i_onStorageControllerChange(i_getId(), aName);
6420
6421 return S_OK;
6422}
6423
6424HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6425 ComPtr<IUSBController> &aController)
6426{
6427 if ( (aType <= USBControllerType_Null)
6428 || (aType >= USBControllerType_Last))
6429 return setError(E_INVALIDARG,
6430 tr("Invalid USB controller type: %d"),
6431 aType);
6432
6433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6434
6435 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6436 if (FAILED(hrc)) return hrc;
6437
6438 /* try to find one with the same type first. */
6439 ComObjPtr<USBController> ctrl;
6440
6441 hrc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6442 if (SUCCEEDED(hrc))
6443 return setError(VBOX_E_OBJECT_IN_USE,
6444 tr("USB controller named '%s' already exists"),
6445 aName.c_str());
6446
6447 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6448 ULONG maxInstances;
6449 hrc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6450 if (FAILED(hrc))
6451 return hrc;
6452
6453 ULONG cInstances = i_getUSBControllerCountByType(aType);
6454 if (cInstances >= maxInstances)
6455 return setError(E_INVALIDARG,
6456 tr("Too many USB controllers of this type"));
6457
6458 ctrl.createObject();
6459
6460 hrc = ctrl->init(this, aName, aType);
6461 if (FAILED(hrc)) return hrc;
6462
6463 i_setModified(IsModified_USB);
6464 mUSBControllers.backup();
6465 mUSBControllers->push_back(ctrl);
6466
6467 ctrl.queryInterfaceTo(aController.asOutParam());
6468
6469 /* inform the direct session if any */
6470 alock.release();
6471 i_onUSBControllerChange();
6472
6473 return S_OK;
6474}
6475
6476HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6477{
6478 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6479
6480 ComObjPtr<USBController> ctrl;
6481
6482 HRESULT hrc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6483 if (SUCCEEDED(hrc))
6484 ctrl.queryInterfaceTo(aController.asOutParam());
6485
6486 return hrc;
6487}
6488
6489HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6490 ULONG *aControllers)
6491{
6492 if ( (aType <= USBControllerType_Null)
6493 || (aType >= USBControllerType_Last))
6494 return setError(E_INVALIDARG,
6495 tr("Invalid USB controller type: %d"),
6496 aType);
6497
6498 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6499
6500 ComObjPtr<USBController> ctrl;
6501
6502 *aControllers = i_getUSBControllerCountByType(aType);
6503
6504 return S_OK;
6505}
6506
6507HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6508{
6509
6510 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6511
6512 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6513 if (FAILED(hrc)) return hrc;
6514
6515 ComObjPtr<USBController> ctrl;
6516 hrc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6517 if (FAILED(hrc)) return hrc;
6518
6519 i_setModified(IsModified_USB);
6520 mUSBControllers.backup();
6521
6522 ctrl->i_unshare();
6523
6524 mUSBControllers->remove(ctrl);
6525
6526 /* inform the direct session if any */
6527 alock.release();
6528 i_onUSBControllerChange();
6529
6530 return S_OK;
6531}
6532
6533HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6534 ULONG *aOriginX,
6535 ULONG *aOriginY,
6536 ULONG *aWidth,
6537 ULONG *aHeight,
6538 BOOL *aEnabled)
6539{
6540 uint32_t u32OriginX= 0;
6541 uint32_t u32OriginY= 0;
6542 uint32_t u32Width = 0;
6543 uint32_t u32Height = 0;
6544 uint16_t u16Flags = 0;
6545
6546#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6547 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6548#else
6549 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6550#endif
6551 int vrc = readSavedGuestScreenInfo(SavedStateStream, mSSData->strStateFilePath, aScreenId,
6552 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6553 if (RT_FAILURE(vrc))
6554 {
6555#ifdef RT_OS_WINDOWS
6556 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6557 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6558 * So just assign fEnable to TRUE again.
6559 * The right fix would be to change GUI API wrappers to make sure that parameters
6560 * are changed only if API succeeds.
6561 */
6562 *aEnabled = TRUE;
6563#endif
6564 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6565 tr("Saved guest size is not available (%Rrc)"),
6566 vrc);
6567 }
6568
6569 *aOriginX = u32OriginX;
6570 *aOriginY = u32OriginY;
6571 *aWidth = u32Width;
6572 *aHeight = u32Height;
6573 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6574
6575 return S_OK;
6576}
6577
6578HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6579 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6580{
6581 if (aScreenId != 0)
6582 return E_NOTIMPL;
6583
6584 if ( aBitmapFormat != BitmapFormat_BGR0
6585 && aBitmapFormat != BitmapFormat_BGRA
6586 && aBitmapFormat != BitmapFormat_RGBA
6587 && aBitmapFormat != BitmapFormat_PNG)
6588 return setError(E_NOTIMPL,
6589 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6590
6591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6592
6593 uint8_t *pu8Data = NULL;
6594 uint32_t cbData = 0;
6595 uint32_t u32Width = 0;
6596 uint32_t u32Height = 0;
6597
6598#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6599 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6600#else
6601 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6602#endif
6603 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 0 /* u32Type */,
6604 &pu8Data, &cbData, &u32Width, &u32Height);
6605 if (RT_FAILURE(vrc))
6606 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6607 tr("Saved thumbnail data is not available (%Rrc)"),
6608 vrc);
6609
6610 HRESULT hrc = S_OK;
6611
6612 *aWidth = u32Width;
6613 *aHeight = u32Height;
6614
6615 if (cbData > 0)
6616 {
6617 /* Convert pixels to the format expected by the API caller. */
6618 if (aBitmapFormat == BitmapFormat_BGR0)
6619 {
6620 /* [0] B, [1] G, [2] R, [3] 0. */
6621 aData.resize(cbData);
6622 memcpy(&aData.front(), pu8Data, cbData);
6623 }
6624 else if (aBitmapFormat == BitmapFormat_BGRA)
6625 {
6626 /* [0] B, [1] G, [2] R, [3] A. */
6627 aData.resize(cbData);
6628 for (uint32_t i = 0; i < cbData; i += 4)
6629 {
6630 aData[i] = pu8Data[i];
6631 aData[i + 1] = pu8Data[i + 1];
6632 aData[i + 2] = pu8Data[i + 2];
6633 aData[i + 3] = 0xff;
6634 }
6635 }
6636 else if (aBitmapFormat == BitmapFormat_RGBA)
6637 {
6638 /* [0] R, [1] G, [2] B, [3] A. */
6639 aData.resize(cbData);
6640 for (uint32_t i = 0; i < cbData; i += 4)
6641 {
6642 aData[i] = pu8Data[i + 2];
6643 aData[i + 1] = pu8Data[i + 1];
6644 aData[i + 2] = pu8Data[i];
6645 aData[i + 3] = 0xff;
6646 }
6647 }
6648 else if (aBitmapFormat == BitmapFormat_PNG)
6649 {
6650 uint8_t *pu8PNG = NULL;
6651 uint32_t cbPNG = 0;
6652 uint32_t cxPNG = 0;
6653 uint32_t cyPNG = 0;
6654
6655 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6656
6657 if (RT_SUCCESS(vrc))
6658 {
6659 aData.resize(cbPNG);
6660 if (cbPNG)
6661 memcpy(&aData.front(), pu8PNG, cbPNG);
6662 }
6663 else
6664 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not convert saved thumbnail to PNG (%Rrc)"), vrc);
6665
6666 RTMemFree(pu8PNG);
6667 }
6668 }
6669
6670 freeSavedDisplayScreenshot(pu8Data);
6671
6672 return hrc;
6673}
6674
6675HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6676 ULONG *aWidth,
6677 ULONG *aHeight,
6678 std::vector<BitmapFormat_T> &aBitmapFormats)
6679{
6680 if (aScreenId != 0)
6681 return E_NOTIMPL;
6682
6683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6684
6685 uint8_t *pu8Data = NULL;
6686 uint32_t cbData = 0;
6687 uint32_t u32Width = 0;
6688 uint32_t u32Height = 0;
6689
6690#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6691 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6692#else
6693 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6694#endif
6695 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6696 &pu8Data, &cbData, &u32Width, &u32Height);
6697
6698 if (RT_FAILURE(vrc))
6699 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6700 tr("Saved screenshot data is not available (%Rrc)"),
6701 vrc);
6702
6703 *aWidth = u32Width;
6704 *aHeight = u32Height;
6705 aBitmapFormats.resize(1);
6706 aBitmapFormats[0] = BitmapFormat_PNG;
6707
6708 freeSavedDisplayScreenshot(pu8Data);
6709
6710 return S_OK;
6711}
6712
6713HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6714 BitmapFormat_T aBitmapFormat,
6715 ULONG *aWidth,
6716 ULONG *aHeight,
6717 std::vector<BYTE> &aData)
6718{
6719 if (aScreenId != 0)
6720 return E_NOTIMPL;
6721
6722 if (aBitmapFormat != BitmapFormat_PNG)
6723 return E_NOTIMPL;
6724
6725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6726
6727 uint8_t *pu8Data = NULL;
6728 uint32_t cbData = 0;
6729 uint32_t u32Width = 0;
6730 uint32_t u32Height = 0;
6731
6732#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6733 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6734#else
6735 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6736#endif
6737 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6738 &pu8Data, &cbData, &u32Width, &u32Height);
6739
6740 if (RT_FAILURE(vrc))
6741 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6742 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6743 vrc);
6744
6745 *aWidth = u32Width;
6746 *aHeight = u32Height;
6747
6748 aData.resize(cbData);
6749 if (cbData)
6750 memcpy(&aData.front(), pu8Data, cbData);
6751
6752 freeSavedDisplayScreenshot(pu8Data);
6753
6754 return S_OK;
6755}
6756
6757HRESULT Machine::hotPlugCPU(ULONG aCpu)
6758{
6759 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6760
6761 if (!mHWData->mCPUHotPlugEnabled)
6762 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6763
6764 if (aCpu >= mHWData->mCPUCount)
6765 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6766
6767 if (mHWData->mCPUAttached[aCpu])
6768 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6769
6770 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
6771 if (FAILED(hrc)) return hrc;
6772
6773 alock.release();
6774 hrc = i_onCPUChange(aCpu, false);
6775 alock.acquire();
6776 if (FAILED(hrc)) return hrc;
6777
6778 i_setModified(IsModified_MachineData);
6779 mHWData.backup();
6780 mHWData->mCPUAttached[aCpu] = true;
6781
6782 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6783 if (Global::IsOnline(mData->mMachineState))
6784 i_saveSettings(NULL, alock);
6785
6786 return S_OK;
6787}
6788
6789HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6790{
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 >= SchemaDefs::MaxCPUCount)
6797 return setError(E_INVALIDARG,
6798 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6799 SchemaDefs::MaxCPUCount);
6800
6801 if (!mHWData->mCPUAttached[aCpu])
6802 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6803
6804 /* CPU 0 can't be detached */
6805 if (aCpu == 0)
6806 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6807
6808 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
6809 if (FAILED(hrc)) return hrc;
6810
6811 alock.release();
6812 hrc = i_onCPUChange(aCpu, true);
6813 alock.acquire();
6814 if (FAILED(hrc)) return hrc;
6815
6816 i_setModified(IsModified_MachineData);
6817 mHWData.backup();
6818 mHWData->mCPUAttached[aCpu] = false;
6819
6820 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6821 if (Global::IsOnline(mData->mMachineState))
6822 i_saveSettings(NULL, alock);
6823
6824 return S_OK;
6825}
6826
6827HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6828{
6829 *aAttached = false;
6830
6831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6832
6833 /* If hotplug is enabled the CPU is always enabled. */
6834 if (!mHWData->mCPUHotPlugEnabled)
6835 {
6836 if (aCpu < mHWData->mCPUCount)
6837 *aAttached = true;
6838 }
6839 else
6840 {
6841 if (aCpu < SchemaDefs::MaxCPUCount)
6842 *aAttached = mHWData->mCPUAttached[aCpu];
6843 }
6844
6845 return S_OK;
6846}
6847
6848HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6849{
6850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6851
6852 Utf8Str log = i_getLogFilename(aIdx);
6853 if (!RTFileExists(log.c_str()))
6854 log.setNull();
6855 aFilename = log;
6856
6857 return S_OK;
6858}
6859
6860HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6861{
6862 if (aSize < 0)
6863 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6864
6865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6866
6867 HRESULT hrc = S_OK;
6868 Utf8Str log = i_getLogFilename(aIdx);
6869
6870 /* do not unnecessarily hold the lock while doing something which does
6871 * not need the lock and potentially takes a long time. */
6872 alock.release();
6873
6874 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6875 * keeps the SOAP reply size under 1M for the webservice (we're using
6876 * base64 encoded strings for binary data for years now, avoiding the
6877 * expansion of each byte array element to approx. 25 bytes of XML. */
6878 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6879 aData.resize(cbData);
6880
6881 int vrc = VINF_SUCCESS;
6882 RTVFSIOSTREAM hVfsIosLog = NIL_RTVFSIOSTREAM;
6883
6884#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6885 if (mData->mstrLogKeyId.isNotEmpty() && mData->mstrLogKeyStore.isNotEmpty())
6886 {
6887 PCVBOXCRYPTOIF pCryptoIf = NULL;
6888 hrc = i_getVirtualBox()->i_retainCryptoIf(&pCryptoIf);
6889 if (SUCCEEDED(hrc))
6890 {
6891 alock.acquire();
6892
6893 SecretKey *pKey = NULL;
6894 vrc = mData->mpKeyStore->retainSecretKey(mData->mstrLogKeyId, &pKey);
6895 alock.release();
6896
6897 if (RT_SUCCESS(vrc))
6898 {
6899 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6900 if (RT_SUCCESS(vrc))
6901 {
6902 RTVFSIOSTREAM hVfsIosLogDec = NIL_RTVFSIOSTREAM;
6903 vrc = pCryptoIf->pfnCryptoIoStrmFromVfsIoStrmDecrypt(hVfsIosLog, mData->mstrLogKeyStore.c_str(),
6904 (const char *)pKey->getKeyBuffer(), &hVfsIosLogDec);
6905 if (RT_SUCCESS(vrc))
6906 {
6907 RTVfsIoStrmRelease(hVfsIosLog);
6908 hVfsIosLog = hVfsIosLogDec;
6909 }
6910 }
6911
6912 pKey->release();
6913 }
6914
6915 i_getVirtualBox()->i_releaseCryptoIf(pCryptoIf);
6916 }
6917 }
6918 else
6919 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6920#else
6921 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6922#endif
6923 if (RT_SUCCESS(vrc))
6924 {
6925 vrc = RTVfsIoStrmReadAt(hVfsIosLog, aOffset,
6926 cbData ? &aData.front() : NULL, cbData,
6927 true /*fBlocking*/, &cbData);
6928 if (RT_SUCCESS(vrc))
6929 aData.resize(cbData);
6930 else
6931 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not read log file '%s' (%Rrc)"), log.c_str(), vrc);
6932
6933 RTVfsIoStrmRelease(hVfsIosLog);
6934 }
6935 else
6936 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not open log file '%s' (%Rrc)"), log.c_str(), vrc);
6937
6938 if (FAILED(hrc))
6939 aData.resize(0);
6940
6941 return hrc;
6942}
6943
6944
6945/**
6946 * Currently this method doesn't attach device to the running VM,
6947 * just makes sure it's plugged on next VM start.
6948 */
6949HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6950{
6951 // lock scope
6952 {
6953 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6954
6955 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6956 if (FAILED(hrc)) return hrc;
6957
6958 ChipsetType_T aChipset = ChipsetType_PIIX3;
6959 COMGETTER(ChipsetType)(&aChipset);
6960
6961 if (aChipset != ChipsetType_ICH9)
6962 {
6963 return setError(E_INVALIDARG,
6964 tr("Host PCI attachment only supported with ICH9 chipset"));
6965 }
6966
6967 // check if device with this host PCI address already attached
6968 for (HWData::PCIDeviceAssignmentList::const_iterator
6969 it = mHWData->mPCIDeviceAssignments.begin();
6970 it != mHWData->mPCIDeviceAssignments.end();
6971 ++it)
6972 {
6973 LONG iHostAddress = -1;
6974 ComPtr<PCIDeviceAttachment> pAttach;
6975 pAttach = *it;
6976 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6977 if (iHostAddress == aHostAddress)
6978 return setError(E_INVALIDARG,
6979 tr("Device with host PCI address already attached to this VM"));
6980 }
6981
6982 ComObjPtr<PCIDeviceAttachment> pda;
6983 char name[32];
6984
6985 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6986 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6987 pda.createObject();
6988 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6989 i_setModified(IsModified_MachineData);
6990 mHWData.backup();
6991 mHWData->mPCIDeviceAssignments.push_back(pda);
6992 }
6993
6994 return S_OK;
6995}
6996
6997/**
6998 * Currently this method doesn't detach device from the running VM,
6999 * just makes sure it's not plugged on next VM start.
7000 */
7001HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
7002{
7003 ComObjPtr<PCIDeviceAttachment> pAttach;
7004 bool fRemoved = false;
7005 HRESULT hrc;
7006
7007 // lock scope
7008 {
7009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7010
7011 hrc = i_checkStateDependency(MutableStateDep);
7012 if (FAILED(hrc)) return hrc;
7013
7014 for (HWData::PCIDeviceAssignmentList::const_iterator
7015 it = mHWData->mPCIDeviceAssignments.begin();
7016 it != mHWData->mPCIDeviceAssignments.end();
7017 ++it)
7018 {
7019 LONG iHostAddress = -1;
7020 pAttach = *it;
7021 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7022 if (iHostAddress != -1 && iHostAddress == aHostAddress)
7023 {
7024 i_setModified(IsModified_MachineData);
7025 mHWData.backup();
7026 mHWData->mPCIDeviceAssignments.remove(pAttach);
7027 fRemoved = true;
7028 break;
7029 }
7030 }
7031 }
7032
7033
7034 /* Fire event outside of the lock */
7035 if (fRemoved)
7036 {
7037 Assert(!pAttach.isNull());
7038 ComPtr<IEventSource> es;
7039 hrc = mParent->COMGETTER(EventSource)(es.asOutParam());
7040 Assert(SUCCEEDED(hrc));
7041 Bstr mid;
7042 hrc = this->COMGETTER(Id)(mid.asOutParam());
7043 Assert(SUCCEEDED(hrc));
7044 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7045 }
7046
7047 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7048 tr("No host PCI device %08x attached"),
7049 aHostAddress
7050 );
7051}
7052
7053HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
7054{
7055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7056
7057 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
7058 size_t i = 0;
7059 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
7060 it = mHWData->mPCIDeviceAssignments.begin();
7061 it != mHWData->mPCIDeviceAssignments.end();
7062 ++it, ++i)
7063 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
7064
7065 return S_OK;
7066}
7067
7068HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
7069{
7070 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
7071
7072 return S_OK;
7073}
7074
7075HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
7076{
7077 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7078
7079 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
7080
7081 return S_OK;
7082}
7083
7084HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
7085{
7086 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7087 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7088 if (SUCCEEDED(hrc))
7089 {
7090 hrc = mHWData.backupEx();
7091 if (SUCCEEDED(hrc))
7092 {
7093 i_setModified(IsModified_MachineData);
7094 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
7095 }
7096 }
7097 return hrc;
7098}
7099
7100HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
7101{
7102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7103 aTracingConfig = mHWData->mDebugging.strTracingConfig;
7104 return S_OK;
7105}
7106
7107HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
7108{
7109 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7110 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7111 if (SUCCEEDED(hrc))
7112 {
7113 hrc = mHWData.backupEx();
7114 if (SUCCEEDED(hrc))
7115 {
7116 mHWData->mDebugging.strTracingConfig = aTracingConfig;
7117 if (SUCCEEDED(hrc))
7118 i_setModified(IsModified_MachineData);
7119 }
7120 }
7121 return hrc;
7122}
7123
7124HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
7125{
7126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7127
7128 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
7129
7130 return S_OK;
7131}
7132
7133HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
7134{
7135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7136 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7137 if (SUCCEEDED(hrc))
7138 {
7139 hrc = mHWData.backupEx();
7140 if (SUCCEEDED(hrc))
7141 {
7142 i_setModified(IsModified_MachineData);
7143 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
7144 }
7145 }
7146 return hrc;
7147}
7148
7149HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7150{
7151 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7152
7153 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7154
7155 return S_OK;
7156}
7157
7158HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7159{
7160 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7161
7162 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7163 if ( SUCCEEDED(hrc)
7164 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7165 {
7166 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7167 int vrc;
7168
7169 if (aAutostartEnabled)
7170 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7171 else
7172 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7173
7174 if (RT_SUCCESS(vrc))
7175 {
7176 hrc = mHWData.backupEx();
7177 if (SUCCEEDED(hrc))
7178 {
7179 i_setModified(IsModified_MachineData);
7180 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7181 }
7182 }
7183 else if (vrc == VERR_NOT_SUPPORTED)
7184 hrc = setError(VBOX_E_NOT_SUPPORTED,
7185 tr("The VM autostart feature is not supported on this platform"));
7186 else if (vrc == VERR_PATH_NOT_FOUND)
7187 hrc = setError(E_FAIL,
7188 tr("The path to the autostart database is not set"));
7189 else
7190 hrc = setError(E_UNEXPECTED,
7191 aAutostartEnabled ?
7192 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
7193 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
7194 mUserData->s.strName.c_str(), vrc);
7195 }
7196 return hrc;
7197}
7198
7199HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7200{
7201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7202
7203 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7204
7205 return S_OK;
7206}
7207
7208HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7209{
7210 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7211 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7212 if (SUCCEEDED(hrc))
7213 {
7214 hrc = mHWData.backupEx();
7215 if (SUCCEEDED(hrc))
7216 {
7217 i_setModified(IsModified_MachineData);
7218 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7219 }
7220 }
7221 return hrc;
7222}
7223
7224HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7225{
7226 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7227
7228 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7229
7230 return S_OK;
7231}
7232
7233HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7234{
7235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7236 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7237 if ( SUCCEEDED(hrc)
7238 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7239 {
7240 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7241 int vrc;
7242
7243 if (aAutostopType != AutostopType_Disabled)
7244 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7245 else
7246 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7247
7248 if (RT_SUCCESS(vrc))
7249 {
7250 hrc = mHWData.backupEx();
7251 if (SUCCEEDED(hrc))
7252 {
7253 i_setModified(IsModified_MachineData);
7254 mHWData->mAutostart.enmAutostopType = aAutostopType;
7255 }
7256 }
7257 else if (vrc == VERR_NOT_SUPPORTED)
7258 hrc = setError(VBOX_E_NOT_SUPPORTED,
7259 tr("The VM autostop feature is not supported on this platform"));
7260 else if (vrc == VERR_PATH_NOT_FOUND)
7261 hrc = setError(E_FAIL,
7262 tr("The path to the autostart database is not set"));
7263 else
7264 hrc = setError(E_UNEXPECTED,
7265 aAutostopType != AutostopType_Disabled ?
7266 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
7267 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
7268 mUserData->s.strName.c_str(), vrc);
7269 }
7270 return hrc;
7271}
7272
7273HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7274{
7275 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7276
7277 aDefaultFrontend = mHWData->mDefaultFrontend;
7278
7279 return S_OK;
7280}
7281
7282HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7283{
7284 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7285 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7286 if (SUCCEEDED(hrc))
7287 {
7288 hrc = mHWData.backupEx();
7289 if (SUCCEEDED(hrc))
7290 {
7291 i_setModified(IsModified_MachineData);
7292 mHWData->mDefaultFrontend = aDefaultFrontend;
7293 }
7294 }
7295 return hrc;
7296}
7297
7298HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7299{
7300 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7301 size_t cbIcon = mUserData->s.ovIcon.size();
7302 aIcon.resize(cbIcon);
7303 if (cbIcon)
7304 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7305 return S_OK;
7306}
7307
7308HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7309{
7310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7311 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7312 if (SUCCEEDED(hrc))
7313 {
7314 i_setModified(IsModified_MachineData);
7315 mUserData.backup();
7316 size_t cbIcon = aIcon.size();
7317 mUserData->s.ovIcon.resize(cbIcon);
7318 if (cbIcon)
7319 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7320 }
7321 return hrc;
7322}
7323
7324HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7325{
7326#ifdef VBOX_WITH_USB
7327 *aUSBProxyAvailable = true;
7328#else
7329 *aUSBProxyAvailable = false;
7330#endif
7331 return S_OK;
7332}
7333
7334HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
7335{
7336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7337
7338 *aVMProcessPriority = mUserData->s.enmVMPriority;
7339
7340 return S_OK;
7341}
7342
7343HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
7344{
7345 RT_NOREF(aVMProcessPriority);
7346 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7347 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7348 if (SUCCEEDED(hrc))
7349 {
7350 hrc = mUserData.backupEx();
7351 if (SUCCEEDED(hrc))
7352 {
7353 i_setModified(IsModified_MachineData);
7354 mUserData->s.enmVMPriority = aVMProcessPriority;
7355 }
7356 }
7357 alock.release();
7358 if (SUCCEEDED(hrc))
7359 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
7360 return hrc;
7361}
7362
7363HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7364 ComPtr<IProgress> &aProgress)
7365{
7366 ComObjPtr<Progress> pP;
7367 Progress *ppP = pP;
7368 IProgress *iP = static_cast<IProgress *>(ppP);
7369 IProgress **pProgress = &iP;
7370
7371 IMachine *pTarget = aTarget;
7372
7373 /* Convert the options. */
7374 RTCList<CloneOptions_T> optList;
7375 if (aOptions.size())
7376 for (size_t i = 0; i < aOptions.size(); ++i)
7377 optList.append(aOptions[i]);
7378
7379 if (optList.contains(CloneOptions_Link))
7380 {
7381 if (!i_isSnapshotMachine())
7382 return setError(E_INVALIDARG,
7383 tr("Linked clone can only be created from a snapshot"));
7384 if (aMode != CloneMode_MachineState)
7385 return setError(E_INVALIDARG,
7386 tr("Linked clone can only be created for a single machine state"));
7387 }
7388 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7389
7390 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7391
7392 HRESULT hrc = pWorker->start(pProgress);
7393
7394 pP = static_cast<Progress *>(*pProgress);
7395 pP.queryInterfaceTo(aProgress.asOutParam());
7396
7397 return hrc;
7398
7399}
7400
7401HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7402 const com::Utf8Str &aType,
7403 ComPtr<IProgress> &aProgress)
7404{
7405 LogFlowThisFuncEnter();
7406
7407 ComObjPtr<Progress> ptrProgress;
7408 HRESULT hrc = ptrProgress.createObject();
7409 if (SUCCEEDED(hrc))
7410 {
7411 com::Utf8Str strDefaultPath;
7412 if (aTargetPath.isEmpty())
7413 i_calculateFullPath(".", strDefaultPath);
7414
7415 /* Initialize our worker task */
7416 MachineMoveVM *pTask = NULL;
7417 try
7418 {
7419 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7420 }
7421 catch (std::bad_alloc &)
7422 {
7423 return E_OUTOFMEMORY;
7424 }
7425
7426 hrc = pTask->init();//no exceptions are thrown
7427
7428 if (SUCCEEDED(hrc))
7429 {
7430 hrc = pTask->createThread();
7431 pTask = NULL; /* Consumed by createThread(). */
7432 if (SUCCEEDED(hrc))
7433 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7434 else
7435 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7436 }
7437 else
7438 delete pTask;
7439 }
7440
7441 LogFlowThisFuncLeave();
7442 return hrc;
7443
7444}
7445
7446HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7447{
7448 NOREF(aProgress);
7449 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7450
7451 // This check should always fail.
7452 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7453 if (FAILED(hrc)) return hrc;
7454
7455 AssertFailedReturn(E_NOTIMPL);
7456}
7457
7458HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7459{
7460 NOREF(aSavedStateFile);
7461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7462
7463 // This check should always fail.
7464 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7465 if (FAILED(hrc)) return hrc;
7466
7467 AssertFailedReturn(E_NOTIMPL);
7468}
7469
7470HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7471{
7472 NOREF(aFRemoveFile);
7473 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7474
7475 // This check should always fail.
7476 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7477 if (FAILED(hrc)) return hrc;
7478
7479 AssertFailedReturn(E_NOTIMPL);
7480}
7481
7482// public methods for internal purposes
7483/////////////////////////////////////////////////////////////////////////////
7484
7485/**
7486 * Adds the given IsModified_* flag to the dirty flags of the machine.
7487 * This must be called either during i_loadSettings or under the machine write lock.
7488 * @param fl Flag
7489 * @param fAllowStateModification If state modifications are allowed.
7490 */
7491void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7492{
7493 mData->flModifications |= fl;
7494 if (fAllowStateModification && i_isStateModificationAllowed())
7495 mData->mCurrentStateModified = true;
7496}
7497
7498/**
7499 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7500 * care of the write locking.
7501 *
7502 * @param fModification The flag to add.
7503 * @param fAllowStateModification If state modifications are allowed.
7504 */
7505void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7506{
7507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7508 i_setModified(fModification, fAllowStateModification);
7509}
7510
7511/**
7512 * Saves the registry entry of this machine to the given configuration node.
7513 *
7514 * @param data Machine registry data.
7515 *
7516 * @note locks this object for reading.
7517 */
7518HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7519{
7520 AutoLimitedCaller autoCaller(this);
7521 AssertComRCReturnRC(autoCaller.hrc());
7522
7523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7524
7525 data.uuid = mData->mUuid;
7526 data.strSettingsFile = mData->m_strConfigFile;
7527
7528 return S_OK;
7529}
7530
7531/**
7532 * Calculates the absolute path of the given path taking the directory of the
7533 * machine settings file as the current directory.
7534 *
7535 * @param strPath Path to calculate the absolute path for.
7536 * @param aResult Where to put the result (used only on success, can be the
7537 * same Utf8Str instance as passed in @a aPath).
7538 * @return IPRT result.
7539 *
7540 * @note Locks this object for reading.
7541 */
7542int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7543{
7544 AutoCaller autoCaller(this);
7545 AssertComRCReturn(autoCaller.hrc(), Global::vboxStatusCodeFromCOM(autoCaller.hrc()));
7546
7547 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7548
7549 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7550
7551 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7552
7553 strSettingsDir.stripFilename();
7554 char szFolder[RTPATH_MAX];
7555 size_t cbFolder = sizeof(szFolder);
7556 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7557 if (RT_SUCCESS(vrc))
7558 aResult = szFolder;
7559
7560 return vrc;
7561}
7562
7563/**
7564 * Copies strSource to strTarget, making it relative to the machine folder
7565 * if it is a subdirectory thereof, or simply copying it otherwise.
7566 *
7567 * @param strSource Path to evaluate and copy.
7568 * @param strTarget Buffer to receive target path.
7569 *
7570 * @note Locks this object for reading.
7571 */
7572void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7573 Utf8Str &strTarget)
7574{
7575 AutoCaller autoCaller(this);
7576 AssertComRCReturn(autoCaller.hrc(), (void)0);
7577
7578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7579
7580 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7581 // use strTarget as a temporary buffer to hold the machine settings dir
7582 strTarget = mData->m_strConfigFileFull;
7583 strTarget.stripFilename();
7584 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7585 {
7586 // is relative: then append what's left
7587 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7588 // for empty paths (only possible for subdirs) use "." to avoid
7589 // triggering default settings for not present config attributes.
7590 if (strTarget.isEmpty())
7591 strTarget = ".";
7592 }
7593 else
7594 // is not relative: then overwrite
7595 strTarget = strSource;
7596}
7597
7598/**
7599 * Returns the full path to the machine's log folder in the
7600 * \a aLogFolder argument.
7601 */
7602void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7603{
7604 AutoCaller autoCaller(this);
7605 AssertComRCReturnVoid(autoCaller.hrc());
7606
7607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7608
7609 char szTmp[RTPATH_MAX];
7610 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7611 if (RT_SUCCESS(vrc))
7612 {
7613 if (szTmp[0] && !mUserData.isNull())
7614 {
7615 char szTmp2[RTPATH_MAX];
7616 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7617 if (RT_SUCCESS(vrc))
7618 aLogFolder.printf("%s%c%s",
7619 szTmp2,
7620 RTPATH_DELIMITER,
7621 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7622 }
7623 else
7624 vrc = VERR_PATH_IS_RELATIVE;
7625 }
7626
7627 if (RT_FAILURE(vrc))
7628 {
7629 // fallback if VBOX_USER_LOGHOME is not set or invalid
7630 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7631 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7632 aLogFolder.append(RTPATH_DELIMITER);
7633 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7634 }
7635}
7636
7637/**
7638 * Returns the full path to the machine's log file for an given index.
7639 */
7640Utf8Str Machine::i_getLogFilename(ULONG idx)
7641{
7642 Utf8Str logFolder;
7643 getLogFolder(logFolder);
7644 Assert(logFolder.length());
7645
7646 Utf8Str log;
7647 if (idx == 0)
7648 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7649#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7650 else if (idx == 1)
7651 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7652 else
7653 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7654#else
7655 else
7656 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7657#endif
7658 return log;
7659}
7660
7661/**
7662 * Returns the full path to the machine's hardened log file.
7663 */
7664Utf8Str Machine::i_getHardeningLogFilename(void)
7665{
7666 Utf8Str strFilename;
7667 getLogFolder(strFilename);
7668 Assert(strFilename.length());
7669 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7670 return strFilename;
7671}
7672
7673/**
7674 * Returns the default NVRAM filename based on the location of the VM config.
7675 * Note that this is a relative path.
7676 */
7677Utf8Str Machine::i_getDefaultNVRAMFilename()
7678{
7679 AutoCaller autoCaller(this);
7680 AssertComRCReturn(autoCaller.hrc(), Utf8Str::Empty);
7681
7682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7683
7684 if (i_isSnapshotMachine())
7685 return Utf8Str::Empty;
7686
7687 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7688 strNVRAMFilePath.stripPath();
7689 strNVRAMFilePath.stripSuffix();
7690 strNVRAMFilePath += ".nvram";
7691
7692 return strNVRAMFilePath;
7693}
7694
7695/**
7696 * Returns the NVRAM filename for a new snapshot. This intentionally works
7697 * similarly to the saved state file naming. Note that this is usually
7698 * a relative path, unless the snapshot folder is absolute.
7699 */
7700Utf8Str Machine::i_getSnapshotNVRAMFilename()
7701{
7702 AutoCaller autoCaller(this);
7703 AssertComRCReturn(autoCaller.hrc(), Utf8Str::Empty);
7704
7705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7706
7707 RTTIMESPEC ts;
7708 RTTimeNow(&ts);
7709 RTTIME time;
7710 RTTimeExplode(&time, &ts);
7711
7712 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7713 strNVRAMFilePath += RTPATH_DELIMITER;
7714 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7715 time.i32Year, time.u8Month, time.u8MonthDay,
7716 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7717
7718 return strNVRAMFilePath;
7719}
7720
7721/**
7722 * Returns the version of the settings file.
7723 */
7724SettingsVersion_T Machine::i_getSettingsVersion(void)
7725{
7726 AutoCaller autoCaller(this);
7727 AssertComRCReturn(autoCaller.hrc(), SettingsVersion_Null);
7728
7729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7730
7731 return mData->pMachineConfigFile->getSettingsVersion();
7732}
7733
7734/**
7735 * Composes a unique saved state filename based on the current system time. The filename is
7736 * granular to the second so this will work so long as no more than one snapshot is taken on
7737 * a machine per second.
7738 *
7739 * Before version 4.1, we used this formula for saved state files:
7740 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7741 * which no longer works because saved state files can now be shared between the saved state of the
7742 * "saved" machine and an online snapshot, and the following would cause problems:
7743 * 1) save machine
7744 * 2) create online snapshot from that machine state --> reusing saved state file
7745 * 3) save machine again --> filename would be reused, breaking the online snapshot
7746 *
7747 * So instead we now use a timestamp.
7748 *
7749 * @param strStateFilePath
7750 */
7751
7752void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7753{
7754 AutoCaller autoCaller(this);
7755 AssertComRCReturnVoid(autoCaller.hrc());
7756
7757 {
7758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7759 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7760 }
7761
7762 RTTIMESPEC ts;
7763 RTTimeNow(&ts);
7764 RTTIME time;
7765 RTTimeExplode(&time, &ts);
7766
7767 strStateFilePath += RTPATH_DELIMITER;
7768 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7769 time.i32Year, time.u8Month, time.u8MonthDay,
7770 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7771}
7772
7773/**
7774 * Returns whether at least one USB controller is present for the VM.
7775 */
7776bool Machine::i_isUSBControllerPresent()
7777{
7778 AutoCaller autoCaller(this);
7779 AssertComRCReturn(autoCaller.hrc(), false);
7780
7781 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7782
7783 return (mUSBControllers->size() > 0);
7784}
7785
7786
7787/**
7788 * @note Locks this object for writing, calls the client process
7789 * (inside the lock).
7790 */
7791HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7792 const Utf8Str &strFrontend,
7793 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7794 ProgressProxy *aProgress)
7795{
7796 LogFlowThisFuncEnter();
7797
7798 AssertReturn(aControl, E_FAIL);
7799 AssertReturn(aProgress, E_FAIL);
7800 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7801
7802 AutoCaller autoCaller(this);
7803 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
7804
7805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7806
7807 if (!mData->mRegistered)
7808 return setError(E_UNEXPECTED,
7809 tr("The machine '%s' is not registered"),
7810 mUserData->s.strName.c_str());
7811
7812 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7813
7814 /* The process started when launching a VM with separate UI/VM processes is always
7815 * the UI process, i.e. needs special handling as it won't claim the session. */
7816 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7817
7818 if (fSeparate)
7819 {
7820 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7821 return setError(VBOX_E_INVALID_OBJECT_STATE,
7822 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7823 mUserData->s.strName.c_str());
7824 }
7825 else
7826 {
7827 if ( mData->mSession.mState == SessionState_Locked
7828 || mData->mSession.mState == SessionState_Spawning
7829 || mData->mSession.mState == SessionState_Unlocking)
7830 return setError(VBOX_E_INVALID_OBJECT_STATE,
7831 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7832 mUserData->s.strName.c_str());
7833
7834 /* may not be busy */
7835 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7836 }
7837
7838 /* Hardening logging */
7839#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7840 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7841 {
7842 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7843 int vrc2 = VERR_IPE_UNINITIALIZED_STATUS;
7844 i_deleteFile(strHardeningLogFile, false /* fIgnoreFailures */, tr("hardening log file"), &vrc2); /* ignoring return code */
7845 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7846 {
7847 Utf8Str strStartupLogDir = strHardeningLogFile;
7848 strStartupLogDir.stripFilename();
7849 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7850 file without stripping the file. */
7851 }
7852 strSupHardeningLogArg.append(strHardeningLogFile);
7853
7854 /* Remove legacy log filename to avoid confusion. */
7855 Utf8Str strOldStartupLogFile;
7856 getLogFolder(strOldStartupLogFile);
7857 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7858 i_deleteFile(strOldStartupLogFile, true /* fIgnoreFailures */);
7859 }
7860#else
7861 Utf8Str strSupHardeningLogArg;
7862#endif
7863
7864 Utf8Str strAppOverride;
7865#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7866 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7867#endif
7868
7869 bool fUseVBoxSDS = false;
7870 Utf8Str strCanonicalName;
7871 if (false)
7872 { }
7873#ifdef VBOX_WITH_QTGUI
7874 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7875 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7876 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7877 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7878 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7879 {
7880 strCanonicalName = "GUI/Qt";
7881 fUseVBoxSDS = true;
7882 }
7883#endif
7884#ifdef VBOX_WITH_VBOXSDL
7885 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7886 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7887 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7888 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7889 {
7890 strCanonicalName = "GUI/SDL";
7891 fUseVBoxSDS = true;
7892 }
7893#endif
7894#ifdef VBOX_WITH_HEADLESS
7895 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7896 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7897 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7898 {
7899 strCanonicalName = "headless";
7900 }
7901#endif
7902 else
7903 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7904
7905 Utf8Str idStr = mData->mUuid.toString();
7906 Utf8Str const &strMachineName = mUserData->s.strName;
7907 RTPROCESS pid = NIL_RTPROCESS;
7908
7909#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7910 RT_NOREF(fUseVBoxSDS);
7911#else
7912 DWORD idCallerSession = ~(DWORD)0;
7913 if (fUseVBoxSDS)
7914 {
7915 /*
7916 * The VBoxSDS should be used for process launching the VM with
7917 * GUI only if the caller and the VBoxSDS are in different Windows
7918 * sessions and the caller in the interactive one.
7919 */
7920 fUseVBoxSDS = false;
7921
7922 /* Get windows session of the current process. The process token used
7923 due to several reasons:
7924 1. The token is absent for the current thread except someone set it
7925 for us.
7926 2. Needs to get the id of the session where the process is started.
7927 We only need to do this once, though. */
7928 static DWORD s_idCurrentSession = ~(DWORD)0;
7929 DWORD idCurrentSession = s_idCurrentSession;
7930 if (idCurrentSession == ~(DWORD)0)
7931 {
7932 HANDLE hCurrentProcessToken = NULL;
7933 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7934 {
7935 DWORD cbIgn = 0;
7936 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7937 s_idCurrentSession = idCurrentSession;
7938 else
7939 {
7940 idCurrentSession = ~(DWORD)0;
7941 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7942 }
7943 CloseHandle(hCurrentProcessToken);
7944 }
7945 else
7946 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7947 }
7948
7949 /* get the caller's session */
7950 HRESULT hrc = CoImpersonateClient();
7951 if (SUCCEEDED(hrc))
7952 {
7953 HANDLE hCallerThreadToken;
7954 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7955 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7956 &hCallerThreadToken))
7957 {
7958 SetLastError(NO_ERROR);
7959 DWORD cbIgn = 0;
7960 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7961 {
7962 /* Only need to use SDS if the session ID differs: */
7963 if (idCurrentSession != idCallerSession)
7964 {
7965 fUseVBoxSDS = false;
7966
7967 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7968 DWORD cbTokenGroups = 0;
7969 PTOKEN_GROUPS pTokenGroups = NULL;
7970 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7971 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7972 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7973 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7974 {
7975 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7976 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7977 PSID pInteractiveSid = NULL;
7978 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7979 {
7980 /* Iterate over the groups looking for the interactive SID: */
7981 fUseVBoxSDS = false;
7982 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7983 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7984 {
7985 fUseVBoxSDS = true;
7986 break;
7987 }
7988 FreeSid(pInteractiveSid);
7989 }
7990 }
7991 else
7992 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7993 RTMemTmpFree(pTokenGroups);
7994 }
7995 }
7996 else
7997 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7998 CloseHandle(hCallerThreadToken);
7999 }
8000 else
8001 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
8002 CoRevertToSelf();
8003 }
8004 else
8005 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
8006 }
8007 if (fUseVBoxSDS)
8008 {
8009 /* connect to VBoxSDS */
8010 ComPtr<IVirtualBoxSDS> pVBoxSDS;
8011 HRESULT hrc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
8012 if (FAILED(hrc))
8013 return setError(hrc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
8014 strMachineName.c_str());
8015
8016 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
8017 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
8018 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
8019 service to access the files. */
8020 hrc = CoSetProxyBlanket(pVBoxSDS,
8021 RPC_C_AUTHN_DEFAULT,
8022 RPC_C_AUTHZ_DEFAULT,
8023 COLE_DEFAULT_PRINCIPAL,
8024 RPC_C_AUTHN_LEVEL_DEFAULT,
8025 RPC_C_IMP_LEVEL_IMPERSONATE,
8026 NULL,
8027 EOAC_DEFAULT);
8028 if (FAILED(hrc))
8029 return setError(hrc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
8030
8031 size_t const cEnvVars = aEnvironmentChanges.size();
8032 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
8033 for (size_t i = 0; i < cEnvVars; i++)
8034 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
8035
8036 ULONG uPid = 0;
8037 hrc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
8038 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
8039 idCallerSession, &uPid);
8040 if (FAILED(hrc))
8041 return setError(hrc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
8042 pid = (RTPROCESS)uPid;
8043 }
8044 else
8045#endif /* VBOX_WITH_SDS && RT_OS_WINDOWS */
8046 {
8047 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
8048 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
8049 if (RT_FAILURE(vrc))
8050 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
8051 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
8052 }
8053
8054 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
8055 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
8056
8057 if (!fSeparate)
8058 {
8059 /*
8060 * Note that we don't release the lock here before calling the client,
8061 * because it doesn't need to call us back if called with a NULL argument.
8062 * Releasing the lock here is dangerous because we didn't prepare the
8063 * launch data yet, but the client we've just started may happen to be
8064 * too fast and call LockMachine() that will fail (because of PID, etc.),
8065 * so that the Machine will never get out of the Spawning session state.
8066 */
8067
8068 /* inform the session that it will be a remote one */
8069 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8070#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8071 HRESULT hrc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8072#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8073 HRESULT hrc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8074#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8075 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", hrc));
8076
8077 if (FAILED(hrc))
8078 {
8079 /* restore the session state */
8080 mData->mSession.mState = SessionState_Unlocked;
8081 alock.release();
8082 mParent->i_addProcessToReap(pid);
8083 /* The failure may occur w/o any error info (from RPC), so provide one */
8084 return setError(VBOX_E_VM_ERROR,
8085 tr("Failed to assign the machine to the session (%Rhrc)"), hrc);
8086 }
8087
8088 /* attach launch data to the machine */
8089 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8090 mData->mSession.mRemoteControls.push_back(aControl);
8091 mData->mSession.mProgress = aProgress;
8092 mData->mSession.mPID = pid;
8093 mData->mSession.mState = SessionState_Spawning;
8094 Assert(strCanonicalName.isNotEmpty());
8095 mData->mSession.mName = strCanonicalName;
8096 }
8097 else
8098 {
8099 /* For separate UI process we declare the launch as completed instantly, as the
8100 * actual headless VM start may or may not come. No point in remembering anything
8101 * yet, as what matters for us is when the headless VM gets started. */
8102 aProgress->i_notifyComplete(S_OK);
8103 }
8104
8105 alock.release();
8106 mParent->i_addProcessToReap(pid);
8107
8108 LogFlowThisFuncLeave();
8109 return S_OK;
8110}
8111
8112/**
8113 * Returns @c true if the given session machine instance has an open direct
8114 * session (and optionally also for direct sessions which are closing) and
8115 * returns the session control machine instance if so.
8116 *
8117 * Note that when the method returns @c false, the arguments remain unchanged.
8118 *
8119 * @param aMachine Session machine object.
8120 * @param aControl Direct session control object (optional).
8121 * @param aRequireVM If true then only allow VM sessions.
8122 * @param aAllowClosing If true then additionally a session which is currently
8123 * being closed will also be allowed.
8124 *
8125 * @note locks this object for reading.
8126 */
8127bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8128 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8129 bool aRequireVM /*= false*/,
8130 bool aAllowClosing /*= false*/)
8131{
8132 AutoLimitedCaller autoCaller(this);
8133 AssertComRCReturn(autoCaller.hrc(), false);
8134
8135 /* just return false for inaccessible machines */
8136 if (getObjectState().getState() != ObjectState::Ready)
8137 return false;
8138
8139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8140
8141 if ( ( mData->mSession.mState == SessionState_Locked
8142 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
8143 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8144 )
8145 {
8146 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8147
8148 aMachine = mData->mSession.mMachine;
8149
8150 if (aControl != NULL)
8151 *aControl = mData->mSession.mDirectControl;
8152
8153 return true;
8154 }
8155
8156 return false;
8157}
8158
8159/**
8160 * Returns @c true if the given machine has an spawning direct session.
8161 *
8162 * @note locks this object for reading.
8163 */
8164bool Machine::i_isSessionSpawning()
8165{
8166 AutoLimitedCaller autoCaller(this);
8167 AssertComRCReturn(autoCaller.hrc(), false);
8168
8169 /* just return false for inaccessible machines */
8170 if (getObjectState().getState() != ObjectState::Ready)
8171 return false;
8172
8173 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8174
8175 if (mData->mSession.mState == SessionState_Spawning)
8176 return true;
8177
8178 return false;
8179}
8180
8181/**
8182 * Called from the client watcher thread to check for unexpected client process
8183 * death during Session_Spawning state (e.g. before it successfully opened a
8184 * direct session).
8185 *
8186 * On Win32 and on OS/2, this method is called only when we've got the
8187 * direct client's process termination notification, so it always returns @c
8188 * true.
8189 *
8190 * On other platforms, this method returns @c true if the client process is
8191 * terminated and @c false if it's still alive.
8192 *
8193 * @note Locks this object for writing.
8194 */
8195bool Machine::i_checkForSpawnFailure()
8196{
8197 AutoCaller autoCaller(this);
8198 if (!autoCaller.isOk())
8199 {
8200 /* nothing to do */
8201 LogFlowThisFunc(("Already uninitialized!\n"));
8202 return true;
8203 }
8204
8205 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8206
8207 if (mData->mSession.mState != SessionState_Spawning)
8208 {
8209 /* nothing to do */
8210 LogFlowThisFunc(("Not spawning any more!\n"));
8211 return true;
8212 }
8213
8214 /* PID not yet initialized, skip check. */
8215 if (mData->mSession.mPID == NIL_RTPROCESS)
8216 return false;
8217
8218 HRESULT hrc = S_OK;
8219 RTPROCSTATUS status;
8220 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8221 if (vrc != VERR_PROCESS_RUNNING)
8222 {
8223 Utf8Str strExtraInfo;
8224
8225#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8226 /* If the startup logfile exists and is of non-zero length, tell the
8227 user to look there for more details to encourage them to attach it
8228 when reporting startup issues. */
8229 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8230 uint64_t cbStartupLogFile = 0;
8231 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
8232 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8233 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
8234#endif
8235
8236 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8237 hrc = setError(E_FAIL,
8238 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8239 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8240 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8241 hrc = setError(E_FAIL,
8242 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8243 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8244 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8245 hrc = setError(E_FAIL,
8246 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8247 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8248 else
8249 hrc = setErrorBoth(E_FAIL, vrc,
8250 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8251 i_getName().c_str(), vrc, strExtraInfo.c_str());
8252 }
8253
8254 if (FAILED(hrc))
8255 {
8256 /* Close the remote session, remove the remote control from the list
8257 * and reset session state to Closed (@note keep the code in sync with
8258 * the relevant part in LockMachine()). */
8259
8260 Assert(mData->mSession.mRemoteControls.size() == 1);
8261 if (mData->mSession.mRemoteControls.size() == 1)
8262 {
8263 ErrorInfoKeeper eik;
8264 mData->mSession.mRemoteControls.front()->Uninitialize();
8265 }
8266
8267 mData->mSession.mRemoteControls.clear();
8268 mData->mSession.mState = SessionState_Unlocked;
8269
8270 /* finalize the progress after setting the state */
8271 if (!mData->mSession.mProgress.isNull())
8272 {
8273 mData->mSession.mProgress->notifyComplete(hrc);
8274 mData->mSession.mProgress.setNull();
8275 }
8276
8277 mData->mSession.mPID = NIL_RTPROCESS;
8278
8279 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
8280 return true;
8281 }
8282
8283 return false;
8284}
8285
8286/**
8287 * Checks whether the machine can be registered. If so, commits and saves
8288 * all settings.
8289 *
8290 * @note Must be called from mParent's write lock. Locks this object and
8291 * children for writing.
8292 */
8293HRESULT Machine::i_prepareRegister()
8294{
8295 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8296
8297 AutoLimitedCaller autoCaller(this);
8298 AssertComRCReturnRC(autoCaller.hrc());
8299
8300 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8301
8302 /* wait for state dependents to drop to zero */
8303 i_ensureNoStateDependencies(alock);
8304
8305 if (!mData->mAccessible)
8306 return setError(VBOX_E_INVALID_OBJECT_STATE,
8307 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8308 mUserData->s.strName.c_str(),
8309 mData->mUuid.toString().c_str());
8310
8311 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8312
8313 if (mData->mRegistered)
8314 return setError(VBOX_E_INVALID_OBJECT_STATE,
8315 tr("The machine '%s' with UUID {%s} is already registered"),
8316 mUserData->s.strName.c_str(),
8317 mData->mUuid.toString().c_str());
8318
8319 HRESULT hrc = S_OK;
8320
8321 // Ensure the settings are saved. If we are going to be registered and
8322 // no config file exists yet, create it by calling i_saveSettings() too.
8323 if ( (mData->flModifications)
8324 || (!mData->pMachineConfigFile->fileExists())
8325 )
8326 {
8327 hrc = i_saveSettings(NULL, alock);
8328 // no need to check whether VirtualBox.xml needs saving too since
8329 // we can't have a machine XML file rename pending
8330 if (FAILED(hrc)) return hrc;
8331 }
8332
8333 /* more config checking goes here */
8334
8335 if (SUCCEEDED(hrc))
8336 {
8337 /* we may have had implicit modifications we want to fix on success */
8338 i_commit();
8339
8340 mData->mRegistered = true;
8341 }
8342 else
8343 {
8344 /* we may have had implicit modifications we want to cancel on failure*/
8345 i_rollback(false /* aNotify */);
8346 }
8347
8348 return hrc;
8349}
8350
8351/**
8352 * Increases the number of objects dependent on the machine state or on the
8353 * registered state. Guarantees that these two states will not change at least
8354 * until #i_releaseStateDependency() is called.
8355 *
8356 * Depending on the @a aDepType value, additional state checks may be made.
8357 * These checks will set extended error info on failure. See
8358 * #i_checkStateDependency() for more info.
8359 *
8360 * If this method returns a failure, the dependency is not added and the caller
8361 * is not allowed to rely on any particular machine state or registration state
8362 * value and may return the failed result code to the upper level.
8363 *
8364 * @param aDepType Dependency type to add.
8365 * @param aState Current machine state (NULL if not interested).
8366 * @param aRegistered Current registered state (NULL if not interested).
8367 *
8368 * @note Locks this object for writing.
8369 */
8370HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8371 MachineState_T *aState /* = NULL */,
8372 BOOL *aRegistered /* = NULL */)
8373{
8374 AutoCaller autoCaller(this);
8375 AssertComRCReturnRC(autoCaller.hrc());
8376
8377 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8378
8379 HRESULT hrc = i_checkStateDependency(aDepType);
8380 if (FAILED(hrc)) return hrc;
8381
8382 {
8383 if (mData->mMachineStateChangePending != 0)
8384 {
8385 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8386 * drop to zero so don't add more. It may make sense to wait a bit
8387 * and retry before reporting an error (since the pending state
8388 * transition should be really quick) but let's just assert for
8389 * now to see if it ever happens on practice. */
8390
8391 AssertFailed();
8392
8393 return setError(E_ACCESSDENIED,
8394 tr("Machine state change is in progress. Please retry the operation later."));
8395 }
8396
8397 ++mData->mMachineStateDeps;
8398 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8399 }
8400
8401 if (aState)
8402 *aState = mData->mMachineState;
8403 if (aRegistered)
8404 *aRegistered = mData->mRegistered;
8405
8406 return S_OK;
8407}
8408
8409/**
8410 * Decreases the number of objects dependent on the machine state.
8411 * Must always complete the #i_addStateDependency() call after the state
8412 * dependency is no more necessary.
8413 */
8414void Machine::i_releaseStateDependency()
8415{
8416 AutoCaller autoCaller(this);
8417 AssertComRCReturnVoid(autoCaller.hrc());
8418
8419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8420
8421 /* releaseStateDependency() w/o addStateDependency()? */
8422 AssertReturnVoid(mData->mMachineStateDeps != 0);
8423 -- mData->mMachineStateDeps;
8424
8425 if (mData->mMachineStateDeps == 0)
8426 {
8427 /* inform i_ensureNoStateDependencies() that there are no more deps */
8428 if (mData->mMachineStateChangePending != 0)
8429 {
8430 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8431 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8432 }
8433 }
8434}
8435
8436Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8437{
8438 /* start with nothing found */
8439 Utf8Str strResult("");
8440
8441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8442
8443 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8444 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8445 // found:
8446 strResult = it->second; // source is a Utf8Str
8447
8448 return strResult;
8449}
8450
8451// protected methods
8452/////////////////////////////////////////////////////////////////////////////
8453
8454/**
8455 * Performs machine state checks based on the @a aDepType value. If a check
8456 * fails, this method will set extended error info, otherwise it will return
8457 * S_OK. It is supposed, that on failure, the caller will immediately return
8458 * the return value of this method to the upper level.
8459 *
8460 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8461 *
8462 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8463 * current state of this machine object allows to change settings of the
8464 * machine (i.e. the machine is not registered, or registered but not running
8465 * and not saved). It is useful to call this method from Machine setters
8466 * before performing any change.
8467 *
8468 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8469 * as for MutableStateDep except that if the machine is saved, S_OK is also
8470 * returned. This is useful in setters which allow changing machine
8471 * properties when it is in the saved state.
8472 *
8473 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8474 * if the current state of this machine object allows to change runtime
8475 * changeable settings of the machine (i.e. the machine is not registered, or
8476 * registered but either running or not running and not saved). It is useful
8477 * to call this method from Machine setters before performing any changes to
8478 * runtime changeable settings.
8479 *
8480 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8481 * the same as for MutableOrRunningStateDep except that if the machine is
8482 * saved, S_OK is also returned. This is useful in setters which allow
8483 * changing runtime and saved state changeable machine properties.
8484 *
8485 * @param aDepType Dependency type to check.
8486 *
8487 * @note Non Machine based classes should use #i_addStateDependency() and
8488 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8489 * template.
8490 *
8491 * @note This method must be called from under this object's read or write
8492 * lock.
8493 */
8494HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8495{
8496 switch (aDepType)
8497 {
8498 case AnyStateDep:
8499 {
8500 break;
8501 }
8502 case MutableStateDep:
8503 {
8504 if ( mData->mRegistered
8505 && ( !i_isSessionMachine()
8506 || ( mData->mMachineState != MachineState_Aborted
8507 && mData->mMachineState != MachineState_Teleported
8508 && mData->mMachineState != MachineState_PoweredOff
8509 )
8510 )
8511 )
8512 return setError(VBOX_E_INVALID_VM_STATE,
8513 tr("The machine is not mutable (state is %s)"),
8514 Global::stringifyMachineState(mData->mMachineState));
8515 break;
8516 }
8517 case MutableOrSavedStateDep:
8518 {
8519 if ( mData->mRegistered
8520 && ( !i_isSessionMachine()
8521 || ( mData->mMachineState != MachineState_Aborted
8522 && mData->mMachineState != MachineState_Teleported
8523 && mData->mMachineState != MachineState_Saved
8524 && mData->mMachineState != MachineState_AbortedSaved
8525 && mData->mMachineState != MachineState_PoweredOff
8526 )
8527 )
8528 )
8529 return setError(VBOX_E_INVALID_VM_STATE,
8530 tr("The machine is not mutable or saved (state is %s)"),
8531 Global::stringifyMachineState(mData->mMachineState));
8532 break;
8533 }
8534 case MutableOrRunningStateDep:
8535 {
8536 if ( mData->mRegistered
8537 && ( !i_isSessionMachine()
8538 || ( mData->mMachineState != MachineState_Aborted
8539 && mData->mMachineState != MachineState_Teleported
8540 && mData->mMachineState != MachineState_PoweredOff
8541 && !Global::IsOnline(mData->mMachineState)
8542 )
8543 )
8544 )
8545 return setError(VBOX_E_INVALID_VM_STATE,
8546 tr("The machine is not mutable or running (state is %s)"),
8547 Global::stringifyMachineState(mData->mMachineState));
8548 break;
8549 }
8550 case MutableOrSavedOrRunningStateDep:
8551 {
8552 if ( mData->mRegistered
8553 && ( !i_isSessionMachine()
8554 || ( mData->mMachineState != MachineState_Aborted
8555 && mData->mMachineState != MachineState_Teleported
8556 && mData->mMachineState != MachineState_Saved
8557 && mData->mMachineState != MachineState_AbortedSaved
8558 && mData->mMachineState != MachineState_PoweredOff
8559 && !Global::IsOnline(mData->mMachineState)
8560 )
8561 )
8562 )
8563 return setError(VBOX_E_INVALID_VM_STATE,
8564 tr("The machine is not mutable, saved or running (state is %s)"),
8565 Global::stringifyMachineState(mData->mMachineState));
8566 break;
8567 }
8568 }
8569
8570 return S_OK;
8571}
8572
8573/**
8574 * Helper to initialize all associated child objects and allocate data
8575 * structures.
8576 *
8577 * This method must be called as a part of the object's initialization procedure
8578 * (usually done in the #init() method).
8579 *
8580 * @note Must be called only from #init() or from #i_registeredInit().
8581 */
8582HRESULT Machine::initDataAndChildObjects()
8583{
8584 AutoCaller autoCaller(this);
8585 AssertComRCReturnRC(autoCaller.hrc());
8586 AssertReturn( getObjectState().getState() == ObjectState::InInit
8587 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8588
8589 AssertReturn(!mData->mAccessible, E_FAIL);
8590
8591 /* allocate data structures */
8592 mSSData.allocate();
8593 mUserData.allocate();
8594 mHWData.allocate();
8595 mMediumAttachments.allocate();
8596 mStorageControllers.allocate();
8597 mUSBControllers.allocate();
8598
8599 /* initialize mOSTypeId */
8600 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8601
8602/** @todo r=bird: init() methods never fails, right? Why don't we make them
8603 * return void then! */
8604
8605 /* create associated BIOS settings object */
8606 unconst(mBIOSSettings).createObject();
8607 mBIOSSettings->init(this);
8608
8609 /* create associated recording settings object */
8610 unconst(mRecordingSettings).createObject();
8611 mRecordingSettings->init(this);
8612
8613 /* create associated trusted platform module object */
8614 unconst(mTrustedPlatformModule).createObject();
8615 mTrustedPlatformModule->init(this);
8616
8617 /* create associated NVRAM store object */
8618 unconst(mNvramStore).createObject();
8619 mNvramStore->init(this);
8620
8621 /* create the graphics adapter object (always present) */
8622 unconst(mGraphicsAdapter).createObject();
8623 mGraphicsAdapter->init(this);
8624
8625 /* create an associated VRDE object (default is disabled) */
8626 unconst(mVRDEServer).createObject();
8627 mVRDEServer->init(this);
8628
8629 /* create associated serial port objects */
8630 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8631 {
8632 unconst(mSerialPorts[slot]).createObject();
8633 mSerialPorts[slot]->init(this, slot);
8634 }
8635
8636 /* create associated parallel port objects */
8637 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8638 {
8639 unconst(mParallelPorts[slot]).createObject();
8640 mParallelPorts[slot]->init(this, slot);
8641 }
8642
8643 /* create the audio settings object */
8644 unconst(mAudioSettings).createObject();
8645 mAudioSettings->init(this);
8646
8647 /* create the USB device filters object (always present) */
8648 unconst(mUSBDeviceFilters).createObject();
8649 mUSBDeviceFilters->init(this);
8650
8651 /* create associated network adapter objects */
8652 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8653 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8654 {
8655 unconst(mNetworkAdapters[slot]).createObject();
8656 mNetworkAdapters[slot]->init(this, slot);
8657 }
8658
8659 /* create the bandwidth control */
8660 unconst(mBandwidthControl).createObject();
8661 mBandwidthControl->init(this);
8662
8663 /* create the guest debug control object */
8664 unconst(mGuestDebugControl).createObject();
8665 mGuestDebugControl->init(this);
8666
8667 return S_OK;
8668}
8669
8670/**
8671 * Helper to uninitialize all associated child objects and to free all data
8672 * structures.
8673 *
8674 * This method must be called as a part of the object's uninitialization
8675 * procedure (usually done in the #uninit() method).
8676 *
8677 * @note Must be called only from #uninit() or from #i_registeredInit().
8678 */
8679void Machine::uninitDataAndChildObjects()
8680{
8681 AutoCaller autoCaller(this);
8682 AssertComRCReturnVoid(autoCaller.hrc());
8683 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8684 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8685 || getObjectState().getState() == ObjectState::InUninit
8686 || getObjectState().getState() == ObjectState::Limited);
8687
8688 /* tell all our other child objects we've been uninitialized */
8689 if (mGuestDebugControl)
8690 {
8691 mGuestDebugControl->uninit();
8692 unconst(mGuestDebugControl).setNull();
8693 }
8694
8695 if (mBandwidthControl)
8696 {
8697 mBandwidthControl->uninit();
8698 unconst(mBandwidthControl).setNull();
8699 }
8700
8701 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8702 {
8703 if (mNetworkAdapters[slot])
8704 {
8705 mNetworkAdapters[slot]->uninit();
8706 unconst(mNetworkAdapters[slot]).setNull();
8707 }
8708 }
8709
8710 if (mUSBDeviceFilters)
8711 {
8712 mUSBDeviceFilters->uninit();
8713 unconst(mUSBDeviceFilters).setNull();
8714 }
8715
8716 if (mAudioSettings)
8717 {
8718 mAudioSettings->uninit();
8719 unconst(mAudioSettings).setNull();
8720 }
8721
8722 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8723 {
8724 if (mParallelPorts[slot])
8725 {
8726 mParallelPorts[slot]->uninit();
8727 unconst(mParallelPorts[slot]).setNull();
8728 }
8729 }
8730
8731 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8732 {
8733 if (mSerialPorts[slot])
8734 {
8735 mSerialPorts[slot]->uninit();
8736 unconst(mSerialPorts[slot]).setNull();
8737 }
8738 }
8739
8740 if (mVRDEServer)
8741 {
8742 mVRDEServer->uninit();
8743 unconst(mVRDEServer).setNull();
8744 }
8745
8746 if (mGraphicsAdapter)
8747 {
8748 mGraphicsAdapter->uninit();
8749 unconst(mGraphicsAdapter).setNull();
8750 }
8751
8752 if (mBIOSSettings)
8753 {
8754 mBIOSSettings->uninit();
8755 unconst(mBIOSSettings).setNull();
8756 }
8757
8758 if (mRecordingSettings)
8759 {
8760 mRecordingSettings->uninit();
8761 unconst(mRecordingSettings).setNull();
8762 }
8763
8764 if (mTrustedPlatformModule)
8765 {
8766 mTrustedPlatformModule->uninit();
8767 unconst(mTrustedPlatformModule).setNull();
8768 }
8769
8770 if (mNvramStore)
8771 {
8772 mNvramStore->uninit();
8773 unconst(mNvramStore).setNull();
8774 }
8775
8776 /* Deassociate media (only when a real Machine or a SnapshotMachine
8777 * instance is uninitialized; SessionMachine instances refer to real
8778 * Machine media). This is necessary for a clean re-initialization of
8779 * the VM after successfully re-checking the accessibility state. Note
8780 * that in case of normal Machine or SnapshotMachine uninitialization (as
8781 * a result of unregistering or deleting the snapshot), outdated media
8782 * attachments will already be uninitialized and deleted, so this
8783 * code will not affect them. */
8784 if ( !mMediumAttachments.isNull()
8785 && !i_isSessionMachine()
8786 )
8787 {
8788 for (MediumAttachmentList::const_iterator
8789 it = mMediumAttachments->begin();
8790 it != mMediumAttachments->end();
8791 ++it)
8792 {
8793 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8794 if (pMedium.isNull())
8795 continue;
8796 HRESULT hrc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8797 AssertComRC(hrc);
8798 }
8799 }
8800
8801 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8802 {
8803 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children)
8804 if (mData->mFirstSnapshot)
8805 {
8806 // Snapshots tree is protected by machine write lock.
8807 // Otherwise we assert in Snapshot::uninit()
8808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8809 mData->mFirstSnapshot->uninit();
8810 mData->mFirstSnapshot.setNull();
8811 }
8812
8813 mData->mCurrentSnapshot.setNull();
8814 }
8815
8816 /* free data structures (the essential mData structure is not freed here
8817 * since it may be still in use) */
8818 mMediumAttachments.free();
8819 mStorageControllers.free();
8820 mUSBControllers.free();
8821 mHWData.free();
8822 mUserData.free();
8823 mSSData.free();
8824}
8825
8826/**
8827 * Returns a pointer to the Machine object for this machine that acts like a
8828 * parent for complex machine data objects such as shared folders, etc.
8829 *
8830 * For primary Machine objects and for SnapshotMachine objects, returns this
8831 * object's pointer itself. For SessionMachine objects, returns the peer
8832 * (primary) machine pointer.
8833 */
8834Machine *Machine::i_getMachine()
8835{
8836 if (i_isSessionMachine())
8837 return (Machine*)mPeer;
8838 return this;
8839}
8840
8841/**
8842 * Makes sure that there are no machine state dependents. If necessary, waits
8843 * for the number of dependents to drop to zero.
8844 *
8845 * Make sure this method is called from under this object's write lock to
8846 * guarantee that no new dependents may be added when this method returns
8847 * control to the caller.
8848 *
8849 * @note Receives a lock to this object for writing. The lock will be released
8850 * while waiting (if necessary).
8851 *
8852 * @warning To be used only in methods that change the machine state!
8853 */
8854void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8855{
8856 AssertReturnVoid(isWriteLockOnCurrentThread());
8857
8858 /* Wait for all state dependents if necessary */
8859 if (mData->mMachineStateDeps != 0)
8860 {
8861 /* lazy semaphore creation */
8862 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8863 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8864
8865 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8866 mData->mMachineStateDeps));
8867
8868 ++mData->mMachineStateChangePending;
8869
8870 /* reset the semaphore before waiting, the last dependent will signal
8871 * it */
8872 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8873
8874 alock.release();
8875
8876 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8877
8878 alock.acquire();
8879
8880 -- mData->mMachineStateChangePending;
8881 }
8882}
8883
8884/**
8885 * Changes the machine state and informs callbacks.
8886 *
8887 * This method is not intended to fail so it either returns S_OK or asserts (and
8888 * returns a failure).
8889 *
8890 * @note Locks this object for writing.
8891 */
8892HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8893{
8894 LogFlowThisFuncEnter();
8895 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8896 Assert(aMachineState != MachineState_Null);
8897
8898 AutoCaller autoCaller(this);
8899 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
8900
8901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8902
8903 /* wait for state dependents to drop to zero */
8904 i_ensureNoStateDependencies(alock);
8905
8906 MachineState_T const enmOldState = mData->mMachineState;
8907 if (enmOldState != aMachineState)
8908 {
8909 mData->mMachineState = aMachineState;
8910 RTTimeNow(&mData->mLastStateChange);
8911
8912#ifdef VBOX_WITH_DTRACE_R3_MAIN
8913 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8914#endif
8915 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8916 }
8917
8918 LogFlowThisFuncLeave();
8919 return S_OK;
8920}
8921
8922/**
8923 * Searches for a shared folder with the given logical name
8924 * in the collection of shared folders.
8925 *
8926 * @param aName logical name of the shared folder
8927 * @param aSharedFolder where to return the found object
8928 * @param aSetError whether to set the error info if the folder is
8929 * not found
8930 * @return
8931 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8932 *
8933 * @note
8934 * must be called from under the object's lock!
8935 */
8936HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8937 ComObjPtr<SharedFolder> &aSharedFolder,
8938 bool aSetError /* = false */)
8939{
8940 HRESULT hrc = VBOX_E_OBJECT_NOT_FOUND;
8941 for (HWData::SharedFolderList::const_iterator
8942 it = mHWData->mSharedFolders.begin();
8943 it != mHWData->mSharedFolders.end();
8944 ++it)
8945 {
8946 SharedFolder *pSF = *it;
8947 AutoCaller autoCaller(pSF);
8948 if (pSF->i_getName() == aName)
8949 {
8950 aSharedFolder = pSF;
8951 hrc = S_OK;
8952 break;
8953 }
8954 }
8955
8956 if (aSetError && FAILED(hrc))
8957 setError(hrc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8958
8959 return hrc;
8960}
8961
8962/**
8963 * Initializes all machine instance data from the given settings structures
8964 * from XML. The exception is the machine UUID which needs special handling
8965 * depending on the caller's use case, so the caller needs to set that herself.
8966 *
8967 * This gets called in several contexts during machine initialization:
8968 *
8969 * -- When machine XML exists on disk already and needs to be loaded into memory,
8970 * for example, from #i_registeredInit() to load all registered machines on
8971 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8972 * attached to the machine should be part of some media registry already.
8973 *
8974 * -- During OVF import, when a machine config has been constructed from an
8975 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8976 * ensure that the media listed as attachments in the config (which have
8977 * been imported from the OVF) receive the correct registry ID.
8978 *
8979 * -- During VM cloning.
8980 *
8981 * @param config Machine settings from XML.
8982 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8983 * for each attached medium in the config.
8984 * @return
8985 */
8986HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8987 const Guid *puuidRegistry)
8988{
8989 // copy name, description, OS type, teleporter, UTC etc.
8990 mUserData->s = config.machineUserData;
8991
8992 // look up the object by Id to check it is valid
8993 ComObjPtr<GuestOSType> pGuestOSType;
8994 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8995 if (!pGuestOSType.isNull())
8996 mUserData->s.strOsType = pGuestOSType->i_id();
8997
8998#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
8999 // stateFile encryption (optional)
9000 mSSData->strStateKeyId = config.strStateKeyId;
9001 mSSData->strStateKeyStore = config.strStateKeyStore;
9002 mData->mstrLogKeyId = config.strLogKeyId;
9003 mData->mstrLogKeyStore = config.strLogKeyStore;
9004#endif
9005
9006 // stateFile (optional)
9007 if (config.strStateFile.isEmpty())
9008 mSSData->strStateFilePath.setNull();
9009 else
9010 {
9011 Utf8Str stateFilePathFull(config.strStateFile);
9012 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
9013 if (RT_FAILURE(vrc))
9014 return setErrorBoth(E_FAIL, vrc,
9015 tr("Invalid saved state file path '%s' (%Rrc)"),
9016 config.strStateFile.c_str(),
9017 vrc);
9018 mSSData->strStateFilePath = stateFilePathFull;
9019 }
9020
9021 // snapshot folder needs special processing so set it again
9022 HRESULT hrc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
9023 if (FAILED(hrc)) return hrc;
9024
9025 /* Copy the extra data items (config may or may not be the same as
9026 * mData->pMachineConfigFile) if necessary. When loading the XML files
9027 * from disk they are the same, but not for OVF import. */
9028 if (mData->pMachineConfigFile != &config)
9029 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
9030
9031 /* currentStateModified (optional, default is true) */
9032 mData->mCurrentStateModified = config.fCurrentStateModified;
9033
9034 mData->mLastStateChange = config.timeLastStateChange;
9035
9036 /*
9037 * note: all mUserData members must be assigned prior this point because
9038 * we need to commit changes in order to let mUserData be shared by all
9039 * snapshot machine instances.
9040 */
9041 mUserData.commitCopy();
9042
9043 // machine registry, if present (must be loaded before snapshots)
9044 if (config.canHaveOwnMediaRegistry())
9045 {
9046 // determine machine folder
9047 Utf8Str strMachineFolder = i_getSettingsFileFull();
9048 strMachineFolder.stripFilename();
9049 hrc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
9050 config.mediaRegistry,
9051 strMachineFolder);
9052 if (FAILED(hrc)) return hrc;
9053 }
9054
9055 /* Snapshot node (optional) */
9056 size_t cRootSnapshots;
9057 if ((cRootSnapshots = config.llFirstSnapshot.size()))
9058 {
9059 // there must be only one root snapshot
9060 Assert(cRootSnapshots == 1);
9061 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9062
9063 hrc = i_loadSnapshot(snap, config.uuidCurrentSnapshot);
9064 if (FAILED(hrc)) return hrc;
9065 }
9066
9067 // hardware data
9068 hrc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart,
9069 config.recordingSettings);
9070 if (FAILED(hrc)) return hrc;
9071
9072 /*
9073 * NOTE: the assignment below must be the last thing to do,
9074 * otherwise it will be not possible to change the settings
9075 * somewhere in the code above because all setters will be
9076 * blocked by i_checkStateDependency(MutableStateDep).
9077 */
9078
9079 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
9080 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
9081 {
9082 /* no need to use i_setMachineState() during init() */
9083 mData->mMachineState = MachineState_AbortedSaved;
9084 }
9085 else if (config.fAborted)
9086 {
9087 mSSData->strStateFilePath.setNull();
9088
9089 /* no need to use i_setMachineState() during init() */
9090 mData->mMachineState = MachineState_Aborted;
9091 }
9092 else if (!mSSData->strStateFilePath.isEmpty())
9093 {
9094 /* no need to use i_setMachineState() during init() */
9095 mData->mMachineState = MachineState_Saved;
9096 }
9097
9098 // after loading settings, we are no longer different from the XML on disk
9099 mData->flModifications = 0;
9100
9101 return S_OK;
9102}
9103
9104/**
9105 * Loads all snapshots starting from the given settings.
9106 *
9107 * @param data snapshot settings.
9108 * @param aCurSnapshotId Current snapshot ID from the settings file.
9109 */
9110HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
9111 const Guid &aCurSnapshotId)
9112{
9113 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
9114 AssertReturn(!i_isSessionMachine(), E_FAIL);
9115
9116 HRESULT hrc = S_OK;
9117
9118 std::list<const settings::Snapshot *> llSettingsTodo;
9119 llSettingsTodo.push_back(&data);
9120 std::list<Snapshot *> llParentsTodo;
9121 llParentsTodo.push_back(NULL);
9122
9123 while (llSettingsTodo.size() > 0)
9124 {
9125 const settings::Snapshot *current = llSettingsTodo.front();
9126 llSettingsTodo.pop_front();
9127 Snapshot *pParent = llParentsTodo.front();
9128 llParentsTodo.pop_front();
9129
9130 Utf8Str strStateFile;
9131 if (!current->strStateFile.isEmpty())
9132 {
9133 /* optional */
9134 strStateFile = current->strStateFile;
9135 int vrc = i_calculateFullPath(strStateFile, strStateFile);
9136 if (RT_FAILURE(vrc))
9137 {
9138 setErrorBoth(E_FAIL, vrc,
9139 tr("Invalid saved state file path '%s' (%Rrc)"),
9140 strStateFile.c_str(), vrc);
9141 }
9142 }
9143
9144 /* create a snapshot machine object */
9145 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9146 pSnapshotMachine.createObject();
9147 hrc = pSnapshotMachine->initFromSettings(this,
9148 current->hardware,
9149 &current->debugging,
9150 &current->autostart,
9151 current->recordingSettings,
9152 current->uuid.ref(),
9153 strStateFile);
9154 if (FAILED(hrc)) break;
9155
9156 /* create a snapshot object */
9157 ComObjPtr<Snapshot> pSnapshot;
9158 pSnapshot.createObject();
9159 /* initialize the snapshot */
9160 hrc = pSnapshot->init(mParent, // VirtualBox object
9161 current->uuid,
9162 current->strName,
9163 current->strDescription,
9164 current->timestamp,
9165 pSnapshotMachine,
9166 pParent);
9167 if (FAILED(hrc)) break;
9168
9169 /* memorize the first snapshot if necessary */
9170 if (!mData->mFirstSnapshot)
9171 {
9172 Assert(pParent == NULL);
9173 mData->mFirstSnapshot = pSnapshot;
9174 }
9175
9176 /* memorize the current snapshot when appropriate */
9177 if ( !mData->mCurrentSnapshot
9178 && pSnapshot->i_getId() == aCurSnapshotId
9179 )
9180 mData->mCurrentSnapshot = pSnapshot;
9181
9182 /* create all children */
9183 std::list<settings::Snapshot>::const_iterator itBegin = current->llChildSnapshots.begin();
9184 std::list<settings::Snapshot>::const_iterator itEnd = current->llChildSnapshots.end();
9185 for (std::list<settings::Snapshot>::const_iterator it = itBegin; it != itEnd; ++it)
9186 {
9187 llSettingsTodo.push_back(&*it);
9188 llParentsTodo.push_back(pSnapshot);
9189 }
9190 }
9191
9192 return hrc;
9193}
9194
9195/**
9196 * Loads settings into mHWData.
9197 *
9198 * @param puuidRegistry Registry ID.
9199 * @param puuidSnapshot Snapshot ID
9200 * @param data Reference to the hardware settings.
9201 * @param pDbg Pointer to the debugging settings.
9202 * @param pAutostart Pointer to the autostart settings
9203 * @param recording Reference to recording settings.
9204 */
9205HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
9206 const Guid *puuidSnapshot,
9207 const settings::Hardware &data,
9208 const settings::Debugging *pDbg,
9209 const settings::Autostart *pAutostart,
9210 const settings::RecordingSettings &recording)
9211{
9212 AssertReturn(!i_isSessionMachine(), E_FAIL);
9213
9214 HRESULT hrc = S_OK;
9215
9216 try
9217 {
9218 ComObjPtr<GuestOSType> pGuestOSType;
9219 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9220
9221 /* The hardware version attribute (optional). */
9222 mHWData->mHWVersion = data.strVersion;
9223 mHWData->mHardwareUUID = data.uuid;
9224
9225 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9226 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9227 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9228 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9229 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9230 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9231 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
9232 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
9233 mHWData->mPAEEnabled = data.fPAE;
9234 mHWData->mLongMode = data.enmLongMode;
9235 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9236 mHWData->mAPIC = data.fAPIC;
9237 mHWData->mX2APIC = data.fX2APIC;
9238 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9239 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9240 mHWData->mSpecCtrl = data.fSpecCtrl;
9241 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
9242 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
9243 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
9244 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
9245 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
9246 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9247 mHWData->mCPUCount = data.cCPUs;
9248 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9249 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9250 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9251 mHWData->mCpuProfile = data.strCpuProfile;
9252
9253 // cpu
9254 if (mHWData->mCPUHotPlugEnabled)
9255 {
9256 for (settings::CpuList::const_iterator
9257 it = data.llCpus.begin();
9258 it != data.llCpus.end();
9259 ++it)
9260 {
9261 const settings::Cpu &cpu = *it;
9262
9263 mHWData->mCPUAttached[cpu.ulId] = true;
9264 }
9265 }
9266
9267 // cpuid leafs
9268 for (settings::CpuIdLeafsList::const_iterator
9269 it = data.llCpuIdLeafs.begin();
9270 it != data.llCpuIdLeafs.end();
9271 ++it)
9272 {
9273 const settings::CpuIdLeaf &rLeaf= *it;
9274 if ( rLeaf.idx < UINT32_C(0x20)
9275 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9276 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9277 mHWData->mCpuIdLeafList.push_back(rLeaf);
9278 /* else: just ignore */
9279 }
9280
9281 mHWData->mMemorySize = data.ulMemorySizeMB;
9282 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9283
9284 // boot order
9285 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9286 {
9287 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9288 if (it == data.mapBootOrder.end())
9289 mHWData->mBootOrder[i] = DeviceType_Null;
9290 else
9291 mHWData->mBootOrder[i] = it->second;
9292 }
9293
9294 mHWData->mFirmwareType = data.firmwareType;
9295 mHWData->mPointingHIDType = data.pointingHIDType;
9296 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9297 mHWData->mChipsetType = data.chipsetType;
9298 mHWData->mIommuType = data.iommuType;
9299 mHWData->mParavirtProvider = data.paravirtProvider;
9300 mHWData->mParavirtDebug = data.strParavirtDebug;
9301 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9302 mHWData->mHPETEnabled = data.fHPETEnabled;
9303
9304 /* GraphicsAdapter */
9305 hrc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
9306 if (FAILED(hrc)) return hrc;
9307
9308 /* VRDEServer */
9309 hrc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9310 if (FAILED(hrc)) return hrc;
9311
9312 /* BIOS */
9313 hrc = mBIOSSettings->i_loadSettings(data.biosSettings);
9314 if (FAILED(hrc)) return hrc;
9315
9316 /* Recording */
9317 hrc = mRecordingSettings->i_loadSettings(recording);
9318 if (FAILED(hrc)) return hrc;
9319
9320 /* Trusted Platform Module */
9321 hrc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
9322 if (FAILED(hrc)) return hrc;
9323
9324 hrc = mNvramStore->i_loadSettings(data.nvramSettings);
9325 if (FAILED(hrc)) return hrc;
9326
9327 // Bandwidth control (must come before network adapters)
9328 hrc = mBandwidthControl->i_loadSettings(data.ioSettings);
9329 if (FAILED(hrc)) return hrc;
9330
9331 /* USB controllers */
9332 for (settings::USBControllerList::const_iterator
9333 it = data.usbSettings.llUSBControllers.begin();
9334 it != data.usbSettings.llUSBControllers.end();
9335 ++it)
9336 {
9337 const settings::USBController &settingsCtrl = *it;
9338 ComObjPtr<USBController> newCtrl;
9339
9340 newCtrl.createObject();
9341 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9342 mUSBControllers->push_back(newCtrl);
9343 }
9344
9345 /* USB device filters */
9346 hrc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9347 if (FAILED(hrc)) return hrc;
9348
9349 // network adapters (establish array size first and apply defaults, to
9350 // ensure reading the same settings as we saved, since the list skips
9351 // adapters having defaults)
9352 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9353 size_t oldCount = mNetworkAdapters.size();
9354 if (newCount > oldCount)
9355 {
9356 mNetworkAdapters.resize(newCount);
9357 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9358 {
9359 unconst(mNetworkAdapters[slot]).createObject();
9360 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9361 }
9362 }
9363 else if (newCount < oldCount)
9364 mNetworkAdapters.resize(newCount);
9365 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9366 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9367 for (settings::NetworkAdaptersList::const_iterator
9368 it = data.llNetworkAdapters.begin();
9369 it != data.llNetworkAdapters.end();
9370 ++it)
9371 {
9372 const settings::NetworkAdapter &nic = *it;
9373
9374 /* slot uniqueness is guaranteed by XML Schema */
9375 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9376 hrc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9377 if (FAILED(hrc)) return hrc;
9378 }
9379
9380 // serial ports (establish defaults first, to ensure reading the same
9381 // settings as we saved, since the list skips ports having defaults)
9382 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9383 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9384 for (settings::SerialPortsList::const_iterator
9385 it = data.llSerialPorts.begin();
9386 it != data.llSerialPorts.end();
9387 ++it)
9388 {
9389 const settings::SerialPort &s = *it;
9390
9391 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9392 hrc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9393 if (FAILED(hrc)) return hrc;
9394 }
9395
9396 // parallel ports (establish defaults first, to ensure reading the same
9397 // settings as we saved, since the list skips ports having defaults)
9398 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9399 mParallelPorts[i]->i_applyDefaults();
9400 for (settings::ParallelPortsList::const_iterator
9401 it = data.llParallelPorts.begin();
9402 it != data.llParallelPorts.end();
9403 ++it)
9404 {
9405 const settings::ParallelPort &p = *it;
9406
9407 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9408 hrc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9409 if (FAILED(hrc)) return hrc;
9410 }
9411
9412 /* Audio settings */
9413 hrc = mAudioSettings->i_loadSettings(data.audioAdapter);
9414 if (FAILED(hrc)) return hrc;
9415
9416 /* storage controllers */
9417 hrc = i_loadStorageControllers(data.storage, puuidRegistry, puuidSnapshot);
9418 if (FAILED(hrc)) return hrc;
9419
9420 /* Shared folders */
9421 for (settings::SharedFoldersList::const_iterator
9422 it = data.llSharedFolders.begin();
9423 it != data.llSharedFolders.end();
9424 ++it)
9425 {
9426 const settings::SharedFolder &sf = *it;
9427
9428 ComObjPtr<SharedFolder> sharedFolder;
9429 /* Check for double entries. Not allowed! */
9430 hrc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9431 if (SUCCEEDED(hrc))
9432 return setError(VBOX_E_OBJECT_IN_USE,
9433 tr("Shared folder named '%s' already exists"),
9434 sf.strName.c_str());
9435
9436 /* Create the new shared folder. Don't break on error. This will be
9437 * reported when the machine starts. */
9438 sharedFolder.createObject();
9439 hrc = sharedFolder->init(i_getMachine(),
9440 sf.strName,
9441 sf.strHostPath,
9442 RT_BOOL(sf.fWritable),
9443 RT_BOOL(sf.fAutoMount),
9444 sf.strAutoMountPoint,
9445 false /* fFailOnError */);
9446 if (FAILED(hrc)) return hrc;
9447 mHWData->mSharedFolders.push_back(sharedFolder);
9448 }
9449
9450 // Clipboard
9451 mHWData->mClipboardMode = data.clipboardMode;
9452 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9453
9454 // drag'n'drop
9455 mHWData->mDnDMode = data.dndMode;
9456
9457 // guest settings
9458 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9459
9460 // IO settings
9461 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9462 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9463
9464 // Host PCI devices
9465 for (settings::HostPCIDeviceAttachmentList::const_iterator
9466 it = data.pciAttachments.begin();
9467 it != data.pciAttachments.end();
9468 ++it)
9469 {
9470 const settings::HostPCIDeviceAttachment &hpda = *it;
9471 ComObjPtr<PCIDeviceAttachment> pda;
9472
9473 pda.createObject();
9474 pda->i_loadSettings(this, hpda);
9475 mHWData->mPCIDeviceAssignments.push_back(pda);
9476 }
9477
9478 /*
9479 * (The following isn't really real hardware, but it lives in HWData
9480 * for reasons of convenience.)
9481 */
9482
9483#ifdef VBOX_WITH_GUEST_PROPS
9484 /* Guest properties (optional) */
9485
9486 /* Only load transient guest properties for configs which have saved
9487 * state, because there shouldn't be any for powered off VMs. The same
9488 * logic applies for snapshots, as offline snapshots shouldn't have
9489 * any such properties. They confuse the code in various places.
9490 * Note: can't rely on the machine state, as it isn't set yet. */
9491 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9492 /* apologies for the hacky unconst() usage, but this needs hacking
9493 * actually inconsistent settings into consistency, otherwise there
9494 * will be some corner cases where the inconsistency survives
9495 * surprisingly long without getting fixed, especially for snapshots
9496 * as there are no config changes. */
9497 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9498 for (settings::GuestPropertiesList::iterator
9499 it = llGuestProperties.begin();
9500 it != llGuestProperties.end();
9501 /*nothing*/)
9502 {
9503 const settings::GuestProperty &prop = *it;
9504 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9505 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9506 if ( fSkipTransientGuestProperties
9507 && ( fFlags & GUEST_PROP_F_TRANSIENT
9508 || fFlags & GUEST_PROP_F_TRANSRESET))
9509 {
9510 it = llGuestProperties.erase(it);
9511 continue;
9512 }
9513 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9514 mHWData->mGuestProperties[prop.strName] = property;
9515 ++it;
9516 }
9517#endif /* VBOX_WITH_GUEST_PROPS defined */
9518
9519 hrc = i_loadDebugging(pDbg);
9520 if (FAILED(hrc))
9521 return hrc;
9522
9523 mHWData->mAutostart = *pAutostart;
9524
9525 /* default frontend */
9526 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9527 }
9528 catch (std::bad_alloc &)
9529 {
9530 return E_OUTOFMEMORY;
9531 }
9532
9533 AssertComRC(hrc);
9534 return hrc;
9535}
9536
9537/**
9538 * Called from i_loadHardware() to load the debugging settings of the
9539 * machine.
9540 *
9541 * @param pDbg Pointer to the settings.
9542 */
9543HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9544{
9545 mHWData->mDebugging = *pDbg;
9546 /* no more processing currently required, this will probably change. */
9547
9548 HRESULT hrc = mGuestDebugControl->i_loadSettings(*pDbg);
9549 if (FAILED(hrc)) return hrc;
9550
9551 return S_OK;
9552}
9553
9554/**
9555 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9556 *
9557 * @param data storage settings.
9558 * @param puuidRegistry media registry ID to set media to or NULL;
9559 * see Machine::i_loadMachineDataFromSettings()
9560 * @param puuidSnapshot snapshot ID
9561 * @return
9562 */
9563HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9564 const Guid *puuidRegistry,
9565 const Guid *puuidSnapshot)
9566{
9567 AssertReturn(!i_isSessionMachine(), E_FAIL);
9568
9569 HRESULT hrc = S_OK;
9570
9571 for (settings::StorageControllersList::const_iterator
9572 it = data.llStorageControllers.begin();
9573 it != data.llStorageControllers.end();
9574 ++it)
9575 {
9576 const settings::StorageController &ctlData = *it;
9577
9578 ComObjPtr<StorageController> pCtl;
9579 /* Try to find one with the name first. */
9580 hrc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9581 if (SUCCEEDED(hrc))
9582 return setError(VBOX_E_OBJECT_IN_USE,
9583 tr("Storage controller named '%s' already exists"),
9584 ctlData.strName.c_str());
9585
9586 pCtl.createObject();
9587 hrc = pCtl->init(this, ctlData.strName, ctlData.storageBus, ctlData.ulInstance, ctlData.fBootable);
9588 if (FAILED(hrc)) return hrc;
9589
9590 mStorageControllers->push_back(pCtl);
9591
9592 hrc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9593 if (FAILED(hrc)) return hrc;
9594
9595 hrc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9596 if (FAILED(hrc)) return hrc;
9597
9598 hrc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9599 if (FAILED(hrc)) return hrc;
9600
9601 /* Load the attached devices now. */
9602 hrc = i_loadStorageDevices(pCtl, ctlData, puuidRegistry, puuidSnapshot);
9603 if (FAILED(hrc)) return hrc;
9604 }
9605
9606 return S_OK;
9607}
9608
9609/**
9610 * Called from i_loadStorageControllers for a controller's devices.
9611 *
9612 * @param aStorageController
9613 * @param data
9614 * @param puuidRegistry media registry ID to set media to or NULL; see
9615 * Machine::i_loadMachineDataFromSettings()
9616 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9617 * @return
9618 */
9619HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9620 const settings::StorageController &data,
9621 const Guid *puuidRegistry,
9622 const Guid *puuidSnapshot)
9623{
9624 HRESULT hrc = S_OK;
9625
9626 /* paranoia: detect duplicate attachments */
9627 for (settings::AttachedDevicesList::const_iterator
9628 it = data.llAttachedDevices.begin();
9629 it != data.llAttachedDevices.end();
9630 ++it)
9631 {
9632 const settings::AttachedDevice &ad = *it;
9633
9634 for (settings::AttachedDevicesList::const_iterator it2 = it;
9635 it2 != data.llAttachedDevices.end();
9636 ++it2)
9637 {
9638 if (it == it2)
9639 continue;
9640
9641 const settings::AttachedDevice &ad2 = *it2;
9642
9643 if ( ad.lPort == ad2.lPort
9644 && ad.lDevice == ad2.lDevice)
9645 {
9646 return setError(E_FAIL,
9647 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9648 aStorageController->i_getName().c_str(),
9649 ad.lPort,
9650 ad.lDevice,
9651 mUserData->s.strName.c_str());
9652 }
9653 }
9654 }
9655
9656 for (settings::AttachedDevicesList::const_iterator
9657 it = data.llAttachedDevices.begin();
9658 it != data.llAttachedDevices.end();
9659 ++it)
9660 {
9661 const settings::AttachedDevice &dev = *it;
9662 ComObjPtr<Medium> medium;
9663
9664 switch (dev.deviceType)
9665 {
9666 case DeviceType_Floppy:
9667 case DeviceType_DVD:
9668 if (dev.strHostDriveSrc.isNotEmpty())
9669 hrc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9670 false /* fRefresh */, medium);
9671 else
9672 hrc = mParent->i_findRemoveableMedium(dev.deviceType,
9673 dev.uuid,
9674 false /* fRefresh */,
9675 false /* aSetError */,
9676 medium);
9677 if (hrc == VBOX_E_OBJECT_NOT_FOUND)
9678 // This is not an error. The host drive or UUID might have vanished, so just go
9679 // ahead without this removeable medium attachment
9680 hrc = S_OK;
9681 break;
9682
9683 case DeviceType_HardDisk:
9684 {
9685 /* find a hard disk by UUID */
9686 hrc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9687 if (FAILED(hrc))
9688 {
9689 if (i_isSnapshotMachine())
9690 {
9691 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9692 // so the user knows that the bad disk is in a snapshot somewhere
9693 com::ErrorInfo info;
9694 return setError(E_FAIL,
9695 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9696 puuidSnapshot->raw(),
9697 info.getText().raw());
9698 }
9699 return hrc;
9700 }
9701
9702 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9703
9704 if (medium->i_getType() == MediumType_Immutable)
9705 {
9706 if (i_isSnapshotMachine())
9707 return setError(E_FAIL,
9708 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9709 "of the virtual machine '%s' ('%s')"),
9710 medium->i_getLocationFull().c_str(),
9711 dev.uuid.raw(),
9712 puuidSnapshot->raw(),
9713 mUserData->s.strName.c_str(),
9714 mData->m_strConfigFileFull.c_str());
9715
9716 return setError(E_FAIL,
9717 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9718 medium->i_getLocationFull().c_str(),
9719 dev.uuid.raw(),
9720 mUserData->s.strName.c_str(),
9721 mData->m_strConfigFileFull.c_str());
9722 }
9723
9724 if (medium->i_getType() == MediumType_MultiAttach)
9725 {
9726 if (i_isSnapshotMachine())
9727 return setError(E_FAIL,
9728 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9729 "of the virtual machine '%s' ('%s')"),
9730 medium->i_getLocationFull().c_str(),
9731 dev.uuid.raw(),
9732 puuidSnapshot->raw(),
9733 mUserData->s.strName.c_str(),
9734 mData->m_strConfigFileFull.c_str());
9735
9736 return setError(E_FAIL,
9737 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9738 medium->i_getLocationFull().c_str(),
9739 dev.uuid.raw(),
9740 mUserData->s.strName.c_str(),
9741 mData->m_strConfigFileFull.c_str());
9742 }
9743
9744 if ( !i_isSnapshotMachine()
9745 && medium->i_getChildren().size() != 0
9746 )
9747 return setError(E_FAIL,
9748 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9749 "because it has %d differencing child hard disks"),
9750 medium->i_getLocationFull().c_str(),
9751 dev.uuid.raw(),
9752 mUserData->s.strName.c_str(),
9753 mData->m_strConfigFileFull.c_str(),
9754 medium->i_getChildren().size());
9755
9756 if (i_findAttachment(*mMediumAttachments.data(),
9757 medium))
9758 return setError(E_FAIL,
9759 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9760 medium->i_getLocationFull().c_str(),
9761 dev.uuid.raw(),
9762 mUserData->s.strName.c_str(),
9763 mData->m_strConfigFileFull.c_str());
9764
9765 break;
9766 }
9767
9768 default:
9769 return setError(E_FAIL,
9770 tr("Controller '%s' port %u unit %u has device with unknown type (%d) - virtual machine '%s' ('%s')"),
9771 data.strName.c_str(), dev.lPort, dev.lDevice, dev.deviceType,
9772 mUserData->s.strName.c_str(), mData->m_strConfigFileFull.c_str());
9773 }
9774
9775 if (FAILED(hrc))
9776 break;
9777
9778 /* Bandwidth groups are loaded at this point. */
9779 ComObjPtr<BandwidthGroup> pBwGroup;
9780
9781 if (!dev.strBwGroup.isEmpty())
9782 {
9783 hrc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9784 if (FAILED(hrc))
9785 return setError(E_FAIL,
9786 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9787 medium->i_getLocationFull().c_str(),
9788 dev.strBwGroup.c_str(),
9789 mUserData->s.strName.c_str(),
9790 mData->m_strConfigFileFull.c_str());
9791 pBwGroup->i_reference();
9792 }
9793
9794 const Utf8Str controllerName = aStorageController->i_getName();
9795 ComObjPtr<MediumAttachment> pAttachment;
9796 pAttachment.createObject();
9797 hrc = pAttachment->init(this,
9798 medium,
9799 controllerName,
9800 dev.lPort,
9801 dev.lDevice,
9802 dev.deviceType,
9803 false,
9804 dev.fPassThrough,
9805 dev.fTempEject,
9806 dev.fNonRotational,
9807 dev.fDiscard,
9808 dev.fHotPluggable,
9809 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9810 if (FAILED(hrc)) break;
9811
9812 /* associate the medium with this machine and snapshot */
9813 if (!medium.isNull())
9814 {
9815 AutoCaller medCaller(medium);
9816 if (FAILED(medCaller.hrc())) return medCaller.hrc();
9817 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9818
9819 if (i_isSnapshotMachine())
9820 hrc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9821 else
9822 hrc = medium->i_addBackReference(mData->mUuid);
9823 /* If the medium->addBackReference fails it sets an appropriate
9824 * error message, so no need to do any guesswork here. */
9825
9826 if (puuidRegistry)
9827 // caller wants registry ID to be set on all attached media (OVF import case)
9828 medium->i_addRegistry(*puuidRegistry);
9829 }
9830
9831 if (FAILED(hrc))
9832 break;
9833
9834 /* back up mMediumAttachments to let registeredInit() properly rollback
9835 * on failure (= limited accessibility) */
9836 i_setModified(IsModified_Storage);
9837 mMediumAttachments.backup();
9838 mMediumAttachments->push_back(pAttachment);
9839 }
9840
9841 return hrc;
9842}
9843
9844/**
9845 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9846 *
9847 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9848 * @param aSnapshot where to return the found snapshot
9849 * @param aSetError true to set extended error info on failure
9850 */
9851HRESULT Machine::i_findSnapshotById(const Guid &aId,
9852 ComObjPtr<Snapshot> &aSnapshot,
9853 bool aSetError /* = false */)
9854{
9855 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9856
9857 if (!mData->mFirstSnapshot)
9858 {
9859 if (aSetError)
9860 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9861 return E_FAIL;
9862 }
9863
9864 if (aId.isZero())
9865 aSnapshot = mData->mFirstSnapshot;
9866 else
9867 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9868
9869 if (!aSnapshot)
9870 {
9871 if (aSetError)
9872 return setError(E_FAIL,
9873 tr("Could not find a snapshot with UUID {%s}"),
9874 aId.toString().c_str());
9875 return E_FAIL;
9876 }
9877
9878 return S_OK;
9879}
9880
9881/**
9882 * Returns the snapshot with the given name or fails of no such snapshot.
9883 *
9884 * @param strName snapshot name to find
9885 * @param aSnapshot where to return the found snapshot
9886 * @param aSetError true to set extended error info on failure
9887 */
9888HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9889 ComObjPtr<Snapshot> &aSnapshot,
9890 bool aSetError /* = false */)
9891{
9892 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9893
9894 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9895
9896 if (!mData->mFirstSnapshot)
9897 {
9898 if (aSetError)
9899 return setError(VBOX_E_OBJECT_NOT_FOUND,
9900 tr("This machine does not have any snapshots"));
9901 return VBOX_E_OBJECT_NOT_FOUND;
9902 }
9903
9904 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9905
9906 if (!aSnapshot)
9907 {
9908 if (aSetError)
9909 return setError(VBOX_E_OBJECT_NOT_FOUND,
9910 tr("Could not find a snapshot named '%s'"), strName.c_str());
9911 return VBOX_E_OBJECT_NOT_FOUND;
9912 }
9913
9914 return S_OK;
9915}
9916
9917/**
9918 * Returns a storage controller object with the given name.
9919 *
9920 * @param aName storage controller name to find
9921 * @param aStorageController where to return the found storage controller
9922 * @param aSetError true to set extended error info on failure
9923 */
9924HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9925 ComObjPtr<StorageController> &aStorageController,
9926 bool aSetError /* = false */)
9927{
9928 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9929
9930 for (StorageControllerList::const_iterator
9931 it = mStorageControllers->begin();
9932 it != mStorageControllers->end();
9933 ++it)
9934 {
9935 if ((*it)->i_getName() == aName)
9936 {
9937 aStorageController = (*it);
9938 return S_OK;
9939 }
9940 }
9941
9942 if (aSetError)
9943 return setError(VBOX_E_OBJECT_NOT_FOUND,
9944 tr("Could not find a storage controller named '%s'"),
9945 aName.c_str());
9946 return VBOX_E_OBJECT_NOT_FOUND;
9947}
9948
9949/**
9950 * Returns a USB controller object with the given name.
9951 *
9952 * @param aName USB controller name to find
9953 * @param aUSBController where to return the found USB controller
9954 * @param aSetError true to set extended error info on failure
9955 */
9956HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9957 ComObjPtr<USBController> &aUSBController,
9958 bool aSetError /* = false */)
9959{
9960 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9961
9962 for (USBControllerList::const_iterator
9963 it = mUSBControllers->begin();
9964 it != mUSBControllers->end();
9965 ++it)
9966 {
9967 if ((*it)->i_getName() == aName)
9968 {
9969 aUSBController = (*it);
9970 return S_OK;
9971 }
9972 }
9973
9974 if (aSetError)
9975 return setError(VBOX_E_OBJECT_NOT_FOUND,
9976 tr("Could not find a storage controller named '%s'"),
9977 aName.c_str());
9978 return VBOX_E_OBJECT_NOT_FOUND;
9979}
9980
9981/**
9982 * Returns the number of USB controller instance of the given type.
9983 *
9984 * @param enmType USB controller type.
9985 */
9986ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9987{
9988 ULONG cCtrls = 0;
9989
9990 for (USBControllerList::const_iterator
9991 it = mUSBControllers->begin();
9992 it != mUSBControllers->end();
9993 ++it)
9994 {
9995 if ((*it)->i_getControllerType() == enmType)
9996 cCtrls++;
9997 }
9998
9999 return cCtrls;
10000}
10001
10002HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
10003 MediumAttachmentList &atts)
10004{
10005 AutoCaller autoCaller(this);
10006 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
10007
10008 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10009
10010 for (MediumAttachmentList::const_iterator
10011 it = mMediumAttachments->begin();
10012 it != mMediumAttachments->end();
10013 ++it)
10014 {
10015 const ComObjPtr<MediumAttachment> &pAtt = *it;
10016 // should never happen, but deal with NULL pointers in the list.
10017 AssertContinue(!pAtt.isNull());
10018
10019 // getControllerName() needs caller+read lock
10020 AutoCaller autoAttCaller(pAtt);
10021 if (FAILED(autoAttCaller.hrc()))
10022 {
10023 atts.clear();
10024 return autoAttCaller.hrc();
10025 }
10026 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
10027
10028 if (pAtt->i_getControllerName() == aName)
10029 atts.push_back(pAtt);
10030 }
10031
10032 return S_OK;
10033}
10034
10035
10036/**
10037 * Helper for #i_saveSettings. Cares about renaming the settings directory and
10038 * file if the machine name was changed and about creating a new settings file
10039 * if this is a new machine.
10040 *
10041 * @note Must be never called directly but only from #saveSettings().
10042 */
10043HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
10044 bool *pfSettingsFileIsNew)
10045{
10046 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10047
10048 HRESULT hrc = S_OK;
10049
10050 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
10051 /// @todo need to handle primary group change, too
10052
10053 /* attempt to rename the settings file if machine name is changed */
10054 if ( mUserData->s.fNameSync
10055 && mUserData.isBackedUp()
10056 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
10057 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
10058 )
10059 {
10060 bool dirRenamed = false;
10061 bool fileRenamed = false;
10062
10063 Utf8Str configFile, newConfigFile;
10064 Utf8Str configFilePrev, newConfigFilePrev;
10065 Utf8Str NVRAMFile, newNVRAMFile;
10066 Utf8Str configDir, newConfigDir;
10067
10068 do
10069 {
10070 int vrc = VINF_SUCCESS;
10071
10072 Utf8Str name = mUserData.backedUpData()->s.strName;
10073 Utf8Str newName = mUserData->s.strName;
10074 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
10075 if (group == "/")
10076 group.setNull();
10077 Utf8Str newGroup = mUserData->s.llGroups.front();
10078 if (newGroup == "/")
10079 newGroup.setNull();
10080
10081 configFile = mData->m_strConfigFileFull;
10082
10083 /* first, rename the directory if it matches the group and machine name */
10084 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
10085 /** @todo hack, make somehow use of ComposeMachineFilename */
10086 if (mUserData->s.fDirectoryIncludesUUID)
10087 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10088 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
10089 /** @todo hack, make somehow use of ComposeMachineFilename */
10090 if (mUserData->s.fDirectoryIncludesUUID)
10091 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10092 configDir = configFile;
10093 configDir.stripFilename();
10094 newConfigDir = configDir;
10095 if ( configDir.length() >= groupPlusName.length()
10096 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
10097 groupPlusName.c_str()))
10098 {
10099 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10100 Utf8Str newConfigBaseDir(newConfigDir);
10101 newConfigDir.append(newGroupPlusName);
10102 /* consistency: use \ if appropriate on the platform */
10103 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10104 /* new dir and old dir cannot be equal here because of 'if'
10105 * above and because name != newName */
10106 Assert(configDir != newConfigDir);
10107 if (!fSettingsFileIsNew)
10108 {
10109 /* perform real rename only if the machine is not new */
10110 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10111 if ( vrc == VERR_FILE_NOT_FOUND
10112 || vrc == VERR_PATH_NOT_FOUND)
10113 {
10114 /* create the parent directory, then retry renaming */
10115 Utf8Str parent(newConfigDir);
10116 parent.stripFilename();
10117 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10118 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10119 }
10120 if (RT_FAILURE(vrc))
10121 {
10122 hrc = setErrorBoth(E_FAIL, vrc,
10123 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10124 configDir.c_str(),
10125 newConfigDir.c_str(),
10126 vrc);
10127 break;
10128 }
10129 /* delete subdirectories which are no longer needed */
10130 Utf8Str dir(configDir);
10131 dir.stripFilename();
10132 while (dir != newConfigBaseDir && dir != ".")
10133 {
10134 vrc = RTDirRemove(dir.c_str());
10135 if (RT_FAILURE(vrc))
10136 break;
10137 dir.stripFilename();
10138 }
10139 dirRenamed = true;
10140 }
10141 }
10142
10143 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10144
10145 /* then try to rename the settings file itself */
10146 if (newConfigFile != configFile)
10147 {
10148 /* get the path to old settings file in renamed directory */
10149 Assert(mData->m_strConfigFileFull == configFile);
10150 configFile.printf("%s%c%s",
10151 newConfigDir.c_str(),
10152 RTPATH_DELIMITER,
10153 RTPathFilename(mData->m_strConfigFileFull.c_str()));
10154 if (!fSettingsFileIsNew)
10155 {
10156 /* perform real rename only if the machine is not new */
10157 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10158 if (RT_FAILURE(vrc))
10159 {
10160 hrc = setErrorBoth(E_FAIL, vrc,
10161 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10162 configFile.c_str(),
10163 newConfigFile.c_str(),
10164 vrc);
10165 break;
10166 }
10167 fileRenamed = true;
10168 configFilePrev = configFile;
10169 configFilePrev += "-prev";
10170 newConfigFilePrev = newConfigFile;
10171 newConfigFilePrev += "-prev";
10172 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10173 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
10174 if (NVRAMFile.isNotEmpty())
10175 {
10176 // in the NVRAM file path, replace the old directory with the new directory
10177 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
10178 {
10179 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
10180 NVRAMFile = newConfigDir + strNVRAMFile;
10181 }
10182 newNVRAMFile = newConfigFile;
10183 newNVRAMFile.stripSuffix();
10184 newNVRAMFile += ".nvram";
10185 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
10186 }
10187 }
10188 }
10189
10190 // update m_strConfigFileFull amd mConfigFile
10191 mData->m_strConfigFileFull = newConfigFile;
10192 // compute the relative path too
10193 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10194
10195 // store the old and new so that VirtualBox::i_saveSettings() can update
10196 // the media registry
10197 if ( mData->mRegistered
10198 && (configDir != newConfigDir || configFile != newConfigFile))
10199 {
10200 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10201
10202 if (pfNeedsGlobalSaveSettings)
10203 *pfNeedsGlobalSaveSettings = true;
10204 }
10205
10206 // in the saved state file path, replace the old directory with the new directory
10207 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10208 {
10209 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
10210 mSSData->strStateFilePath = newConfigDir + strStateFileName;
10211 }
10212 if (newNVRAMFile.isNotEmpty())
10213 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
10214
10215 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
10216 if (mData->mFirstSnapshot)
10217 {
10218 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10219 newConfigDir.c_str());
10220 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
10221 newConfigDir.c_str());
10222 }
10223 }
10224 while (0);
10225
10226 if (FAILED(hrc))
10227 {
10228 /* silently try to rename everything back */
10229 if (fileRenamed)
10230 {
10231 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10232 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10233 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
10234 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
10235 }
10236 if (dirRenamed)
10237 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10238 }
10239
10240 if (FAILED(hrc)) return hrc;
10241 }
10242
10243 if (fSettingsFileIsNew)
10244 {
10245 /* create a virgin config file */
10246 int vrc = VINF_SUCCESS;
10247
10248 /* ensure the settings directory exists */
10249 Utf8Str path(mData->m_strConfigFileFull);
10250 path.stripFilename();
10251 if (!RTDirExists(path.c_str()))
10252 {
10253 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10254 if (RT_FAILURE(vrc))
10255 {
10256 return setErrorBoth(E_FAIL, vrc,
10257 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10258 path.c_str(),
10259 vrc);
10260 }
10261 }
10262
10263 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10264 path = mData->m_strConfigFileFull;
10265 RTFILE f = NIL_RTFILE;
10266 vrc = RTFileOpen(&f, path.c_str(),
10267 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10268 if (RT_FAILURE(vrc))
10269 return setErrorBoth(E_FAIL, vrc,
10270 tr("Could not create the settings file '%s' (%Rrc)"),
10271 path.c_str(),
10272 vrc);
10273 RTFileClose(f);
10274 }
10275 if (pfSettingsFileIsNew)
10276 *pfSettingsFileIsNew = fSettingsFileIsNew;
10277
10278 return hrc;
10279}
10280
10281/**
10282 * Saves and commits machine data, user data and hardware data.
10283 *
10284 * Note that on failure, the data remains uncommitted.
10285 *
10286 * @a aFlags may combine the following flags:
10287 *
10288 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10289 * Used when saving settings after an operation that makes them 100%
10290 * correspond to the settings from the current snapshot.
10291 * - SaveS_Force: settings will be saved without doing a deep compare of the
10292 * settings structures. This is used when this is called because snapshots
10293 * have changed to avoid the overhead of the deep compare.
10294 *
10295 * @note Must be called from under this object's write lock. Locks children for
10296 * writing.
10297 *
10298 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10299 * initialized to false and that will be set to true by this function if
10300 * the caller must invoke VirtualBox::i_saveSettings() because the global
10301 * settings have changed. This will happen if a machine rename has been
10302 * saved and the global machine and media registries will therefore need
10303 * updating.
10304 * @param alock Reference to the lock for this machine object.
10305 * @param aFlags Flags.
10306 */
10307HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10308 AutoWriteLock &alock,
10309 int aFlags /*= 0*/)
10310{
10311 LogFlowThisFuncEnter();
10312
10313 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10314
10315 /* make sure child objects are unable to modify the settings while we are
10316 * saving them */
10317 i_ensureNoStateDependencies(alock);
10318
10319 AssertReturn(!i_isSnapshotMachine(),
10320 E_FAIL);
10321
10322 if (!mData->mAccessible)
10323 return setError(VBOX_E_INVALID_VM_STATE,
10324 tr("The machine is not accessible, so cannot save settings"));
10325
10326 HRESULT hrc = S_OK;
10327 PCVBOXCRYPTOIF pCryptoIf = NULL;
10328 const char *pszPassword = NULL;
10329 SecretKey *pKey = NULL;
10330
10331#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10332 if (mData->mstrKeyId.isNotEmpty())
10333 {
10334 /* VM is going to be encrypted. */
10335 alock.release(); /** @todo Revise the locking. */
10336 hrc = mParent->i_retainCryptoIf(&pCryptoIf);
10337 alock.acquire();
10338 if (FAILED(hrc)) return hrc; /* Error is set. */
10339
10340 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
10341 if (RT_SUCCESS(vrc))
10342 pszPassword = (const char *)pKey->getKeyBuffer();
10343 else
10344 {
10345 mParent->i_releaseCryptoIf(pCryptoIf);
10346 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
10347 tr("Failed to retain VM encryption password using ID '%s' with %Rrc"),
10348 mData->mstrKeyId.c_str(), vrc);
10349 }
10350 }
10351#else
10352 RT_NOREF(pKey);
10353#endif
10354
10355 bool fNeedsWrite = false;
10356 bool fSettingsFileIsNew = false;
10357
10358 /* First, prepare to save settings. It will care about renaming the
10359 * settings directory and file if the machine name was changed and about
10360 * creating a new settings file if this is a new machine. */
10361 hrc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings, &fSettingsFileIsNew);
10362 if (FAILED(hrc))
10363 {
10364#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10365 if (pCryptoIf)
10366 {
10367 alock.release(); /** @todo Revise the locking. */
10368 mParent->i_releaseCryptoIf(pCryptoIf);
10369 alock.acquire();
10370 }
10371 if (pKey)
10372 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10373#endif
10374 return hrc;
10375 }
10376
10377 // keep a pointer to the current settings structures
10378 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10379 settings::MachineConfigFile *pNewConfig = NULL;
10380
10381 try
10382 {
10383 // make a fresh one to have everyone write stuff into
10384 pNewConfig = new settings::MachineConfigFile(NULL);
10385 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10386#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10387 pNewConfig->strKeyId = mData->mstrKeyId;
10388 pNewConfig->strKeyStore = mData->mstrKeyStore;
10389#endif
10390
10391 // now go and copy all the settings data from COM to the settings structures
10392 // (this calls i_saveSettings() on all the COM objects in the machine)
10393 i_copyMachineDataToSettings(*pNewConfig);
10394
10395 if (aFlags & SaveS_ResetCurStateModified)
10396 {
10397 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10398 mData->mCurrentStateModified = FALSE;
10399 fNeedsWrite = true; // always, no need to compare
10400 }
10401 else if (aFlags & SaveS_Force)
10402 {
10403 fNeedsWrite = true; // always, no need to compare
10404 }
10405 else
10406 {
10407 if (!mData->mCurrentStateModified)
10408 {
10409 // do a deep compare of the settings that we just saved with the settings
10410 // previously stored in the config file; this invokes MachineConfigFile::operator==
10411 // which does a deep compare of all the settings, which is expensive but less expensive
10412 // than writing out XML in vain
10413 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10414
10415 // could still be modified if any settings changed
10416 mData->mCurrentStateModified = fAnySettingsChanged;
10417
10418 fNeedsWrite = fAnySettingsChanged;
10419 }
10420 else
10421 fNeedsWrite = true;
10422 }
10423
10424 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10425
10426 if (fNeedsWrite)
10427 {
10428 // now spit it all out!
10429 pNewConfig->write(mData->m_strConfigFileFull, pCryptoIf, pszPassword);
10430 if (aFlags & SaveS_RemoveBackup)
10431 i_deleteFile(mData->m_strConfigFileFull + "-prev", true /* fIgnoreFailures */);
10432 }
10433
10434 mData->pMachineConfigFile = pNewConfig;
10435 delete pOldConfig;
10436 i_commit();
10437
10438 // after saving settings, we are no longer different from the XML on disk
10439 mData->flModifications = 0;
10440 }
10441 catch (HRESULT err)
10442 {
10443 // we assume that error info is set by the thrower
10444 hrc = err;
10445
10446 // delete any newly created settings file
10447 if (fSettingsFileIsNew)
10448 i_deleteFile(mData->m_strConfigFileFull, true /* fIgnoreFailures */);
10449
10450 // restore old config
10451 delete pNewConfig;
10452 mData->pMachineConfigFile = pOldConfig;
10453 }
10454 catch (...)
10455 {
10456 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10457 }
10458
10459#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10460 if (pCryptoIf)
10461 {
10462 alock.release(); /** @todo Revise the locking. */
10463 mParent->i_releaseCryptoIf(pCryptoIf);
10464 alock.acquire();
10465 }
10466 if (pKey)
10467 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10468#endif
10469
10470 if (fNeedsWrite)
10471 {
10472 /* Fire the data change event, even on failure (since we've already
10473 * committed all data). This is done only for SessionMachines because
10474 * mutable Machine instances are always not registered (i.e. private
10475 * to the client process that creates them) and thus don't need to
10476 * inform callbacks. */
10477 if (i_isSessionMachine())
10478 mParent->i_onMachineDataChanged(mData->mUuid);
10479 }
10480
10481 LogFlowThisFunc(("hrc=%08X\n", hrc));
10482 LogFlowThisFuncLeave();
10483 return hrc;
10484}
10485
10486/**
10487 * Implementation for saving the machine settings into the given
10488 * settings::MachineConfigFile instance. This copies machine extradata
10489 * from the previous machine config file in the instance data, if any.
10490 *
10491 * This gets called from two locations:
10492 *
10493 * -- Machine::i_saveSettings(), during the regular XML writing;
10494 *
10495 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10496 * exported to OVF and we write the VirtualBox proprietary XML
10497 * into a <vbox:Machine> tag.
10498 *
10499 * This routine fills all the fields in there, including snapshots, *except*
10500 * for the following:
10501 *
10502 * -- fCurrentStateModified. There is some special logic associated with that.
10503 *
10504 * The caller can then call MachineConfigFile::write() or do something else
10505 * with it.
10506 *
10507 * Caller must hold the machine lock!
10508 *
10509 * This throws XML errors and HRESULT, so the caller must have a catch block!
10510 */
10511void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10512{
10513 // deep copy extradata, being extra careful with self assignment (the STL
10514 // map assignment on Mac OS X clang based Xcode isn't checking)
10515 if (&config != mData->pMachineConfigFile)
10516 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10517
10518 config.uuid = mData->mUuid;
10519
10520 // copy name, description, OS type, teleport, UTC etc.
10521 config.machineUserData = mUserData->s;
10522
10523#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10524 config.strStateKeyId = mSSData->strStateKeyId;
10525 config.strStateKeyStore = mSSData->strStateKeyStore;
10526 config.strLogKeyId = mData->mstrLogKeyId;
10527 config.strLogKeyStore = mData->mstrLogKeyStore;
10528#endif
10529
10530 if ( mData->mMachineState == MachineState_Saved
10531 || mData->mMachineState == MachineState_AbortedSaved
10532 || mData->mMachineState == MachineState_Restoring
10533 // when doing certain snapshot operations we may or may not have
10534 // a saved state in the current state, so keep everything as is
10535 || ( ( mData->mMachineState == MachineState_Snapshotting
10536 || mData->mMachineState == MachineState_DeletingSnapshot
10537 || mData->mMachineState == MachineState_RestoringSnapshot)
10538 && (!mSSData->strStateFilePath.isEmpty())
10539 )
10540 )
10541 {
10542 Assert(!mSSData->strStateFilePath.isEmpty());
10543 /* try to make the file name relative to the settings file dir */
10544 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10545 }
10546 else
10547 {
10548 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10549 config.strStateFile.setNull();
10550 }
10551
10552 if (mData->mCurrentSnapshot)
10553 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10554 else
10555 config.uuidCurrentSnapshot.clear();
10556
10557 config.timeLastStateChange = mData->mLastStateChange;
10558 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10559 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10560
10561 HRESULT hrc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart, config.recordingSettings);
10562 if (FAILED(hrc)) throw hrc;
10563
10564 // save machine's media registry if this is VirtualBox 4.0 or later
10565 if (config.canHaveOwnMediaRegistry())
10566 {
10567 // determine machine folder
10568 Utf8Str strMachineFolder = i_getSettingsFileFull();
10569 strMachineFolder.stripFilename();
10570 mParent->i_saveMediaRegistry(config.mediaRegistry,
10571 i_getId(), // only media with registry ID == machine UUID
10572 strMachineFolder);
10573 // this throws HRESULT
10574 }
10575
10576 // save snapshots
10577 hrc = i_saveAllSnapshots(config);
10578 if (FAILED(hrc)) throw hrc;
10579}
10580
10581/**
10582 * Saves all snapshots of the machine into the given machine config file. Called
10583 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10584 * @param config
10585 * @return
10586 */
10587HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10588{
10589 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10590
10591 HRESULT hrc = S_OK;
10592
10593 try
10594 {
10595 config.llFirstSnapshot.clear();
10596
10597 if (mData->mFirstSnapshot)
10598 {
10599 // the settings use a list for "the first snapshot"
10600 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10601
10602 // get reference to the snapshot on the list and work on that
10603 // element straight in the list to avoid excessive copying later
10604 hrc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10605 if (FAILED(hrc)) throw hrc;
10606 }
10607
10608// if (mType == IsSessionMachine)
10609// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10610
10611 }
10612 catch (HRESULT err)
10613 {
10614 /* we assume that error info is set by the thrower */
10615 hrc = err;
10616 }
10617 catch (...)
10618 {
10619 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10620 }
10621
10622 return hrc;
10623}
10624
10625/**
10626 * Saves the VM hardware configuration. It is assumed that the
10627 * given node is empty.
10628 *
10629 * @param data Reference to the settings object for the hardware config.
10630 * @param pDbg Pointer to the settings object for the debugging config
10631 * which happens to live in mHWData.
10632 * @param pAutostart Pointer to the settings object for the autostart config
10633 * which happens to live in mHWData.
10634 * @param recording Reference to reecording settings.
10635 */
10636HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10637 settings::Autostart *pAutostart, settings::RecordingSettings &recording)
10638{
10639 HRESULT hrc = S_OK;
10640
10641 try
10642 {
10643 /* The hardware version attribute (optional).
10644 Automatically upgrade from 1 to current default hardware version
10645 when there is no saved state. (ugly!) */
10646 if ( mHWData->mHWVersion == "1"
10647 && mSSData->strStateFilePath.isEmpty()
10648 )
10649 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10650
10651 data.strVersion = mHWData->mHWVersion;
10652 data.uuid = mHWData->mHardwareUUID;
10653
10654 // CPU
10655 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10656 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10657 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10658 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10659 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10660 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10661 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10662 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10663 data.fPAE = !!mHWData->mPAEEnabled;
10664 data.enmLongMode = mHWData->mLongMode;
10665 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10666 data.fAPIC = !!mHWData->mAPIC;
10667 data.fX2APIC = !!mHWData->mX2APIC;
10668 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10669 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10670 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10671 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10672 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10673 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10674 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10675 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10676 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10677 data.cCPUs = mHWData->mCPUCount;
10678 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10679 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10680 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10681 data.strCpuProfile = mHWData->mCpuProfile;
10682
10683 data.llCpus.clear();
10684 if (data.fCpuHotPlug)
10685 {
10686 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10687 {
10688 if (mHWData->mCPUAttached[idx])
10689 {
10690 settings::Cpu cpu;
10691 cpu.ulId = idx;
10692 data.llCpus.push_back(cpu);
10693 }
10694 }
10695 }
10696
10697 /* Standard and Extended CPUID leafs. */
10698 data.llCpuIdLeafs.clear();
10699 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10700
10701 // memory
10702 data.ulMemorySizeMB = mHWData->mMemorySize;
10703 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10704
10705 // firmware
10706 data.firmwareType = mHWData->mFirmwareType;
10707
10708 // HID
10709 data.pointingHIDType = mHWData->mPointingHIDType;
10710 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10711
10712 // chipset
10713 data.chipsetType = mHWData->mChipsetType;
10714
10715 // iommu
10716 data.iommuType = mHWData->mIommuType;
10717
10718 // paravirt
10719 data.paravirtProvider = mHWData->mParavirtProvider;
10720 data.strParavirtDebug = mHWData->mParavirtDebug;
10721
10722 // emulated USB card reader
10723 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10724
10725 // HPET
10726 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10727
10728 // boot order
10729 data.mapBootOrder.clear();
10730 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10731 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10732
10733 /* VRDEServer settings (optional) */
10734 hrc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10735 if (FAILED(hrc)) throw hrc;
10736
10737 /* BIOS settings (required) */
10738 hrc = mBIOSSettings->i_saveSettings(data.biosSettings);
10739 if (FAILED(hrc)) throw hrc;
10740
10741 /* Recording settings. */
10742 hrc = mRecordingSettings->i_saveSettings(recording);
10743 if (FAILED(hrc)) throw hrc;
10744
10745 /* Trusted Platform Module settings (required) */
10746 hrc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10747 if (FAILED(hrc)) throw hrc;
10748
10749 /* NVRAM settings (required) */
10750 hrc = mNvramStore->i_saveSettings(data.nvramSettings);
10751 if (FAILED(hrc)) throw hrc;
10752
10753 /* GraphicsAdapter settings (required) */
10754 hrc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10755 if (FAILED(hrc)) throw hrc;
10756
10757 /* USB Controller (required) */
10758 data.usbSettings.llUSBControllers.clear();
10759 for (USBControllerList::const_iterator
10760 it = mUSBControllers->begin();
10761 it != mUSBControllers->end();
10762 ++it)
10763 {
10764 ComObjPtr<USBController> ctrl = *it;
10765 settings::USBController settingsCtrl;
10766
10767 settingsCtrl.strName = ctrl->i_getName();
10768 settingsCtrl.enmType = ctrl->i_getControllerType();
10769
10770 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10771 }
10772
10773 /* USB device filters (required) */
10774 hrc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10775 if (FAILED(hrc)) throw hrc;
10776
10777 /* Network adapters (required) */
10778 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10779 data.llNetworkAdapters.clear();
10780 /* Write out only the nominal number of network adapters for this
10781 * chipset type. Since Machine::commit() hasn't been called there
10782 * may be extra NIC settings in the vector. */
10783 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10784 {
10785 settings::NetworkAdapter nic;
10786 nic.ulSlot = (uint32_t)slot;
10787 /* paranoia check... must not be NULL, but must not crash either. */
10788 if (mNetworkAdapters[slot])
10789 {
10790 if (mNetworkAdapters[slot]->i_hasDefaults())
10791 continue;
10792
10793 hrc = mNetworkAdapters[slot]->i_saveSettings(nic);
10794 if (FAILED(hrc)) throw hrc;
10795
10796 data.llNetworkAdapters.push_back(nic);
10797 }
10798 }
10799
10800 /* Serial ports */
10801 data.llSerialPorts.clear();
10802 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10803 {
10804 if (mSerialPorts[slot]->i_hasDefaults())
10805 continue;
10806
10807 settings::SerialPort s;
10808 s.ulSlot = slot;
10809 hrc = mSerialPorts[slot]->i_saveSettings(s);
10810 if (FAILED(hrc)) return hrc;
10811
10812 data.llSerialPorts.push_back(s);
10813 }
10814
10815 /* Parallel ports */
10816 data.llParallelPorts.clear();
10817 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10818 {
10819 if (mParallelPorts[slot]->i_hasDefaults())
10820 continue;
10821
10822 settings::ParallelPort p;
10823 p.ulSlot = slot;
10824 hrc = mParallelPorts[slot]->i_saveSettings(p);
10825 if (FAILED(hrc)) return hrc;
10826
10827 data.llParallelPorts.push_back(p);
10828 }
10829
10830 /* Audio settings */
10831 hrc = mAudioSettings->i_saveSettings(data.audioAdapter);
10832 if (FAILED(hrc)) return hrc;
10833
10834 hrc = i_saveStorageControllers(data.storage);
10835 if (FAILED(hrc)) return hrc;
10836
10837 /* Shared folders */
10838 data.llSharedFolders.clear();
10839 for (HWData::SharedFolderList::const_iterator
10840 it = mHWData->mSharedFolders.begin();
10841 it != mHWData->mSharedFolders.end();
10842 ++it)
10843 {
10844 SharedFolder *pSF = *it;
10845 AutoCaller sfCaller(pSF);
10846 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10847 settings::SharedFolder sf;
10848 sf.strName = pSF->i_getName();
10849 sf.strHostPath = pSF->i_getHostPath();
10850 sf.fWritable = !!pSF->i_isWritable();
10851 sf.fAutoMount = !!pSF->i_isAutoMounted();
10852 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10853
10854 data.llSharedFolders.push_back(sf);
10855 }
10856
10857 // clipboard
10858 data.clipboardMode = mHWData->mClipboardMode;
10859 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10860
10861 // drag'n'drop
10862 data.dndMode = mHWData->mDnDMode;
10863
10864 /* Guest */
10865 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10866
10867 // IO settings
10868 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10869 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10870
10871 /* BandwidthControl (required) */
10872 hrc = mBandwidthControl->i_saveSettings(data.ioSettings);
10873 if (FAILED(hrc)) throw hrc;
10874
10875 /* Host PCI devices */
10876 data.pciAttachments.clear();
10877 for (HWData::PCIDeviceAssignmentList::const_iterator
10878 it = mHWData->mPCIDeviceAssignments.begin();
10879 it != mHWData->mPCIDeviceAssignments.end();
10880 ++it)
10881 {
10882 ComObjPtr<PCIDeviceAttachment> pda = *it;
10883 settings::HostPCIDeviceAttachment hpda;
10884
10885 hrc = pda->i_saveSettings(hpda);
10886 if (FAILED(hrc)) throw hrc;
10887
10888 data.pciAttachments.push_back(hpda);
10889 }
10890
10891 // guest properties
10892 data.llGuestProperties.clear();
10893#ifdef VBOX_WITH_GUEST_PROPS
10894 for (HWData::GuestPropertyMap::const_iterator
10895 it = mHWData->mGuestProperties.begin();
10896 it != mHWData->mGuestProperties.end();
10897 ++it)
10898 {
10899 HWData::GuestProperty property = it->second;
10900
10901 /* Remove transient guest properties at shutdown unless we
10902 * are saving state. Note that restoring snapshot intentionally
10903 * keeps them, they will be removed if appropriate once the final
10904 * machine state is set (as crashes etc. need to work). */
10905 if ( ( mData->mMachineState == MachineState_PoweredOff
10906 || mData->mMachineState == MachineState_Aborted
10907 || mData->mMachineState == MachineState_Teleported)
10908 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10909 continue;
10910 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10911 prop.strName = it->first;
10912 prop.strValue = property.strValue;
10913 prop.timestamp = (uint64_t)property.mTimestamp;
10914 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10915 GuestPropWriteFlags(property.mFlags, szFlags);
10916 prop.strFlags = szFlags;
10917
10918 data.llGuestProperties.push_back(prop);
10919 }
10920
10921 /* I presume this doesn't require a backup(). */
10922 mData->mGuestPropertiesModified = FALSE;
10923#endif /* VBOX_WITH_GUEST_PROPS defined */
10924
10925 hrc = mGuestDebugControl->i_saveSettings(mHWData->mDebugging);
10926 if (FAILED(hrc)) throw hrc;
10927
10928 *pDbg = mHWData->mDebugging; /// @todo r=aeichner: Move this to guest debug control. */
10929 *pAutostart = mHWData->mAutostart;
10930
10931 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10932 }
10933 catch (std::bad_alloc &)
10934 {
10935 return E_OUTOFMEMORY;
10936 }
10937
10938 AssertComRC(hrc);
10939 return hrc;
10940}
10941
10942/**
10943 * Saves the storage controller configuration.
10944 *
10945 * @param data storage settings.
10946 */
10947HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10948{
10949 data.llStorageControllers.clear();
10950
10951 for (StorageControllerList::const_iterator
10952 it = mStorageControllers->begin();
10953 it != mStorageControllers->end();
10954 ++it)
10955 {
10956 ComObjPtr<StorageController> pCtl = *it;
10957
10958 settings::StorageController ctl;
10959 ctl.strName = pCtl->i_getName();
10960 ctl.controllerType = pCtl->i_getControllerType();
10961 ctl.storageBus = pCtl->i_getStorageBus();
10962 ctl.ulInstance = pCtl->i_getInstance();
10963 ctl.fBootable = pCtl->i_getBootable();
10964
10965 /* Save the port count. */
10966 ULONG portCount;
10967 HRESULT hrc = pCtl->COMGETTER(PortCount)(&portCount);
10968 ComAssertComRCRet(hrc, hrc);
10969 ctl.ulPortCount = portCount;
10970
10971 /* Save fUseHostIOCache */
10972 BOOL fUseHostIOCache;
10973 hrc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10974 ComAssertComRCRet(hrc, hrc);
10975 ctl.fUseHostIOCache = !!fUseHostIOCache;
10976
10977 /* save the devices now. */
10978 hrc = i_saveStorageDevices(pCtl, ctl);
10979 ComAssertComRCRet(hrc, hrc);
10980
10981 data.llStorageControllers.push_back(ctl);
10982 }
10983
10984 return S_OK;
10985}
10986
10987/**
10988 * Saves the hard disk configuration.
10989 */
10990HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10991 settings::StorageController &data)
10992{
10993 MediumAttachmentList atts;
10994
10995 HRESULT hrc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10996 if (FAILED(hrc)) return hrc;
10997
10998 data.llAttachedDevices.clear();
10999 for (MediumAttachmentList::const_iterator
11000 it = atts.begin();
11001 it != atts.end();
11002 ++it)
11003 {
11004 settings::AttachedDevice dev;
11005 IMediumAttachment *iA = *it;
11006 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
11007 Medium *pMedium = pAttach->i_getMedium();
11008
11009 dev.deviceType = pAttach->i_getType();
11010 dev.lPort = pAttach->i_getPort();
11011 dev.lDevice = pAttach->i_getDevice();
11012 dev.fPassThrough = pAttach->i_getPassthrough();
11013 dev.fHotPluggable = pAttach->i_getHotPluggable();
11014 if (pMedium)
11015 {
11016 if (pMedium->i_isHostDrive())
11017 dev.strHostDriveSrc = pMedium->i_getLocationFull();
11018 else
11019 dev.uuid = pMedium->i_getId();
11020 dev.fTempEject = pAttach->i_getTempEject();
11021 dev.fNonRotational = pAttach->i_getNonRotational();
11022 dev.fDiscard = pAttach->i_getDiscard();
11023 }
11024
11025 dev.strBwGroup = pAttach->i_getBandwidthGroup();
11026
11027 data.llAttachedDevices.push_back(dev);
11028 }
11029
11030 return S_OK;
11031}
11032
11033/**
11034 * Saves machine state settings as defined by aFlags
11035 * (SaveSTS_* values).
11036 *
11037 * @param aFlags Combination of SaveSTS_* flags.
11038 *
11039 * @note Locks objects for writing.
11040 */
11041HRESULT Machine::i_saveStateSettings(int aFlags)
11042{
11043 if (aFlags == 0)
11044 return S_OK;
11045
11046 AutoCaller autoCaller(this);
11047 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
11048
11049 /* This object's write lock is also necessary to serialize file access
11050 * (prevent concurrent reads and writes) */
11051 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11052
11053 HRESULT hrc = S_OK;
11054
11055 Assert(mData->pMachineConfigFile);
11056
11057 try
11058 {
11059 if (aFlags & SaveSTS_CurStateModified)
11060 mData->pMachineConfigFile->fCurrentStateModified = true;
11061
11062 if (aFlags & SaveSTS_StateFilePath)
11063 {
11064 if (!mSSData->strStateFilePath.isEmpty())
11065 /* try to make the file name relative to the settings file dir */
11066 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
11067 else
11068 mData->pMachineConfigFile->strStateFile.setNull();
11069 }
11070
11071 if (aFlags & SaveSTS_StateTimeStamp)
11072 {
11073 Assert( mData->mMachineState != MachineState_Aborted
11074 || mSSData->strStateFilePath.isEmpty());
11075
11076 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
11077
11078 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
11079 || mData->mMachineState == MachineState_AbortedSaved);
11080/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
11081 }
11082
11083 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
11084 }
11085 catch (...)
11086 {
11087 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
11088 }
11089
11090 return hrc;
11091}
11092
11093/**
11094 * Ensures that the given medium is added to a media registry. If this machine
11095 * was created with 4.0 or later, then the machine registry is used. Otherwise
11096 * the global VirtualBox media registry is used.
11097 *
11098 * Caller must NOT hold machine lock, media tree or any medium locks!
11099 *
11100 * @param pMedium
11101 */
11102void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
11103{
11104 /* Paranoia checks: do not hold machine or media tree locks. */
11105 AssertReturnVoid(!isWriteLockOnCurrentThread());
11106 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
11107
11108 ComObjPtr<Medium> pBase;
11109 {
11110 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11111 pBase = pMedium->i_getBase();
11112 }
11113
11114 /* Paranoia checks: do not hold medium locks. */
11115 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
11116 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
11117
11118 // decide which medium registry to use now that the medium is attached:
11119 Guid uuid;
11120 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
11121 if (fCanHaveOwnMediaRegistry)
11122 // machine XML is VirtualBox 4.0 or higher:
11123 uuid = i_getId(); // machine UUID
11124 else
11125 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
11126
11127 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
11128 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11129 if (pMedium->i_addRegistry(uuid))
11130 mParent->i_markRegistryModified(uuid);
11131
11132 /* For more complex hard disk structures it can happen that the base
11133 * medium isn't yet associated with any medium registry. Do that now. */
11134 if (pMedium != pBase)
11135 {
11136 /* Tree lock needed by Medium::addRegistryAll. */
11137 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11138 if (fCanHaveOwnMediaRegistry && pBase->i_removeRegistryAll(mParent->i_getGlobalRegistryId()))
11139 {
11140 treeLock.release();
11141 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11142 treeLock.acquire();
11143 }
11144 if (pBase->i_addRegistryAll(uuid))
11145 {
11146 treeLock.release();
11147 mParent->i_markRegistryModified(uuid);
11148 }
11149 }
11150}
11151
11152/**
11153 * Physically deletes a file belonging to a machine.
11154 *
11155 * @returns HRESULT
11156 * @retval VBOX_E_FILE_ERROR on failure.
11157 * @param strFile File to delete.
11158 * @param fIgnoreFailures Whether to ignore deletion failures. Defaults to \c false.
11159 * VERR_FILE_NOT_FOUND and VERR_PATH_NOT_FOUND always will be ignored.
11160 * @param strWhat File hint which will be used when setting an error. Optional.
11161 * @param prc Where to return IPRT's status code on failure.
11162 * Optional and can be NULL.
11163 */
11164HRESULT Machine::i_deleteFile(const Utf8Str &strFile, bool fIgnoreFailures /* = false */,
11165 const Utf8Str &strWhat /* = "" */, int *prc /* = NULL */)
11166{
11167 AssertReturn(strFile.isNotEmpty(), E_INVALIDARG);
11168
11169 HRESULT hrc = S_OK;
11170
11171 LogFunc(("Deleting file '%s'\n", strFile.c_str()));
11172
11173 int vrc = RTFileDelete(strFile.c_str());
11174 if (RT_FAILURE(vrc))
11175 {
11176 if ( !fIgnoreFailures
11177 /* Don't (externally) bitch about stuff which doesn't exist. */
11178 && ( vrc != VERR_FILE_NOT_FOUND
11179 && vrc != VERR_PATH_NOT_FOUND
11180 )
11181 )
11182 {
11183 LogRel(("Deleting file '%s' failed: %Rrc\n", strFile.c_str(), vrc));
11184
11185 Utf8StrFmt strError("Error deleting %s '%s' (%Rrc)",
11186 strWhat.isEmpty() ? tr("file") : strWhat.c_str(), strFile.c_str(), vrc);
11187 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, strError.c_str(), strFile.c_str(), vrc);
11188 }
11189 }
11190
11191 if (prc)
11192 *prc = vrc;
11193 return hrc;
11194}
11195
11196/**
11197 * Creates differencing hard disks for all normal hard disks attached to this
11198 * machine and a new set of attachments to refer to created disks.
11199 *
11200 * Used when taking a snapshot or when deleting the current state. Gets called
11201 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
11202 *
11203 * This method assumes that mMediumAttachments contains the original hard disk
11204 * attachments it needs to create diffs for. On success, these attachments will
11205 * be replaced with the created diffs.
11206 *
11207 * Attachments with non-normal hard disks are left as is.
11208 *
11209 * If @a aOnline is @c false then the original hard disks that require implicit
11210 * diffs will be locked for reading. Otherwise it is assumed that they are
11211 * already locked for writing (when the VM was started). Note that in the latter
11212 * case it is responsibility of the caller to lock the newly created diffs for
11213 * writing if this method succeeds.
11214 *
11215 * @param aProgress Progress object to run (must contain at least as
11216 * many operations left as the number of hard disks
11217 * attached).
11218 * @param aWeight Weight of this operation.
11219 * @param aOnline Whether the VM was online prior to this operation.
11220 *
11221 * @note The progress object is not marked as completed, neither on success nor
11222 * on failure. This is a responsibility of the caller.
11223 *
11224 * @note Locks this object and the media tree for writing.
11225 */
11226HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
11227 ULONG aWeight,
11228 bool aOnline)
11229{
11230 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11231
11232 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
11233 AssertReturn(!!pProgressControl, E_INVALIDARG);
11234
11235 AutoCaller autoCaller(this);
11236 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
11237
11238 AutoMultiWriteLock2 alock(this->lockHandle(),
11239 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11240
11241 /* must be in a protective state because we release the lock below */
11242 AssertReturn( mData->mMachineState == MachineState_Snapshotting
11243 || mData->mMachineState == MachineState_OnlineSnapshotting
11244 || mData->mMachineState == MachineState_LiveSnapshotting
11245 || mData->mMachineState == MachineState_RestoringSnapshot
11246 || mData->mMachineState == MachineState_DeletingSnapshot
11247 , E_FAIL);
11248
11249 HRESULT hrc = S_OK;
11250
11251 // use appropriate locked media map (online or offline)
11252 MediumLockListMap lockedMediaOffline;
11253 MediumLockListMap *lockedMediaMap;
11254 if (aOnline)
11255 lockedMediaMap = &mData->mSession.mLockedMedia;
11256 else
11257 lockedMediaMap = &lockedMediaOffline;
11258
11259 try
11260 {
11261 if (!aOnline)
11262 {
11263 /* lock all attached hard disks early to detect "in use"
11264 * situations before creating actual diffs */
11265 for (MediumAttachmentList::const_iterator
11266 it = mMediumAttachments->begin();
11267 it != mMediumAttachments->end();
11268 ++it)
11269 {
11270 MediumAttachment *pAtt = *it;
11271 if (pAtt->i_getType() == DeviceType_HardDisk)
11272 {
11273 Medium *pMedium = pAtt->i_getMedium();
11274 Assert(pMedium);
11275
11276 MediumLockList *pMediumLockList(new MediumLockList());
11277 alock.release();
11278 hrc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11279 NULL /* pToLockWrite */,
11280 false /* fMediumLockWriteAll */,
11281 NULL,
11282 *pMediumLockList);
11283 alock.acquire();
11284 if (FAILED(hrc))
11285 {
11286 delete pMediumLockList;
11287 throw hrc;
11288 }
11289 hrc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11290 if (FAILED(hrc))
11291 throw setError(hrc, tr("Collecting locking information for all attached media failed"));
11292 }
11293 }
11294
11295 /* Now lock all media. If this fails, nothing is locked. */
11296 alock.release();
11297 hrc = lockedMediaMap->Lock();
11298 alock.acquire();
11299 if (FAILED(hrc))
11300 throw setError(hrc, tr("Locking of attached media failed"));
11301 }
11302
11303 /* remember the current list (note that we don't use backup() since
11304 * mMediumAttachments may be already backed up) */
11305 MediumAttachmentList atts = *mMediumAttachments.data();
11306
11307 /* start from scratch */
11308 mMediumAttachments->clear();
11309
11310 /* go through remembered attachments and create diffs for normal hard
11311 * disks and attach them */
11312 for (MediumAttachmentList::const_iterator
11313 it = atts.begin();
11314 it != atts.end();
11315 ++it)
11316 {
11317 MediumAttachment *pAtt = *it;
11318
11319 DeviceType_T devType = pAtt->i_getType();
11320 Medium *pMedium = pAtt->i_getMedium();
11321
11322 if ( devType != DeviceType_HardDisk
11323 || pMedium == NULL
11324 || pMedium->i_getType() != MediumType_Normal)
11325 {
11326 /* copy the attachment as is */
11327
11328 /** @todo the progress object created in SessionMachine::TakeSnaphot
11329 * only expects operations for hard disks. Later other
11330 * device types need to show up in the progress as well. */
11331 if (devType == DeviceType_HardDisk)
11332 {
11333 if (pMedium == NULL)
11334 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11335 aWeight); // weight
11336 else
11337 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11338 pMedium->i_getBase()->i_getName().c_str()).raw(),
11339 aWeight); // weight
11340 }
11341
11342 mMediumAttachments->push_back(pAtt);
11343 continue;
11344 }
11345
11346 /* need a diff */
11347 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11348 pMedium->i_getBase()->i_getName().c_str()).raw(),
11349 aWeight); // weight
11350
11351 Utf8Str strFullSnapshotFolder;
11352 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11353
11354 ComObjPtr<Medium> diff;
11355 diff.createObject();
11356 // store the diff in the same registry as the parent
11357 // (this cannot fail here because we can't create implicit diffs for
11358 // unregistered images)
11359 Guid uuidRegistryParent;
11360 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11361 Assert(fInRegistry); NOREF(fInRegistry);
11362 hrc = diff->init(mParent,
11363 pMedium->i_getPreferredDiffFormat(),
11364 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11365 uuidRegistryParent,
11366 DeviceType_HardDisk);
11367 if (FAILED(hrc)) throw hrc;
11368
11369 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11370 * the push_back? Looks like we're going to release medium with the
11371 * wrong kind of lock (general issue with if we fail anywhere at all)
11372 * and an orphaned VDI in the snapshots folder. */
11373
11374 /* update the appropriate lock list */
11375 MediumLockList *pMediumLockList;
11376 hrc = lockedMediaMap->Get(pAtt, pMediumLockList);
11377 AssertComRCThrowRC(hrc);
11378 if (aOnline)
11379 {
11380 alock.release();
11381 /* The currently attached medium will be read-only, change
11382 * the lock type to read. */
11383 hrc = pMediumLockList->Update(pMedium, false);
11384 alock.acquire();
11385 AssertComRCThrowRC(hrc);
11386 }
11387
11388 /* release the locks before the potentially lengthy operation */
11389 alock.release();
11390 hrc = pMedium->i_createDiffStorage(diff,
11391 pMedium->i_getPreferredDiffVariant(),
11392 pMediumLockList,
11393 NULL /* aProgress */,
11394 true /* aWait */,
11395 false /* aNotify */);
11396 alock.acquire();
11397 if (FAILED(hrc)) throw hrc;
11398
11399 /* actual lock list update is done in Machine::i_commitMedia */
11400
11401 hrc = diff->i_addBackReference(mData->mUuid);
11402 AssertComRCThrowRC(hrc);
11403
11404 /* add a new attachment */
11405 ComObjPtr<MediumAttachment> attachment;
11406 attachment.createObject();
11407 hrc = attachment->init(this,
11408 diff,
11409 pAtt->i_getControllerName(),
11410 pAtt->i_getPort(),
11411 pAtt->i_getDevice(),
11412 DeviceType_HardDisk,
11413 true /* aImplicit */,
11414 false /* aPassthrough */,
11415 false /* aTempEject */,
11416 pAtt->i_getNonRotational(),
11417 pAtt->i_getDiscard(),
11418 pAtt->i_getHotPluggable(),
11419 pAtt->i_getBandwidthGroup());
11420 if (FAILED(hrc)) throw hrc;
11421
11422 hrc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11423 AssertComRCThrowRC(hrc);
11424 mMediumAttachments->push_back(attachment);
11425 }
11426 }
11427 catch (HRESULT hrcXcpt)
11428 {
11429 hrc = hrcXcpt;
11430 }
11431
11432 /* unlock all hard disks we locked when there is no VM */
11433 if (!aOnline)
11434 {
11435 ErrorInfoKeeper eik;
11436
11437 HRESULT hrc2 = lockedMediaMap->Clear();
11438 AssertComRC(hrc2);
11439 }
11440
11441 return hrc;
11442}
11443
11444/**
11445 * Deletes implicit differencing hard disks created either by
11446 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11447 * mMediumAttachments.
11448 *
11449 * Note that to delete hard disks created by #attachDevice() this method is
11450 * called from #i_rollbackMedia() when the changes are rolled back.
11451 *
11452 * @note Locks this object and the media tree for writing.
11453 */
11454HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11455{
11456 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11457
11458 AutoCaller autoCaller(this);
11459 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
11460
11461 AutoMultiWriteLock2 alock(this->lockHandle(),
11462 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11463
11464 /* We absolutely must have backed up state. */
11465 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11466
11467 /* Check if there are any implicitly created diff images. */
11468 bool fImplicitDiffs = false;
11469 for (MediumAttachmentList::const_iterator
11470 it = mMediumAttachments->begin();
11471 it != mMediumAttachments->end();
11472 ++it)
11473 {
11474 const ComObjPtr<MediumAttachment> &pAtt = *it;
11475 if (pAtt->i_isImplicit())
11476 {
11477 fImplicitDiffs = true;
11478 break;
11479 }
11480 }
11481 /* If there is nothing to do, leave early. This saves lots of image locking
11482 * effort. It also avoids a MachineStateChanged event without real reason.
11483 * This is important e.g. when loading a VM config, because there should be
11484 * no events. Otherwise API clients can become thoroughly confused for
11485 * inaccessible VMs (the code for loading VM configs uses this method for
11486 * cleanup if the config makes no sense), as they take such events as an
11487 * indication that the VM is alive, and they would force the VM config to
11488 * be reread, leading to an endless loop. */
11489 if (!fImplicitDiffs)
11490 return S_OK;
11491
11492 HRESULT hrc = S_OK;
11493 MachineState_T oldState = mData->mMachineState;
11494
11495 /* will release the lock before the potentially lengthy operation,
11496 * so protect with the special state (unless already protected) */
11497 if ( oldState != MachineState_Snapshotting
11498 && oldState != MachineState_OnlineSnapshotting
11499 && oldState != MachineState_LiveSnapshotting
11500 && oldState != MachineState_RestoringSnapshot
11501 && oldState != MachineState_DeletingSnapshot
11502 && oldState != MachineState_DeletingSnapshotOnline
11503 && oldState != MachineState_DeletingSnapshotPaused
11504 )
11505 i_setMachineState(MachineState_SettingUp);
11506
11507 // use appropriate locked media map (online or offline)
11508 MediumLockListMap lockedMediaOffline;
11509 MediumLockListMap *lockedMediaMap;
11510 if (aOnline)
11511 lockedMediaMap = &mData->mSession.mLockedMedia;
11512 else
11513 lockedMediaMap = &lockedMediaOffline;
11514
11515 try
11516 {
11517 if (!aOnline)
11518 {
11519 /* lock all attached hard disks early to detect "in use"
11520 * situations before deleting actual diffs */
11521 for (MediumAttachmentList::const_iterator
11522 it = mMediumAttachments->begin();
11523 it != mMediumAttachments->end();
11524 ++it)
11525 {
11526 MediumAttachment *pAtt = *it;
11527 if (pAtt->i_getType() == DeviceType_HardDisk)
11528 {
11529 Medium *pMedium = pAtt->i_getMedium();
11530 Assert(pMedium);
11531
11532 MediumLockList *pMediumLockList(new MediumLockList());
11533 alock.release();
11534 hrc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11535 NULL /* pToLockWrite */,
11536 false /* fMediumLockWriteAll */,
11537 NULL,
11538 *pMediumLockList);
11539 alock.acquire();
11540
11541 if (FAILED(hrc))
11542 {
11543 delete pMediumLockList;
11544 throw hrc;
11545 }
11546
11547 hrc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11548 if (FAILED(hrc))
11549 throw hrc;
11550 }
11551 }
11552
11553 if (FAILED(hrc))
11554 throw hrc;
11555 } // end of offline
11556
11557 /* Lock lists are now up to date and include implicitly created media */
11558
11559 /* Go through remembered attachments and delete all implicitly created
11560 * diffs and fix up the attachment information */
11561 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11562 MediumAttachmentList implicitAtts;
11563 for (MediumAttachmentList::const_iterator
11564 it = mMediumAttachments->begin();
11565 it != mMediumAttachments->end();
11566 ++it)
11567 {
11568 ComObjPtr<MediumAttachment> pAtt = *it;
11569 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11570 if (pMedium.isNull())
11571 continue;
11572
11573 // Implicit attachments go on the list for deletion and back references are removed.
11574 if (pAtt->i_isImplicit())
11575 {
11576 /* Deassociate and mark for deletion */
11577 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11578 hrc = pMedium->i_removeBackReference(mData->mUuid);
11579 if (FAILED(hrc))
11580 throw hrc;
11581 implicitAtts.push_back(pAtt);
11582 continue;
11583 }
11584
11585 /* Was this medium attached before? */
11586 if (!i_findAttachment(oldAtts, pMedium))
11587 {
11588 /* no: de-associate */
11589 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11590 hrc = pMedium->i_removeBackReference(mData->mUuid);
11591 if (FAILED(hrc))
11592 throw hrc;
11593 continue;
11594 }
11595 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11596 }
11597
11598 /* If there are implicit attachments to delete, throw away the lock
11599 * map contents (which will unlock all media) since the medium
11600 * attachments will be rolled back. Below we need to completely
11601 * recreate the lock map anyway since it is infinitely complex to
11602 * do this incrementally (would need reconstructing each attachment
11603 * change, which would be extremely hairy). */
11604 if (implicitAtts.size() != 0)
11605 {
11606 ErrorInfoKeeper eik;
11607
11608 HRESULT hrc2 = lockedMediaMap->Clear();
11609 AssertComRC(hrc2);
11610 }
11611
11612 /* rollback hard disk changes */
11613 mMediumAttachments.rollback();
11614
11615 MultiResult mrc(S_OK);
11616
11617 // Delete unused implicit diffs.
11618 if (implicitAtts.size() != 0)
11619 {
11620 alock.release();
11621
11622 for (MediumAttachmentList::const_iterator
11623 it = implicitAtts.begin();
11624 it != implicitAtts.end();
11625 ++it)
11626 {
11627 // Remove medium associated with this attachment.
11628 ComObjPtr<MediumAttachment> pAtt = *it;
11629 Assert(pAtt);
11630 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11631 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11632 Assert(pMedium);
11633
11634 hrc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11635 // continue on delete failure, just collect error messages
11636 AssertMsg(SUCCEEDED(hrc), ("hrc=%Rhrc it=%s hd=%s\n", hrc, pAtt->i_getLogName(),
11637 pMedium->i_getLocationFull().c_str() ));
11638 mrc = hrc;
11639 }
11640 // Clear the list of deleted implicit attachments now, while not
11641 // holding the lock, as it will ultimately trigger Medium::uninit()
11642 // calls which assume that the media tree lock isn't held.
11643 implicitAtts.clear();
11644
11645 alock.acquire();
11646
11647 /* if there is a VM recreate media lock map as mentioned above,
11648 * otherwise it is a waste of time and we leave things unlocked */
11649 if (aOnline)
11650 {
11651 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11652 /* must never be NULL, but better safe than sorry */
11653 if (!pMachine.isNull())
11654 {
11655 alock.release();
11656 hrc = mData->mSession.mMachine->i_lockMedia();
11657 alock.acquire();
11658 if (FAILED(hrc))
11659 throw hrc;
11660 }
11661 }
11662 }
11663 }
11664 catch (HRESULT hrcXcpt)
11665 {
11666 hrc = hrcXcpt;
11667 }
11668
11669 if (mData->mMachineState == MachineState_SettingUp)
11670 i_setMachineState(oldState);
11671
11672 /* unlock all hard disks we locked when there is no VM */
11673 if (!aOnline)
11674 {
11675 ErrorInfoKeeper eik;
11676
11677 HRESULT hrc2 = lockedMediaMap->Clear();
11678 AssertComRC(hrc2);
11679 }
11680
11681 return hrc;
11682}
11683
11684
11685/**
11686 * Looks through the given list of media attachments for one with the given parameters
11687 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11688 * can be searched as well if needed.
11689 *
11690 * @param ll
11691 * @param aControllerName
11692 * @param aControllerPort
11693 * @param aDevice
11694 * @return
11695 */
11696MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11697 const Utf8Str &aControllerName,
11698 LONG aControllerPort,
11699 LONG aDevice)
11700{
11701 for (MediumAttachmentList::const_iterator
11702 it = ll.begin();
11703 it != ll.end();
11704 ++it)
11705 {
11706 MediumAttachment *pAttach = *it;
11707 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11708 return pAttach;
11709 }
11710
11711 return NULL;
11712}
11713
11714/**
11715 * Looks through the given list of media attachments for one with the given parameters
11716 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11717 * can be searched as well if needed.
11718 *
11719 * @param ll
11720 * @param pMedium
11721 * @return
11722 */
11723MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11724 ComObjPtr<Medium> pMedium)
11725{
11726 for (MediumAttachmentList::const_iterator
11727 it = ll.begin();
11728 it != ll.end();
11729 ++it)
11730 {
11731 MediumAttachment *pAttach = *it;
11732 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11733 if (pMediumThis == pMedium)
11734 return pAttach;
11735 }
11736
11737 return NULL;
11738}
11739
11740/**
11741 * Looks through the given list of media attachments for one with the given parameters
11742 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11743 * can be searched as well if needed.
11744 *
11745 * @param ll
11746 * @param id
11747 * @return
11748 */
11749MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11750 Guid &id)
11751{
11752 for (MediumAttachmentList::const_iterator
11753 it = ll.begin();
11754 it != ll.end();
11755 ++it)
11756 {
11757 MediumAttachment *pAttach = *it;
11758 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11759 if (pMediumThis->i_getId() == id)
11760 return pAttach;
11761 }
11762
11763 return NULL;
11764}
11765
11766/**
11767 * Main implementation for Machine::DetachDevice. This also gets called
11768 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11769 *
11770 * @param pAttach Medium attachment to detach.
11771 * @param writeLock Machine write lock which the caller must have locked once.
11772 * This may be released temporarily in here.
11773 * @param pSnapshot If NULL, then the detachment is for the current machine.
11774 * Otherwise this is for a SnapshotMachine, and this must be
11775 * its snapshot.
11776 * @return
11777 */
11778HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11779 AutoWriteLock &writeLock,
11780 Snapshot *pSnapshot)
11781{
11782 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11783 DeviceType_T mediumType = pAttach->i_getType();
11784
11785 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11786
11787 if (pAttach->i_isImplicit())
11788 {
11789 /* attempt to implicitly delete the implicitly created diff */
11790
11791 /// @todo move the implicit flag from MediumAttachment to Medium
11792 /// and forbid any hard disk operation when it is implicit. Or maybe
11793 /// a special media state for it to make it even more simple.
11794
11795 Assert(mMediumAttachments.isBackedUp());
11796
11797 /* will release the lock before the potentially lengthy operation, so
11798 * protect with the special state */
11799 MachineState_T oldState = mData->mMachineState;
11800 i_setMachineState(MachineState_SettingUp);
11801
11802 writeLock.release();
11803
11804 HRESULT hrc = oldmedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11805
11806 writeLock.acquire();
11807
11808 i_setMachineState(oldState);
11809
11810 if (FAILED(hrc)) return hrc;
11811 }
11812
11813 i_setModified(IsModified_Storage);
11814 mMediumAttachments.backup();
11815 mMediumAttachments->remove(pAttach);
11816
11817 if (!oldmedium.isNull())
11818 {
11819 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11820 if (pSnapshot)
11821 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11822 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11823 else if (mediumType != DeviceType_HardDisk)
11824 oldmedium->i_removeBackReference(mData->mUuid);
11825 }
11826
11827 return S_OK;
11828}
11829
11830/**
11831 * Goes thru all media of the given list and
11832 *
11833 * 1) calls i_detachDevice() on each of them for this machine and
11834 * 2) adds all Medium objects found in the process to the given list,
11835 * depending on cleanupMode.
11836 *
11837 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11838 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11839 * media to the list.
11840 * CleanupMode_DetachAllReturnHardDisksAndVMRemovable adds hard disks and
11841 * also removable media if they are located in the VM folder and referenced
11842 * only by this VM (media prepared by unattended installer).
11843 *
11844 * This gets called from Machine::Unregister, both for the actual Machine and
11845 * the SnapshotMachine objects that might be found in the snapshots.
11846 *
11847 * Requires caller and locking. The machine lock must be passed in because it
11848 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11849 *
11850 * @param writeLock Machine lock from top-level caller; this gets passed to
11851 * i_detachDevice.
11852 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11853 * object if called for a SnapshotMachine.
11854 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11855 * added to llMedia; if Full, then all media get added;
11856 * otherwise no media get added.
11857 * @param llMedia Caller's list to receive Medium objects which got detached so
11858 * caller can close() them, depending on cleanupMode.
11859 * @return
11860 */
11861HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11862 Snapshot *pSnapshot,
11863 CleanupMode_T cleanupMode,
11864 MediaList &llMedia)
11865{
11866 Assert(isWriteLockOnCurrentThread());
11867
11868 HRESULT hrc;
11869
11870 // make a temporary list because i_detachDevice invalidates iterators into
11871 // mMediumAttachments
11872 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11873
11874 for (MediumAttachmentList::iterator
11875 it = llAttachments2.begin();
11876 it != llAttachments2.end();
11877 ++it)
11878 {
11879 ComObjPtr<MediumAttachment> &pAttach = *it;
11880 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11881
11882 if (!pMedium.isNull())
11883 {
11884 AutoCaller mac(pMedium);
11885 if (FAILED(mac.hrc())) return mac.hrc();
11886 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11887 DeviceType_T devType = pMedium->i_getDeviceType();
11888 size_t cBackRefs = pMedium->i_getMachineBackRefCount();
11889 Utf8Str strMediumLocation = pMedium->i_getLocationFull();
11890 strMediumLocation.stripFilename();
11891 Utf8Str strMachineFolder = i_getSettingsFileFull();
11892 strMachineFolder.stripFilename();
11893 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11894 && devType == DeviceType_HardDisk)
11895 || ( cleanupMode == CleanupMode_DetachAllReturnHardDisksAndVMRemovable
11896 && ( devType == DeviceType_HardDisk
11897 || ( cBackRefs <= 1
11898 && strMediumLocation == strMachineFolder
11899 && *pMedium->i_getFirstMachineBackrefId() == i_getId())))
11900 || (cleanupMode == CleanupMode_Full)
11901 )
11902 {
11903 llMedia.push_back(pMedium);
11904 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11905 /* Not allowed to keep this lock as below we need the parent
11906 * medium lock, and the lock order is parent to child. */
11907 lock.release();
11908 /*
11909 * Search for media which are not attached to any machine, but
11910 * in the chain to an attached disk. Media are only consided
11911 * if they are:
11912 * - have only one child
11913 * - no references to any machines
11914 * - are of normal medium type
11915 */
11916 while (!pParent.isNull())
11917 {
11918 AutoCaller mac1(pParent);
11919 if (FAILED(mac1.hrc())) return mac1.hrc();
11920 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11921 if (pParent->i_getChildren().size() == 1)
11922 {
11923 if ( pParent->i_getMachineBackRefCount() == 0
11924 && pParent->i_getType() == MediumType_Normal
11925 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11926 llMedia.push_back(pParent);
11927 }
11928 else
11929 break;
11930 pParent = pParent->i_getParent();
11931 }
11932 }
11933 }
11934
11935 // real machine: then we need to use the proper method
11936 hrc = i_detachDevice(pAttach, writeLock, pSnapshot);
11937
11938 if (FAILED(hrc))
11939 return hrc;
11940 }
11941
11942 return S_OK;
11943}
11944
11945/**
11946 * Perform deferred hard disk detachments.
11947 *
11948 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11949 * changed (not backed up).
11950 *
11951 * If @a aOnline is @c true then this method will also unlock the old hard
11952 * disks for which the new implicit diffs were created and will lock these new
11953 * diffs for writing.
11954 *
11955 * @param aOnline Whether the VM was online prior to this operation.
11956 *
11957 * @note Locks this object for writing!
11958 */
11959void Machine::i_commitMedia(bool aOnline /*= false*/)
11960{
11961 AutoCaller autoCaller(this);
11962 AssertComRCReturnVoid(autoCaller.hrc());
11963
11964 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11965
11966 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11967
11968 HRESULT hrc = S_OK;
11969
11970 /* no attach/detach operations -- nothing to do */
11971 if (!mMediumAttachments.isBackedUp())
11972 return;
11973
11974 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11975 bool fMediaNeedsLocking = false;
11976
11977 /* enumerate new attachments */
11978 for (MediumAttachmentList::const_iterator
11979 it = mMediumAttachments->begin();
11980 it != mMediumAttachments->end();
11981 ++it)
11982 {
11983 MediumAttachment *pAttach = *it;
11984
11985 pAttach->i_commit();
11986
11987 Medium *pMedium = pAttach->i_getMedium();
11988 bool fImplicit = pAttach->i_isImplicit();
11989
11990 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11991 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11992 fImplicit));
11993
11994 /** @todo convert all this Machine-based voodoo to MediumAttachment
11995 * based commit logic. */
11996 if (fImplicit)
11997 {
11998 /* convert implicit attachment to normal */
11999 pAttach->i_setImplicit(false);
12000
12001 if ( aOnline
12002 && pMedium
12003 && pAttach->i_getType() == DeviceType_HardDisk
12004 )
12005 {
12006 /* update the appropriate lock list */
12007 MediumLockList *pMediumLockList;
12008 hrc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
12009 AssertComRC(hrc);
12010 if (pMediumLockList)
12011 {
12012 /* unlock if there's a need to change the locking */
12013 if (!fMediaNeedsLocking)
12014 {
12015 Assert(mData->mSession.mLockedMedia.IsLocked());
12016 hrc = mData->mSession.mLockedMedia.Unlock();
12017 AssertComRC(hrc);
12018 fMediaNeedsLocking = true;
12019 }
12020 hrc = pMediumLockList->Update(pMedium->i_getParent(), false);
12021 AssertComRC(hrc);
12022 hrc = pMediumLockList->Append(pMedium, true);
12023 AssertComRC(hrc);
12024 }
12025 }
12026
12027 continue;
12028 }
12029
12030 if (pMedium)
12031 {
12032 /* was this medium attached before? */
12033 for (MediumAttachmentList::iterator
12034 oldIt = oldAtts.begin();
12035 oldIt != oldAtts.end();
12036 ++oldIt)
12037 {
12038 MediumAttachment *pOldAttach = *oldIt;
12039 if (pOldAttach->i_getMedium() == pMedium)
12040 {
12041 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
12042
12043 /* yes: remove from old to avoid de-association */
12044 oldAtts.erase(oldIt);
12045 break;
12046 }
12047 }
12048 }
12049 }
12050
12051 /* enumerate remaining old attachments and de-associate from the
12052 * current machine state */
12053 for (MediumAttachmentList::const_iterator
12054 it = oldAtts.begin();
12055 it != oldAtts.end();
12056 ++it)
12057 {
12058 MediumAttachment *pAttach = *it;
12059 Medium *pMedium = pAttach->i_getMedium();
12060
12061 /* Detach only hard disks, since DVD/floppy media is detached
12062 * instantly in MountMedium. */
12063 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
12064 {
12065 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
12066
12067 /* now de-associate from the current machine state */
12068 hrc = pMedium->i_removeBackReference(mData->mUuid);
12069 AssertComRC(hrc);
12070
12071 if (aOnline)
12072 {
12073 /* unlock since medium is not used anymore */
12074 MediumLockList *pMediumLockList;
12075 hrc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
12076 if (RT_UNLIKELY(hrc == VBOX_E_INVALID_OBJECT_STATE))
12077 {
12078 /* this happens for online snapshots, there the attachment
12079 * is changing, but only to a diff image created under
12080 * the old one, so there is no separate lock list */
12081 Assert(!pMediumLockList);
12082 }
12083 else
12084 {
12085 AssertComRC(hrc);
12086 if (pMediumLockList)
12087 {
12088 hrc = mData->mSession.mLockedMedia.Remove(pAttach);
12089 AssertComRC(hrc);
12090 }
12091 }
12092 }
12093 }
12094 }
12095
12096 /* take media locks again so that the locking state is consistent */
12097 if (fMediaNeedsLocking)
12098 {
12099 Assert(aOnline);
12100 hrc = mData->mSession.mLockedMedia.Lock();
12101 AssertComRC(hrc);
12102 }
12103
12104 /* commit the hard disk changes */
12105 mMediumAttachments.commit();
12106
12107 if (i_isSessionMachine())
12108 {
12109 /*
12110 * Update the parent machine to point to the new owner.
12111 * This is necessary because the stored parent will point to the
12112 * session machine otherwise and cause crashes or errors later
12113 * when the session machine gets invalid.
12114 */
12115 /** @todo Change the MediumAttachment class to behave like any other
12116 * class in this regard by creating peer MediumAttachment
12117 * objects for session machines and share the data with the peer
12118 * machine.
12119 */
12120 for (MediumAttachmentList::const_iterator
12121 it = mMediumAttachments->begin();
12122 it != mMediumAttachments->end();
12123 ++it)
12124 (*it)->i_updateParentMachine(mPeer);
12125
12126 /* attach new data to the primary machine and reshare it */
12127 mPeer->mMediumAttachments.attach(mMediumAttachments);
12128 }
12129
12130 return;
12131}
12132
12133/**
12134 * Perform deferred deletion of implicitly created diffs.
12135 *
12136 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
12137 * changed (not backed up).
12138 *
12139 * @note Locks this object for writing!
12140 */
12141void Machine::i_rollbackMedia()
12142{
12143 AutoCaller autoCaller(this);
12144 AssertComRCReturnVoid(autoCaller.hrc());
12145
12146 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12147 LogFlowThisFunc(("Entering rollbackMedia\n"));
12148
12149 HRESULT hrc = S_OK;
12150
12151 /* no attach/detach operations -- nothing to do */
12152 if (!mMediumAttachments.isBackedUp())
12153 return;
12154
12155 /* enumerate new attachments */
12156 for (MediumAttachmentList::const_iterator
12157 it = mMediumAttachments->begin();
12158 it != mMediumAttachments->end();
12159 ++it)
12160 {
12161 MediumAttachment *pAttach = *it;
12162 /* Fix up the backrefs for DVD/floppy media. */
12163 if (pAttach->i_getType() != DeviceType_HardDisk)
12164 {
12165 Medium *pMedium = pAttach->i_getMedium();
12166 if (pMedium)
12167 {
12168 hrc = pMedium->i_removeBackReference(mData->mUuid);
12169 AssertComRC(hrc);
12170 }
12171 }
12172
12173 (*it)->i_rollback();
12174
12175 pAttach = *it;
12176 /* Fix up the backrefs for DVD/floppy media. */
12177 if (pAttach->i_getType() != DeviceType_HardDisk)
12178 {
12179 Medium *pMedium = pAttach->i_getMedium();
12180 if (pMedium)
12181 {
12182 hrc = pMedium->i_addBackReference(mData->mUuid);
12183 AssertComRC(hrc);
12184 }
12185 }
12186 }
12187
12188 /** @todo convert all this Machine-based voodoo to MediumAttachment
12189 * based rollback logic. */
12190 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
12191
12192 return;
12193}
12194
12195/**
12196 * Returns true if the settings file is located in the directory named exactly
12197 * as the machine; this means, among other things, that the machine directory
12198 * should be auto-renamed.
12199 *
12200 * @param aSettingsDir if not NULL, the full machine settings file directory
12201 * name will be assigned there.
12202 *
12203 * @note Doesn't lock anything.
12204 * @note Not thread safe (must be called from this object's lock).
12205 */
12206bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
12207{
12208 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12209 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
12210 if (aSettingsDir)
12211 *aSettingsDir = strMachineDirName;
12212 strMachineDirName.stripPath(); // vmname
12213 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12214 strConfigFileOnly.stripPath() // vmname.vbox
12215 .stripSuffix(); // vmname
12216 /** @todo hack, make somehow use of ComposeMachineFilename */
12217 if (mUserData->s.fDirectoryIncludesUUID)
12218 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
12219
12220 AssertReturn(!strMachineDirName.isEmpty(), false);
12221 AssertReturn(!strConfigFileOnly.isEmpty(), false);
12222
12223 return strMachineDirName == strConfigFileOnly;
12224}
12225
12226/**
12227 * Discards all changes to machine settings.
12228 *
12229 * @param aNotify Whether to notify the direct session about changes or not.
12230 *
12231 * @note Locks objects for writing!
12232 */
12233void Machine::i_rollback(bool aNotify)
12234{
12235 AutoCaller autoCaller(this);
12236 AssertComRCReturn(autoCaller.hrc(), (void)0);
12237
12238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12239
12240 if (!mStorageControllers.isNull())
12241 {
12242 if (mStorageControllers.isBackedUp())
12243 {
12244 /* unitialize all new devices (absent in the backed up list). */
12245 StorageControllerList *backedList = mStorageControllers.backedUpData();
12246 for (StorageControllerList::const_iterator
12247 it = mStorageControllers->begin();
12248 it != mStorageControllers->end();
12249 ++it)
12250 {
12251 if ( std::find(backedList->begin(), backedList->end(), *it)
12252 == backedList->end()
12253 )
12254 {
12255 (*it)->uninit();
12256 }
12257 }
12258
12259 /* restore the list */
12260 mStorageControllers.rollback();
12261 }
12262
12263 /* rollback any changes to devices after restoring the list */
12264 if (mData->flModifications & IsModified_Storage)
12265 {
12266 for (StorageControllerList::const_iterator
12267 it = mStorageControllers->begin();
12268 it != mStorageControllers->end();
12269 ++it)
12270 {
12271 (*it)->i_rollback();
12272 }
12273 }
12274 }
12275
12276 if (!mUSBControllers.isNull())
12277 {
12278 if (mUSBControllers.isBackedUp())
12279 {
12280 /* unitialize all new devices (absent in the backed up list). */
12281 USBControllerList *backedList = mUSBControllers.backedUpData();
12282 for (USBControllerList::const_iterator
12283 it = mUSBControllers->begin();
12284 it != mUSBControllers->end();
12285 ++it)
12286 {
12287 if ( std::find(backedList->begin(), backedList->end(), *it)
12288 == backedList->end()
12289 )
12290 {
12291 (*it)->uninit();
12292 }
12293 }
12294
12295 /* restore the list */
12296 mUSBControllers.rollback();
12297 }
12298
12299 /* rollback any changes to devices after restoring the list */
12300 if (mData->flModifications & IsModified_USB)
12301 {
12302 for (USBControllerList::const_iterator
12303 it = mUSBControllers->begin();
12304 it != mUSBControllers->end();
12305 ++it)
12306 {
12307 (*it)->i_rollback();
12308 }
12309 }
12310 }
12311
12312 mUserData.rollback();
12313
12314 mHWData.rollback();
12315
12316 if (mData->flModifications & IsModified_Storage)
12317 i_rollbackMedia();
12318
12319 if (mBIOSSettings)
12320 mBIOSSettings->i_rollback();
12321
12322 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
12323 mRecordingSettings->i_rollback();
12324
12325 if (mTrustedPlatformModule)
12326 mTrustedPlatformModule->i_rollback();
12327
12328 if (mNvramStore)
12329 mNvramStore->i_rollback();
12330
12331 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
12332 mGraphicsAdapter->i_rollback();
12333
12334 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12335 mVRDEServer->i_rollback();
12336
12337 if (mAudioSettings && (mData->flModifications & IsModified_AudioSettings))
12338 mAudioSettings->i_rollback();
12339
12340 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12341 mUSBDeviceFilters->i_rollback();
12342
12343 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12344 mBandwidthControl->i_rollback();
12345
12346 if (mGuestDebugControl && (mData->flModifications & IsModified_GuestDebugControl))
12347 mGuestDebugControl->i_rollback();
12348
12349 if (!mHWData.isNull())
12350 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12351 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12352 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12353 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12354
12355 if (mData->flModifications & IsModified_NetworkAdapters)
12356 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12357 if ( mNetworkAdapters[slot]
12358 && mNetworkAdapters[slot]->i_isModified())
12359 {
12360 mNetworkAdapters[slot]->i_rollback();
12361 networkAdapters[slot] = mNetworkAdapters[slot];
12362 }
12363
12364 if (mData->flModifications & IsModified_SerialPorts)
12365 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12366 if ( mSerialPorts[slot]
12367 && mSerialPorts[slot]->i_isModified())
12368 {
12369 mSerialPorts[slot]->i_rollback();
12370 serialPorts[slot] = mSerialPorts[slot];
12371 }
12372
12373 if (mData->flModifications & IsModified_ParallelPorts)
12374 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12375 if ( mParallelPorts[slot]
12376 && mParallelPorts[slot]->i_isModified())
12377 {
12378 mParallelPorts[slot]->i_rollback();
12379 parallelPorts[slot] = mParallelPorts[slot];
12380 }
12381
12382 if (aNotify)
12383 {
12384 /* inform the direct session about changes */
12385
12386 ComObjPtr<Machine> that = this;
12387 uint32_t flModifications = mData->flModifications;
12388 alock.release();
12389
12390 if (flModifications & IsModified_SharedFolders)
12391 that->i_onSharedFolderChange();
12392
12393 if (flModifications & IsModified_VRDEServer)
12394 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12395 if (flModifications & IsModified_USB)
12396 that->i_onUSBControllerChange();
12397
12398 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12399 if (networkAdapters[slot])
12400 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12401 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12402 if (serialPorts[slot])
12403 that->i_onSerialPortChange(serialPorts[slot]);
12404 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12405 if (parallelPorts[slot])
12406 that->i_onParallelPortChange(parallelPorts[slot]);
12407
12408 if (flModifications & IsModified_Storage)
12409 {
12410 for (StorageControllerList::const_iterator
12411 it = mStorageControllers->begin();
12412 it != mStorageControllers->end();
12413 ++it)
12414 {
12415 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
12416 }
12417 }
12418
12419 if (flModifications & IsModified_GuestDebugControl)
12420 that->i_onGuestDebugControlChange(mGuestDebugControl);
12421
12422#if 0
12423 if (flModifications & IsModified_BandwidthControl)
12424 that->onBandwidthControlChange();
12425#endif
12426 }
12427}
12428
12429/**
12430 * Commits all the changes to machine settings.
12431 *
12432 * Note that this operation is supposed to never fail.
12433 *
12434 * @note Locks this object and children for writing.
12435 */
12436void Machine::i_commit()
12437{
12438 AutoCaller autoCaller(this);
12439 AssertComRCReturnVoid(autoCaller.hrc());
12440
12441 AutoCaller peerCaller(mPeer);
12442 AssertComRCReturnVoid(peerCaller.hrc());
12443
12444 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12445
12446 /*
12447 * use safe commit to ensure Snapshot machines (that share mUserData)
12448 * will still refer to a valid memory location
12449 */
12450 mUserData.commitCopy();
12451
12452 mHWData.commit();
12453
12454 if (mMediumAttachments.isBackedUp())
12455 i_commitMedia(Global::IsOnline(mData->mMachineState));
12456
12457 mBIOSSettings->i_commit();
12458 mRecordingSettings->i_commit();
12459 mTrustedPlatformModule->i_commit();
12460 mNvramStore->i_commit();
12461 mGraphicsAdapter->i_commit();
12462 mVRDEServer->i_commit();
12463 mAudioSettings->i_commit();
12464 mUSBDeviceFilters->i_commit();
12465 mBandwidthControl->i_commit();
12466 mGuestDebugControl->i_commit();
12467
12468 /* Since mNetworkAdapters is a list which might have been changed (resized)
12469 * without using the Backupable<> template we need to handle the copying
12470 * of the list entries manually, including the creation of peers for the
12471 * new objects. */
12472 bool commitNetworkAdapters = false;
12473 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12474 if (mPeer)
12475 {
12476 /* commit everything, even the ones which will go away */
12477 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12478 mNetworkAdapters[slot]->i_commit();
12479 /* copy over the new entries, creating a peer and uninit the original */
12480 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12481 for (size_t slot = 0; slot < newSize; slot++)
12482 {
12483 /* look if this adapter has a peer device */
12484 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12485 if (!peer)
12486 {
12487 /* no peer means the adapter is a newly created one;
12488 * create a peer owning data this data share it with */
12489 peer.createObject();
12490 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12491 }
12492 mPeer->mNetworkAdapters[slot] = peer;
12493 }
12494 /* uninit any no longer needed network adapters */
12495 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12496 mNetworkAdapters[slot]->uninit();
12497 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12498 {
12499 if (mPeer->mNetworkAdapters[slot])
12500 mPeer->mNetworkAdapters[slot]->uninit();
12501 }
12502 /* Keep the original network adapter count until this point, so that
12503 * discarding a chipset type change will not lose settings. */
12504 mNetworkAdapters.resize(newSize);
12505 mPeer->mNetworkAdapters.resize(newSize);
12506 }
12507 else
12508 {
12509 /* we have no peer (our parent is the newly created machine);
12510 * just commit changes to the network adapters */
12511 commitNetworkAdapters = true;
12512 }
12513 if (commitNetworkAdapters)
12514 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12515 mNetworkAdapters[slot]->i_commit();
12516
12517 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12518 mSerialPorts[slot]->i_commit();
12519 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12520 mParallelPorts[slot]->i_commit();
12521
12522 bool commitStorageControllers = false;
12523
12524 if (mStorageControllers.isBackedUp())
12525 {
12526 mStorageControllers.commit();
12527
12528 if (mPeer)
12529 {
12530 /* Commit all changes to new controllers (this will reshare data with
12531 * peers for those who have peers) */
12532 StorageControllerList *newList = new StorageControllerList();
12533 for (StorageControllerList::const_iterator
12534 it = mStorageControllers->begin();
12535 it != mStorageControllers->end();
12536 ++it)
12537 {
12538 (*it)->i_commit();
12539
12540 /* look if this controller has a peer device */
12541 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12542 if (!peer)
12543 {
12544 /* no peer means the device is a newly created one;
12545 * create a peer owning data this device share it with */
12546 peer.createObject();
12547 peer->init(mPeer, *it, true /* aReshare */);
12548 }
12549 else
12550 {
12551 /* remove peer from the old list */
12552 mPeer->mStorageControllers->remove(peer);
12553 }
12554 /* and add it to the new list */
12555 newList->push_back(peer);
12556 }
12557
12558 /* uninit old peer's controllers that are left */
12559 for (StorageControllerList::const_iterator
12560 it = mPeer->mStorageControllers->begin();
12561 it != mPeer->mStorageControllers->end();
12562 ++it)
12563 {
12564 (*it)->uninit();
12565 }
12566
12567 /* attach new list of controllers to our peer */
12568 mPeer->mStorageControllers.attach(newList);
12569 }
12570 else
12571 {
12572 /* we have no peer (our parent is the newly created machine);
12573 * just commit changes to devices */
12574 commitStorageControllers = true;
12575 }
12576 }
12577 else
12578 {
12579 /* the list of controllers itself is not changed,
12580 * just commit changes to controllers themselves */
12581 commitStorageControllers = true;
12582 }
12583
12584 if (commitStorageControllers)
12585 {
12586 for (StorageControllerList::const_iterator
12587 it = mStorageControllers->begin();
12588 it != mStorageControllers->end();
12589 ++it)
12590 {
12591 (*it)->i_commit();
12592 }
12593 }
12594
12595 bool commitUSBControllers = false;
12596
12597 if (mUSBControllers.isBackedUp())
12598 {
12599 mUSBControllers.commit();
12600
12601 if (mPeer)
12602 {
12603 /* Commit all changes to new controllers (this will reshare data with
12604 * peers for those who have peers) */
12605 USBControllerList *newList = new USBControllerList();
12606 for (USBControllerList::const_iterator
12607 it = mUSBControllers->begin();
12608 it != mUSBControllers->end();
12609 ++it)
12610 {
12611 (*it)->i_commit();
12612
12613 /* look if this controller has a peer device */
12614 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12615 if (!peer)
12616 {
12617 /* no peer means the device is a newly created one;
12618 * create a peer owning data this device share it with */
12619 peer.createObject();
12620 peer->init(mPeer, *it, true /* aReshare */);
12621 }
12622 else
12623 {
12624 /* remove peer from the old list */
12625 mPeer->mUSBControllers->remove(peer);
12626 }
12627 /* and add it to the new list */
12628 newList->push_back(peer);
12629 }
12630
12631 /* uninit old peer's controllers that are left */
12632 for (USBControllerList::const_iterator
12633 it = mPeer->mUSBControllers->begin();
12634 it != mPeer->mUSBControllers->end();
12635 ++it)
12636 {
12637 (*it)->uninit();
12638 }
12639
12640 /* attach new list of controllers to our peer */
12641 mPeer->mUSBControllers.attach(newList);
12642 }
12643 else
12644 {
12645 /* we have no peer (our parent is the newly created machine);
12646 * just commit changes to devices */
12647 commitUSBControllers = true;
12648 }
12649 }
12650 else
12651 {
12652 /* the list of controllers itself is not changed,
12653 * just commit changes to controllers themselves */
12654 commitUSBControllers = true;
12655 }
12656
12657 if (commitUSBControllers)
12658 {
12659 for (USBControllerList::const_iterator
12660 it = mUSBControllers->begin();
12661 it != mUSBControllers->end();
12662 ++it)
12663 {
12664 (*it)->i_commit();
12665 }
12666 }
12667
12668 if (i_isSessionMachine())
12669 {
12670 /* attach new data to the primary machine and reshare it */
12671 mPeer->mUserData.attach(mUserData);
12672 mPeer->mHWData.attach(mHWData);
12673 /* mmMediumAttachments is reshared by fixupMedia */
12674 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12675 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12676 }
12677}
12678
12679/**
12680 * Copies all the hardware data from the given machine.
12681 *
12682 * Currently, only called when the VM is being restored from a snapshot. In
12683 * particular, this implies that the VM is not running during this method's
12684 * call.
12685 *
12686 * @note This method must be called from under this object's lock.
12687 *
12688 * @note This method doesn't call #i_commit(), so all data remains backed up and
12689 * unsaved.
12690 */
12691void Machine::i_copyFrom(Machine *aThat)
12692{
12693 AssertReturnVoid(!i_isSnapshotMachine());
12694 AssertReturnVoid(aThat->i_isSnapshotMachine());
12695
12696 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12697
12698 mHWData.assignCopy(aThat->mHWData);
12699
12700 // create copies of all shared folders (mHWData after attaching a copy
12701 // contains just references to original objects)
12702 for (HWData::SharedFolderList::iterator
12703 it = mHWData->mSharedFolders.begin();
12704 it != mHWData->mSharedFolders.end();
12705 ++it)
12706 {
12707 ComObjPtr<SharedFolder> folder;
12708 folder.createObject();
12709 HRESULT hrc = folder->initCopy(i_getMachine(), *it);
12710 AssertComRC(hrc);
12711 *it = folder;
12712 }
12713
12714 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12715 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12716 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12717 mNvramStore->i_copyFrom(aThat->mNvramStore);
12718 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12719 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12720 mAudioSettings->i_copyFrom(aThat->mAudioSettings);
12721 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12722 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12723 mGuestDebugControl->i_copyFrom(aThat->mGuestDebugControl);
12724
12725 /* create private copies of all controllers */
12726 mStorageControllers.backup();
12727 mStorageControllers->clear();
12728 for (StorageControllerList::const_iterator
12729 it = aThat->mStorageControllers->begin();
12730 it != aThat->mStorageControllers->end();
12731 ++it)
12732 {
12733 ComObjPtr<StorageController> ctrl;
12734 ctrl.createObject();
12735 ctrl->initCopy(this, *it);
12736 mStorageControllers->push_back(ctrl);
12737 }
12738
12739 /* create private copies of all USB controllers */
12740 mUSBControllers.backup();
12741 mUSBControllers->clear();
12742 for (USBControllerList::const_iterator
12743 it = aThat->mUSBControllers->begin();
12744 it != aThat->mUSBControllers->end();
12745 ++it)
12746 {
12747 ComObjPtr<USBController> ctrl;
12748 ctrl.createObject();
12749 ctrl->initCopy(this, *it);
12750 mUSBControllers->push_back(ctrl);
12751 }
12752
12753 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12754 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12755 {
12756 if (mNetworkAdapters[slot].isNotNull())
12757 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12758 else
12759 {
12760 unconst(mNetworkAdapters[slot]).createObject();
12761 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12762 }
12763 }
12764 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12765 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12766 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12767 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12768}
12769
12770/**
12771 * Returns whether the given storage controller is hotplug capable.
12772 *
12773 * @returns true if the controller supports hotplugging
12774 * false otherwise.
12775 * @param enmCtrlType The controller type to check for.
12776 */
12777bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12778{
12779 ComPtr<ISystemProperties> systemProperties;
12780 HRESULT hrc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12781 if (FAILED(hrc))
12782 return false;
12783
12784 BOOL aHotplugCapable = FALSE;
12785 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12786
12787 return RT_BOOL(aHotplugCapable);
12788}
12789
12790#ifdef VBOX_WITH_RESOURCE_USAGE_API
12791
12792void Machine::i_getDiskList(MediaList &list)
12793{
12794 for (MediumAttachmentList::const_iterator
12795 it = mMediumAttachments->begin();
12796 it != mMediumAttachments->end();
12797 ++it)
12798 {
12799 MediumAttachment *pAttach = *it;
12800 /* just in case */
12801 AssertContinue(pAttach);
12802
12803 AutoCaller localAutoCallerA(pAttach);
12804 if (FAILED(localAutoCallerA.hrc())) continue;
12805
12806 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12807
12808 if (pAttach->i_getType() == DeviceType_HardDisk)
12809 list.push_back(pAttach->i_getMedium());
12810 }
12811}
12812
12813void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12814{
12815 AssertReturnVoid(isWriteLockOnCurrentThread());
12816 AssertPtrReturnVoid(aCollector);
12817
12818 pm::CollectorHAL *hal = aCollector->getHAL();
12819 /* Create sub metrics */
12820 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12821 "Percentage of processor time spent in user mode by the VM process.");
12822 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12823 "Percentage of processor time spent in kernel mode by the VM process.");
12824 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12825 "Size of resident portion of VM process in memory.");
12826 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12827 "Actual size of all VM disks combined.");
12828 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12829 "Network receive rate.");
12830 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12831 "Network transmit rate.");
12832 /* Create and register base metrics */
12833 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12834 cpuLoadUser, cpuLoadKernel);
12835 aCollector->registerBaseMetric(cpuLoad);
12836 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12837 ramUsageUsed);
12838 aCollector->registerBaseMetric(ramUsage);
12839 MediaList disks;
12840 i_getDiskList(disks);
12841 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12842 diskUsageUsed);
12843 aCollector->registerBaseMetric(diskUsage);
12844
12845 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12846 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12847 new pm::AggregateAvg()));
12848 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12849 new pm::AggregateMin()));
12850 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12851 new pm::AggregateMax()));
12852 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12853 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12854 new pm::AggregateAvg()));
12855 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12856 new pm::AggregateMin()));
12857 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12858 new pm::AggregateMax()));
12859
12860 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12861 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12862 new pm::AggregateAvg()));
12863 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12864 new pm::AggregateMin()));
12865 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12866 new pm::AggregateMax()));
12867
12868 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12869 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12870 new pm::AggregateAvg()));
12871 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12872 new pm::AggregateMin()));
12873 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12874 new pm::AggregateMax()));
12875
12876
12877 /* Guest metrics collector */
12878 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12879 aCollector->registerGuest(mCollectorGuest);
12880 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12881
12882 /* Create sub metrics */
12883 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12884 "Percentage of processor time spent in user mode as seen by the guest.");
12885 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12886 "Percentage of processor time spent in kernel mode as seen by the guest.");
12887 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12888 "Percentage of processor time spent idling as seen by the guest.");
12889
12890 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12891 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12892 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12893 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12894 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12895 pm::SubMetric *guestMemCache = new pm::SubMetric(
12896 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12897
12898 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12899 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12900
12901 /* Create and register base metrics */
12902 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12903 machineNetRx, machineNetTx);
12904 aCollector->registerBaseMetric(machineNetRate);
12905
12906 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12907 guestLoadUser, guestLoadKernel, guestLoadIdle);
12908 aCollector->registerBaseMetric(guestCpuLoad);
12909
12910 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12911 guestMemTotal, guestMemFree,
12912 guestMemBalloon, guestMemShared,
12913 guestMemCache, guestPagedTotal);
12914 aCollector->registerBaseMetric(guestCpuMem);
12915
12916 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12917 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12918 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12919 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12920
12921 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12922 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12923 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12924 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12925
12926 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12927 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12928 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12929 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12930
12931 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12932 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12933 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12934 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12935
12936 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12937 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12938 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12939 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12940
12941 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12942 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12943 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12944 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12945
12946 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12947 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12948 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12949 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12950
12951 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12952 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12953 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12954 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12955
12956 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12957 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12958 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12959 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12960
12961 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12962 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12963 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12964 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12965
12966 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12967 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12968 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12969 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12970}
12971
12972void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12973{
12974 AssertReturnVoid(isWriteLockOnCurrentThread());
12975
12976 if (aCollector)
12977 {
12978 aCollector->unregisterMetricsFor(aMachine);
12979 aCollector->unregisterBaseMetricsFor(aMachine);
12980 }
12981}
12982
12983#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12984
12985
12986////////////////////////////////////////////////////////////////////////////////
12987
12988DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12989
12990HRESULT SessionMachine::FinalConstruct()
12991{
12992 LogFlowThisFunc(("\n"));
12993
12994 mClientToken = NULL;
12995
12996 return BaseFinalConstruct();
12997}
12998
12999void SessionMachine::FinalRelease()
13000{
13001 LogFlowThisFunc(("\n"));
13002
13003 Assert(!mClientToken);
13004 /* paranoia, should not hang around any more */
13005 if (mClientToken)
13006 {
13007 delete mClientToken;
13008 mClientToken = NULL;
13009 }
13010
13011 uninit(Uninit::Unexpected);
13012
13013 BaseFinalRelease();
13014}
13015
13016/**
13017 * @note Must be called only by Machine::LockMachine() from its own write lock.
13018 */
13019HRESULT SessionMachine::init(Machine *aMachine)
13020{
13021 LogFlowThisFuncEnter();
13022 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
13023
13024 AssertReturn(aMachine, E_INVALIDARG);
13025
13026 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
13027
13028 /* Enclose the state transition NotReady->InInit->Ready */
13029 AutoInitSpan autoInitSpan(this);
13030 AssertReturn(autoInitSpan.isOk(), E_FAIL);
13031
13032 HRESULT hrc = S_OK;
13033
13034 RT_ZERO(mAuthLibCtx);
13035
13036 /* create the machine client token */
13037 try
13038 {
13039 mClientToken = new ClientToken(aMachine, this);
13040 if (!mClientToken->isReady())
13041 {
13042 delete mClientToken;
13043 mClientToken = NULL;
13044 hrc = E_FAIL;
13045 }
13046 }
13047 catch (std::bad_alloc &)
13048 {
13049 hrc = E_OUTOFMEMORY;
13050 }
13051 if (FAILED(hrc))
13052 return hrc;
13053
13054 /* memorize the peer Machine */
13055 unconst(mPeer) = aMachine;
13056 /* share the parent pointer */
13057 unconst(mParent) = aMachine->mParent;
13058
13059 /* take the pointers to data to share */
13060 mData.share(aMachine->mData);
13061 mSSData.share(aMachine->mSSData);
13062
13063 mUserData.share(aMachine->mUserData);
13064 mHWData.share(aMachine->mHWData);
13065 mMediumAttachments.share(aMachine->mMediumAttachments);
13066
13067 mStorageControllers.allocate();
13068 for (StorageControllerList::const_iterator
13069 it = aMachine->mStorageControllers->begin();
13070 it != aMachine->mStorageControllers->end();
13071 ++it)
13072 {
13073 ComObjPtr<StorageController> ctl;
13074 ctl.createObject();
13075 ctl->init(this, *it);
13076 mStorageControllers->push_back(ctl);
13077 }
13078
13079 mUSBControllers.allocate();
13080 for (USBControllerList::const_iterator
13081 it = aMachine->mUSBControllers->begin();
13082 it != aMachine->mUSBControllers->end();
13083 ++it)
13084 {
13085 ComObjPtr<USBController> ctl;
13086 ctl.createObject();
13087 ctl->init(this, *it);
13088 mUSBControllers->push_back(ctl);
13089 }
13090
13091 unconst(mBIOSSettings).createObject();
13092 mBIOSSettings->init(this, aMachine->mBIOSSettings);
13093
13094 unconst(mRecordingSettings).createObject();
13095 mRecordingSettings->init(this, aMachine->mRecordingSettings);
13096
13097 unconst(mTrustedPlatformModule).createObject();
13098 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
13099
13100 unconst(mNvramStore).createObject();
13101 mNvramStore->init(this, aMachine->mNvramStore);
13102
13103 /* create another GraphicsAdapter object that will be mutable */
13104 unconst(mGraphicsAdapter).createObject();
13105 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
13106 /* create another VRDEServer object that will be mutable */
13107 unconst(mVRDEServer).createObject();
13108 mVRDEServer->init(this, aMachine->mVRDEServer);
13109 /* create another audio settings object that will be mutable */
13110 unconst(mAudioSettings).createObject();
13111 mAudioSettings->init(this, aMachine->mAudioSettings);
13112 /* create a list of serial ports that will be mutable */
13113 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
13114 {
13115 unconst(mSerialPorts[slot]).createObject();
13116 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
13117 }
13118 /* create a list of parallel ports that will be mutable */
13119 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
13120 {
13121 unconst(mParallelPorts[slot]).createObject();
13122 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
13123 }
13124
13125 /* create another USB device filters object that will be mutable */
13126 unconst(mUSBDeviceFilters).createObject();
13127 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
13128
13129 /* create a list of network adapters that will be mutable */
13130 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
13131 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13132 {
13133 unconst(mNetworkAdapters[slot]).createObject();
13134 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
13135 }
13136
13137 /* create another bandwidth control object that will be mutable */
13138 unconst(mBandwidthControl).createObject();
13139 mBandwidthControl->init(this, aMachine->mBandwidthControl);
13140
13141 unconst(mGuestDebugControl).createObject();
13142 mGuestDebugControl->init(this, aMachine->mGuestDebugControl);
13143
13144 /* default is to delete saved state on Saved -> PoweredOff transition */
13145 mRemoveSavedState = true;
13146
13147 /* Confirm a successful initialization when it's the case */
13148 autoInitSpan.setSucceeded();
13149
13150 miNATNetworksStarted = 0;
13151
13152 LogFlowThisFuncLeave();
13153 return hrc;
13154}
13155
13156/**
13157 * Uninitializes this session object. If the reason is other than
13158 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
13159 * or the client watcher code.
13160 *
13161 * @param aReason uninitialization reason
13162 *
13163 * @note Locks mParent + this object for writing.
13164 */
13165void SessionMachine::uninit(Uninit::Reason aReason)
13166{
13167 LogFlowThisFuncEnter();
13168 LogFlowThisFunc(("reason=%d\n", aReason));
13169
13170 /*
13171 * Strongly reference ourselves to prevent this object deletion after
13172 * mData->mSession.mMachine.setNull() below (which can release the last
13173 * reference and call the destructor). Important: this must be done before
13174 * accessing any members (and before AutoUninitSpan that does it as well).
13175 * This self reference will be released as the very last step on return.
13176 */
13177 ComObjPtr<SessionMachine> selfRef;
13178 if (aReason != Uninit::Unexpected)
13179 selfRef = this;
13180
13181 /* Enclose the state transition Ready->InUninit->NotReady */
13182 AutoUninitSpan autoUninitSpan(this);
13183 if (autoUninitSpan.uninitDone())
13184 {
13185 LogFlowThisFunc(("Already uninitialized\n"));
13186 LogFlowThisFuncLeave();
13187 return;
13188 }
13189
13190 if (autoUninitSpan.initFailed())
13191 {
13192 /* We've been called by init() because it's failed. It's not really
13193 * necessary (nor it's safe) to perform the regular uninit sequence
13194 * below, the following is enough.
13195 */
13196 LogFlowThisFunc(("Initialization failed.\n"));
13197 /* destroy the machine client token */
13198 if (mClientToken)
13199 {
13200 delete mClientToken;
13201 mClientToken = NULL;
13202 }
13203 uninitDataAndChildObjects();
13204 mData.free();
13205 unconst(mParent) = NULL;
13206 unconst(mPeer) = NULL;
13207 LogFlowThisFuncLeave();
13208 return;
13209 }
13210
13211 MachineState_T lastState;
13212 {
13213 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
13214 lastState = mData->mMachineState;
13215 }
13216 NOREF(lastState);
13217
13218#ifdef VBOX_WITH_USB
13219 // release all captured USB devices, but do this before requesting the locks below
13220 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
13221 {
13222 /* Console::captureUSBDevices() is called in the VM process only after
13223 * setting the machine state to Starting or Restoring.
13224 * Console::detachAllUSBDevices() will be called upon successful
13225 * termination. So, we need to release USB devices only if there was
13226 * an abnormal termination of a running VM.
13227 *
13228 * This is identical to SessionMachine::DetachAllUSBDevices except
13229 * for the aAbnormal argument. */
13230 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13231 AssertComRC(hrc);
13232 NOREF(hrc);
13233
13234 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13235 if (service)
13236 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
13237 }
13238#endif /* VBOX_WITH_USB */
13239
13240 // we need to lock this object in uninit() because the lock is shared
13241 // with mPeer (as well as data we modify below). mParent lock is needed
13242 // by several calls to it.
13243 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
13244
13245#ifdef VBOX_WITH_RESOURCE_USAGE_API
13246 /*
13247 * It is safe to call Machine::i_unregisterMetrics() here because
13248 * PerformanceCollector::samplerCallback no longer accesses guest methods
13249 * holding the lock.
13250 */
13251 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
13252 /* The guest must be unregistered after its metrics (@bugref{5949}). */
13253 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
13254 if (mCollectorGuest)
13255 {
13256 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
13257 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
13258 mCollectorGuest = NULL;
13259 }
13260#endif
13261
13262 if (aReason == Uninit::Abnormal)
13263 {
13264 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
13265
13266 /*
13267 * Move the VM to the 'Aborted' machine state unless we are restoring a
13268 * VM that was in the 'Saved' machine state. In that case, if the VM
13269 * fails before reaching either the 'Restoring' machine state or the
13270 * 'Running' machine state then we set the machine state to
13271 * 'AbortedSaved' in order to preserve the saved state file so that the
13272 * VM can be restored in the future.
13273 */
13274 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
13275 i_setMachineState(MachineState_AbortedSaved);
13276 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
13277 i_setMachineState(MachineState_Aborted);
13278 }
13279
13280 // any machine settings modified?
13281 if (mData->flModifications)
13282 {
13283 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
13284 i_rollback(false /* aNotify */);
13285 }
13286
13287 mData->mSession.mPID = NIL_RTPROCESS;
13288
13289 if (aReason == Uninit::Unexpected)
13290 {
13291 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
13292 * client watcher thread to update the set of machines that have open
13293 * sessions. */
13294 mParent->i_updateClientWatcher();
13295 }
13296
13297 /* uninitialize all remote controls */
13298 if (mData->mSession.mRemoteControls.size())
13299 {
13300 LogFlowThisFunc(("Closing remote sessions (%d):\n",
13301 mData->mSession.mRemoteControls.size()));
13302
13303 /* Always restart a the beginning, since the iterator is invalidated
13304 * by using erase(). */
13305 for (Data::Session::RemoteControlList::iterator
13306 it = mData->mSession.mRemoteControls.begin();
13307 it != mData->mSession.mRemoteControls.end();
13308 it = mData->mSession.mRemoteControls.begin())
13309 {
13310 ComPtr<IInternalSessionControl> pControl = *it;
13311 mData->mSession.mRemoteControls.erase(it);
13312 multilock.release();
13313 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
13314 HRESULT hrc = pControl->Uninitialize();
13315 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", hrc));
13316 if (FAILED(hrc))
13317 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
13318 multilock.acquire();
13319 }
13320 mData->mSession.mRemoteControls.clear();
13321 }
13322
13323 /* Remove all references to the NAT network service. The service will stop
13324 * if all references (also from other VMs) are removed. */
13325 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
13326 {
13327 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13328 {
13329 BOOL enabled;
13330 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13331 if ( FAILED(hrc)
13332 || !enabled)
13333 continue;
13334
13335 NetworkAttachmentType_T type;
13336 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13337 if ( SUCCEEDED(hrc)
13338 && type == NetworkAttachmentType_NATNetwork)
13339 {
13340 Bstr name;
13341 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13342 if (SUCCEEDED(hrc))
13343 {
13344 multilock.release();
13345 Utf8Str strName(name);
13346 LogRel(("VM '%s' stops using NAT network '%s'\n",
13347 mUserData->s.strName.c_str(), strName.c_str()));
13348 mParent->i_natNetworkRefDec(strName);
13349 multilock.acquire();
13350 }
13351 }
13352 }
13353 }
13354
13355 /*
13356 * An expected uninitialization can come only from #i_checkForDeath().
13357 * Otherwise it means that something's gone really wrong (for example,
13358 * the Session implementation has released the VirtualBox reference
13359 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
13360 * etc). However, it's also possible, that the client releases the IPC
13361 * semaphore correctly (i.e. before it releases the VirtualBox reference),
13362 * but the VirtualBox release event comes first to the server process.
13363 * This case is practically possible, so we should not assert on an
13364 * unexpected uninit, just log a warning.
13365 */
13366
13367 if (aReason == Uninit::Unexpected)
13368 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
13369
13370 if (aReason != Uninit::Normal)
13371 {
13372 mData->mSession.mDirectControl.setNull();
13373 }
13374 else
13375 {
13376 /* this must be null here (see #OnSessionEnd()) */
13377 Assert(mData->mSession.mDirectControl.isNull());
13378 Assert(mData->mSession.mState == SessionState_Unlocking);
13379 Assert(!mData->mSession.mProgress.isNull());
13380 }
13381 if (mData->mSession.mProgress)
13382 {
13383 if (aReason == Uninit::Normal)
13384 mData->mSession.mProgress->i_notifyComplete(S_OK);
13385 else
13386 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
13387 COM_IIDOF(ISession),
13388 getComponentName(),
13389 tr("The VM session was aborted"));
13390 mData->mSession.mProgress.setNull();
13391 }
13392
13393 if (mConsoleTaskData.mProgress)
13394 {
13395 Assert(aReason == Uninit::Abnormal);
13396 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
13397 COM_IIDOF(ISession),
13398 getComponentName(),
13399 tr("The VM session was aborted"));
13400 mConsoleTaskData.mProgress.setNull();
13401 }
13402
13403 /* remove the association between the peer machine and this session machine */
13404 Assert( (SessionMachine*)mData->mSession.mMachine == this
13405 || aReason == Uninit::Unexpected);
13406
13407 /* reset the rest of session data */
13408 mData->mSession.mLockType = LockType_Null;
13409 mData->mSession.mMachine.setNull();
13410 mData->mSession.mState = SessionState_Unlocked;
13411 mData->mSession.mName.setNull();
13412
13413 /* destroy the machine client token before leaving the exclusive lock */
13414 if (mClientToken)
13415 {
13416 delete mClientToken;
13417 mClientToken = NULL;
13418 }
13419
13420 /* fire an event */
13421 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
13422
13423 uninitDataAndChildObjects();
13424
13425 /* free the essential data structure last */
13426 mData.free();
13427
13428 /* release the exclusive lock before setting the below two to NULL */
13429 multilock.release();
13430
13431 unconst(mParent) = NULL;
13432 unconst(mPeer) = NULL;
13433
13434 AuthLibUnload(&mAuthLibCtx);
13435
13436 LogFlowThisFuncLeave();
13437}
13438
13439// util::Lockable interface
13440////////////////////////////////////////////////////////////////////////////////
13441
13442/**
13443 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13444 * with the primary Machine instance (mPeer).
13445 */
13446RWLockHandle *SessionMachine::lockHandle() const
13447{
13448 AssertReturn(mPeer != NULL, NULL);
13449 return mPeer->lockHandle();
13450}
13451
13452// IInternalMachineControl methods
13453////////////////////////////////////////////////////////////////////////////////
13454
13455/**
13456 * Passes collected guest statistics to performance collector object
13457 */
13458HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13459 ULONG aCpuKernel, ULONG aCpuIdle,
13460 ULONG aMemTotal, ULONG aMemFree,
13461 ULONG aMemBalloon, ULONG aMemShared,
13462 ULONG aMemCache, ULONG aPageTotal,
13463 ULONG aAllocVMM, ULONG aFreeVMM,
13464 ULONG aBalloonedVMM, ULONG aSharedVMM,
13465 ULONG aVmNetRx, ULONG aVmNetTx)
13466{
13467#ifdef VBOX_WITH_RESOURCE_USAGE_API
13468 if (mCollectorGuest)
13469 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13470 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13471 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13472 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13473
13474 return S_OK;
13475#else
13476 NOREF(aValidStats);
13477 NOREF(aCpuUser);
13478 NOREF(aCpuKernel);
13479 NOREF(aCpuIdle);
13480 NOREF(aMemTotal);
13481 NOREF(aMemFree);
13482 NOREF(aMemBalloon);
13483 NOREF(aMemShared);
13484 NOREF(aMemCache);
13485 NOREF(aPageTotal);
13486 NOREF(aAllocVMM);
13487 NOREF(aFreeVMM);
13488 NOREF(aBalloonedVMM);
13489 NOREF(aSharedVMM);
13490 NOREF(aVmNetRx);
13491 NOREF(aVmNetTx);
13492 return E_NOTIMPL;
13493#endif
13494}
13495
13496////////////////////////////////////////////////////////////////////////////////
13497//
13498// SessionMachine task records
13499//
13500////////////////////////////////////////////////////////////////////////////////
13501
13502/**
13503 * Task record for saving the machine state.
13504 */
13505class SessionMachine::SaveStateTask
13506 : public Machine::Task
13507{
13508public:
13509 SaveStateTask(SessionMachine *m,
13510 Progress *p,
13511 const Utf8Str &t,
13512 Reason_T enmReason,
13513 const Utf8Str &strStateFilePath)
13514 : Task(m, p, t),
13515 m_enmReason(enmReason),
13516 m_strStateFilePath(strStateFilePath)
13517 {}
13518
13519private:
13520 void handler()
13521 {
13522 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13523 }
13524
13525 Reason_T m_enmReason;
13526 Utf8Str m_strStateFilePath;
13527
13528 friend class SessionMachine;
13529};
13530
13531/**
13532 * Task thread implementation for SessionMachine::SaveState(), called from
13533 * SessionMachine::taskHandler().
13534 *
13535 * @note Locks this object for writing.
13536 *
13537 * @param task
13538 * @return
13539 */
13540void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13541{
13542 LogFlowThisFuncEnter();
13543
13544 AutoCaller autoCaller(this);
13545 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13546 if (FAILED(autoCaller.hrc()))
13547 {
13548 /* we might have been uninitialized because the session was accidentally
13549 * closed by the client, so don't assert */
13550 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
13551 task.m_pProgress->i_notifyComplete(hrc);
13552 LogFlowThisFuncLeave();
13553 return;
13554 }
13555
13556 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13557
13558 HRESULT hrc = S_OK;
13559
13560 try
13561 {
13562 ComPtr<IInternalSessionControl> directControl;
13563 if (mData->mSession.mLockType == LockType_VM)
13564 directControl = mData->mSession.mDirectControl;
13565 if (directControl.isNull())
13566 throw setError(VBOX_E_INVALID_VM_STATE,
13567 tr("Trying to save state without a running VM"));
13568 alock.release();
13569 BOOL fSuspendedBySave;
13570 hrc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13571 Assert(!fSuspendedBySave);
13572 alock.acquire();
13573
13574 AssertStmt( (SUCCEEDED(hrc) && mData->mMachineState == MachineState_Saved)
13575 || (FAILED(hrc) && mData->mMachineState == MachineState_Saving),
13576 throw E_FAIL);
13577
13578 if (SUCCEEDED(hrc))
13579 {
13580 mSSData->strStateFilePath = task.m_strStateFilePath;
13581
13582 /* save all VM settings */
13583 hrc = i_saveSettings(NULL, alock);
13584 // no need to check whether VirtualBox.xml needs saving also since
13585 // we can't have a name change pending at this point
13586 }
13587 else
13588 {
13589 // On failure, set the state to the state we had at the beginning.
13590 i_setMachineState(task.m_machineStateBackup);
13591 i_updateMachineStateOnClient();
13592
13593 // Delete the saved state file (might have been already created).
13594 // No need to check whether this is shared with a snapshot here
13595 // because we certainly created a fresh saved state file here.
13596 i_deleteFile(task.m_strStateFilePath, true /* fIgnoreFailures */);
13597 }
13598 }
13599 catch (HRESULT hrcXcpt)
13600 {
13601 hrc = hrcXcpt;
13602 }
13603
13604 task.m_pProgress->i_notifyComplete(hrc);
13605
13606 LogFlowThisFuncLeave();
13607}
13608
13609/**
13610 * @note Locks this object for writing.
13611 */
13612HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13613{
13614 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13615}
13616
13617HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13618{
13619 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13620
13621 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
13622 if (FAILED(hrc)) return hrc;
13623
13624 if ( mData->mMachineState != MachineState_Running
13625 && mData->mMachineState != MachineState_Paused
13626 )
13627 return setError(VBOX_E_INVALID_VM_STATE,
13628 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13629 Global::stringifyMachineState(mData->mMachineState));
13630
13631 ComObjPtr<Progress> pProgress;
13632 pProgress.createObject();
13633 hrc = pProgress->init(i_getVirtualBox(),
13634 static_cast<IMachine *>(this) /* aInitiator */,
13635 tr("Saving the execution state of the virtual machine"),
13636 FALSE /* aCancelable */);
13637 if (FAILED(hrc))
13638 return hrc;
13639
13640 Utf8Str strStateFilePath;
13641 i_composeSavedStateFilename(strStateFilePath);
13642
13643 /* create and start the task on a separate thread (note that it will not
13644 * start working until we release alock) */
13645 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13646 hrc = pTask->createThread();
13647 if (FAILED(hrc))
13648 return hrc;
13649
13650 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13651 i_setMachineState(MachineState_Saving);
13652 i_updateMachineStateOnClient();
13653
13654 pProgress.queryInterfaceTo(aProgress.asOutParam());
13655
13656 return S_OK;
13657}
13658
13659/**
13660 * @note Locks this object for writing.
13661 */
13662HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13663{
13664 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13665
13666 HRESULT hrc = i_checkStateDependency(MutableStateDep);
13667 if (FAILED(hrc)) return hrc;
13668
13669 if ( mData->mMachineState != MachineState_PoweredOff
13670 && mData->mMachineState != MachineState_Teleported
13671 && mData->mMachineState != MachineState_Aborted
13672 )
13673 return setError(VBOX_E_INVALID_VM_STATE,
13674 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13675 Global::stringifyMachineState(mData->mMachineState));
13676
13677 com::Utf8Str stateFilePathFull;
13678 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13679 if (RT_FAILURE(vrc))
13680 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13681 tr("Invalid saved state file path '%s' (%Rrc)"),
13682 aSavedStateFile.c_str(),
13683 vrc);
13684
13685 mSSData->strStateFilePath = stateFilePathFull;
13686
13687 /* The below i_setMachineState() will detect the state transition and will
13688 * update the settings file */
13689
13690 return i_setMachineState(MachineState_Saved);
13691}
13692
13693/**
13694 * @note Locks this object for writing.
13695 */
13696HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13697{
13698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13699
13700 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
13701 if (FAILED(hrc)) return hrc;
13702
13703 if ( mData->mMachineState != MachineState_Saved
13704 && mData->mMachineState != MachineState_AbortedSaved)
13705 return setError(VBOX_E_INVALID_VM_STATE,
13706 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13707 Global::stringifyMachineState(mData->mMachineState));
13708
13709 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13710
13711 /*
13712 * Saved -> PoweredOff transition will be detected in the SessionMachine
13713 * and properly handled.
13714 */
13715 hrc = i_setMachineState(MachineState_PoweredOff);
13716 return hrc;
13717}
13718
13719
13720/**
13721 * @note Locks the same as #i_setMachineState() does.
13722 */
13723HRESULT SessionMachine::updateState(MachineState_T aState)
13724{
13725 return i_setMachineState(aState);
13726}
13727
13728/**
13729 * @note Locks this object for writing.
13730 */
13731HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13732{
13733 IProgress *pProgress(aProgress);
13734
13735 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13736
13737 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13738
13739 if (mData->mSession.mState != SessionState_Locked)
13740 return VBOX_E_INVALID_OBJECT_STATE;
13741
13742 if (!mData->mSession.mProgress.isNull())
13743 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13744
13745 /* If we didn't reference the NAT network service yet, add a reference to
13746 * force a start */
13747 if (miNATNetworksStarted < 1)
13748 {
13749 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13750 {
13751 BOOL enabled;
13752 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13753 if ( FAILED(hrc)
13754 || !enabled)
13755 continue;
13756
13757 NetworkAttachmentType_T type;
13758 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13759 if ( SUCCEEDED(hrc)
13760 && type == NetworkAttachmentType_NATNetwork)
13761 {
13762 Bstr name;
13763 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13764 if (SUCCEEDED(hrc))
13765 {
13766 Utf8Str strName(name);
13767 LogRel(("VM '%s' starts using NAT network '%s'\n",
13768 mUserData->s.strName.c_str(), strName.c_str()));
13769 mPeer->lockHandle()->unlockWrite();
13770 mParent->i_natNetworkRefInc(strName);
13771#ifdef RT_LOCK_STRICT
13772 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13773#else
13774 mPeer->lockHandle()->lockWrite();
13775#endif
13776 }
13777 }
13778 }
13779 miNATNetworksStarted++;
13780 }
13781
13782 LogFlowThisFunc(("returns S_OK.\n"));
13783 return S_OK;
13784}
13785
13786/**
13787 * @note Locks this object for writing.
13788 */
13789HRESULT SessionMachine::endPowerUp(LONG aResult)
13790{
13791 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13792
13793 if (mData->mSession.mState != SessionState_Locked)
13794 return VBOX_E_INVALID_OBJECT_STATE;
13795
13796 /* Finalize the LaunchVMProcess progress object. */
13797 if (mData->mSession.mProgress)
13798 {
13799 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13800 mData->mSession.mProgress.setNull();
13801 }
13802
13803 if (SUCCEEDED((HRESULT)aResult))
13804 {
13805#ifdef VBOX_WITH_RESOURCE_USAGE_API
13806 /* The VM has been powered up successfully, so it makes sense
13807 * now to offer the performance metrics for a running machine
13808 * object. Doing it earlier wouldn't be safe. */
13809 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13810 mData->mSession.mPID);
13811#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13812 }
13813
13814 return S_OK;
13815}
13816
13817/**
13818 * @note Locks this object for writing.
13819 */
13820HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13821{
13822 LogFlowThisFuncEnter();
13823
13824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13825
13826 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13827 E_FAIL);
13828
13829 /* create a progress object to track operation completion */
13830 ComObjPtr<Progress> pProgress;
13831 pProgress.createObject();
13832 pProgress->init(i_getVirtualBox(),
13833 static_cast<IMachine *>(this) /* aInitiator */,
13834 tr("Stopping the virtual machine"),
13835 FALSE /* aCancelable */);
13836
13837 /* fill in the console task data */
13838 mConsoleTaskData.mLastState = mData->mMachineState;
13839 mConsoleTaskData.mProgress = pProgress;
13840
13841 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13842 i_setMachineState(MachineState_Stopping);
13843
13844 pProgress.queryInterfaceTo(aProgress.asOutParam());
13845
13846 return S_OK;
13847}
13848
13849/**
13850 * @note Locks this object for writing.
13851 */
13852HRESULT SessionMachine::endPoweringDown(LONG aResult,
13853 const com::Utf8Str &aErrMsg)
13854{
13855 HRESULT const hrcResult = (HRESULT)aResult;
13856 LogFlowThisFuncEnter();
13857
13858 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13859
13860 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13861 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13862 && mConsoleTaskData.mLastState != MachineState_Null,
13863 E_FAIL);
13864
13865 /*
13866 * On failure, set the state to the state we had when BeginPoweringDown()
13867 * was called (this is expected by Console::PowerDown() and the associated
13868 * task). On success the VM process already changed the state to
13869 * MachineState_PoweredOff, so no need to do anything.
13870 */
13871 if (FAILED(hrcResult))
13872 i_setMachineState(mConsoleTaskData.mLastState);
13873
13874 /* notify the progress object about operation completion */
13875 Assert(mConsoleTaskData.mProgress);
13876 if (SUCCEEDED(hrcResult))
13877 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13878 else
13879 {
13880 if (aErrMsg.length())
13881 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13882 COM_IIDOF(ISession),
13883 getComponentName(),
13884 aErrMsg.c_str());
13885 else
13886 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13887 }
13888
13889 /* clear out the temporary saved state data */
13890 mConsoleTaskData.mLastState = MachineState_Null;
13891 mConsoleTaskData.mProgress.setNull();
13892
13893 LogFlowThisFuncLeave();
13894 return S_OK;
13895}
13896
13897
13898/**
13899 * Goes through the USB filters of the given machine to see if the given
13900 * device matches any filter or not.
13901 *
13902 * @note Locks the same as USBController::hasMatchingFilter() does.
13903 */
13904HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13905 BOOL *aMatched,
13906 ULONG *aMaskedInterfaces)
13907{
13908 LogFlowThisFunc(("\n"));
13909
13910#ifdef VBOX_WITH_USB
13911 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13912#else
13913 NOREF(aDevice);
13914 NOREF(aMaskedInterfaces);
13915 *aMatched = FALSE;
13916#endif
13917
13918 return S_OK;
13919}
13920
13921/**
13922 * @note Locks the same as Host::captureUSBDevice() does.
13923 */
13924HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13925{
13926 LogFlowThisFunc(("\n"));
13927
13928#ifdef VBOX_WITH_USB
13929 /* if captureDeviceForVM() fails, it must have set extended error info */
13930 clearError();
13931 MultiResult hrc = mParent->i_host()->i_checkUSBProxyService();
13932 if (FAILED(hrc) || SUCCEEDED_WARNING(hrc))
13933 return hrc;
13934
13935 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13936 AssertReturn(service, E_FAIL);
13937 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13938#else
13939 RT_NOREF(aId, aCaptureFilename);
13940 return E_NOTIMPL;
13941#endif
13942}
13943
13944/**
13945 * @note Locks the same as Host::detachUSBDevice() does.
13946 */
13947HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13948 BOOL aDone)
13949{
13950 LogFlowThisFunc(("\n"));
13951
13952#ifdef VBOX_WITH_USB
13953 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13954 AssertReturn(service, E_FAIL);
13955 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13956#else
13957 NOREF(aId);
13958 NOREF(aDone);
13959 return E_NOTIMPL;
13960#endif
13961}
13962
13963/**
13964 * Inserts all machine filters to the USB proxy service and then calls
13965 * Host::autoCaptureUSBDevices().
13966 *
13967 * Called by Console from the VM process upon VM startup.
13968 *
13969 * @note Locks what called methods lock.
13970 */
13971HRESULT SessionMachine::autoCaptureUSBDevices()
13972{
13973 LogFlowThisFunc(("\n"));
13974
13975#ifdef VBOX_WITH_USB
13976 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13977 AssertComRC(hrc);
13978 NOREF(hrc);
13979
13980 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13981 AssertReturn(service, E_FAIL);
13982 return service->autoCaptureDevicesForVM(this);
13983#else
13984 return S_OK;
13985#endif
13986}
13987
13988/**
13989 * Removes all machine filters from the USB proxy service and then calls
13990 * Host::detachAllUSBDevices().
13991 *
13992 * Called by Console from the VM process upon normal VM termination or by
13993 * SessionMachine::uninit() upon abnormal VM termination (from under the
13994 * Machine/SessionMachine lock).
13995 *
13996 * @note Locks what called methods lock.
13997 */
13998HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13999{
14000 LogFlowThisFunc(("\n"));
14001
14002#ifdef VBOX_WITH_USB
14003 HRESULT hrc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
14004 AssertComRC(hrc);
14005 NOREF(hrc);
14006
14007 USBProxyService *service = mParent->i_host()->i_usbProxyService();
14008 AssertReturn(service, E_FAIL);
14009 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
14010#else
14011 NOREF(aDone);
14012 return S_OK;
14013#endif
14014}
14015
14016/**
14017 * @note Locks this object for writing.
14018 */
14019HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
14020 ComPtr<IProgress> &aProgress)
14021{
14022 LogFlowThisFuncEnter();
14023
14024 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
14025 /*
14026 * We don't assert below because it might happen that a non-direct session
14027 * informs us it is closed right after we've been uninitialized -- it's ok.
14028 */
14029
14030 /* get IInternalSessionControl interface */
14031 ComPtr<IInternalSessionControl> control(aSession);
14032
14033 ComAssertRet(!control.isNull(), E_INVALIDARG);
14034
14035 /* Creating a Progress object requires the VirtualBox lock, and
14036 * thus locking it here is required by the lock order rules. */
14037 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
14038
14039 if (control == mData->mSession.mDirectControl)
14040 {
14041 /* The direct session is being normally closed by the client process
14042 * ----------------------------------------------------------------- */
14043
14044 /* go to the closing state (essential for all open*Session() calls and
14045 * for #i_checkForDeath()) */
14046 Assert(mData->mSession.mState == SessionState_Locked);
14047 mData->mSession.mState = SessionState_Unlocking;
14048
14049 /* set direct control to NULL to release the remote instance */
14050 mData->mSession.mDirectControl.setNull();
14051 LogFlowThisFunc(("Direct control is set to NULL\n"));
14052
14053 if (mData->mSession.mProgress)
14054 {
14055 /* finalize the progress, someone might wait if a frontend
14056 * closes the session before powering on the VM. */
14057 mData->mSession.mProgress->notifyComplete(E_FAIL,
14058 COM_IIDOF(ISession),
14059 getComponentName(),
14060 tr("The VM session was closed before any attempt to power it on"));
14061 mData->mSession.mProgress.setNull();
14062 }
14063
14064 /* Create the progress object the client will use to wait until
14065 * #i_checkForDeath() is called to uninitialize this session object after
14066 * it releases the IPC semaphore.
14067 * Note! Because we're "reusing" mProgress here, this must be a proxy
14068 * object just like for LaunchVMProcess. */
14069 Assert(mData->mSession.mProgress.isNull());
14070 ComObjPtr<ProgressProxy> progress;
14071 progress.createObject();
14072 ComPtr<IUnknown> pPeer(mPeer);
14073 progress->init(mParent, pPeer,
14074 Bstr(tr("Closing session")).raw(),
14075 FALSE /* aCancelable */);
14076 progress.queryInterfaceTo(aProgress.asOutParam());
14077 mData->mSession.mProgress = progress;
14078 }
14079 else
14080 {
14081 /* the remote session is being normally closed */
14082 bool found = false;
14083 for (Data::Session::RemoteControlList::iterator
14084 it = mData->mSession.mRemoteControls.begin();
14085 it != mData->mSession.mRemoteControls.end();
14086 ++it)
14087 {
14088 if (control == *it)
14089 {
14090 found = true;
14091 // This MUST be erase(it), not remove(*it) as the latter
14092 // triggers a very nasty use after free due to the place where
14093 // the value "lives".
14094 mData->mSession.mRemoteControls.erase(it);
14095 break;
14096 }
14097 }
14098 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
14099 E_INVALIDARG);
14100 }
14101
14102 /* signal the client watcher thread, because the client is going away */
14103 mParent->i_updateClientWatcher();
14104
14105 LogFlowThisFuncLeave();
14106 return S_OK;
14107}
14108
14109HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14110 std::vector<com::Utf8Str> &aValues,
14111 std::vector<LONG64> &aTimestamps,
14112 std::vector<com::Utf8Str> &aFlags)
14113{
14114 LogFlowThisFunc(("\n"));
14115
14116#ifdef VBOX_WITH_GUEST_PROPS
14117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14118
14119 size_t cEntries = mHWData->mGuestProperties.size();
14120 aNames.resize(cEntries);
14121 aValues.resize(cEntries);
14122 aTimestamps.resize(cEntries);
14123 aFlags.resize(cEntries);
14124
14125 size_t i = 0;
14126 for (HWData::GuestPropertyMap::const_iterator
14127 it = mHWData->mGuestProperties.begin();
14128 it != mHWData->mGuestProperties.end();
14129 ++it, ++i)
14130 {
14131 aNames[i] = it->first;
14132 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
14133 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
14134
14135 aValues[i] = it->second.strValue;
14136 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
14137 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
14138
14139 aTimestamps[i] = it->second.mTimestamp;
14140
14141 /* If it is NULL, keep it NULL. */
14142 if (it->second.mFlags)
14143 {
14144 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
14145 GuestPropWriteFlags(it->second.mFlags, szFlags);
14146 aFlags[i] = szFlags;
14147 }
14148 else
14149 aFlags[i] = "";
14150 }
14151 return S_OK;
14152#else
14153 ReturnComNotImplemented();
14154#endif
14155}
14156
14157HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
14158 const com::Utf8Str &aValue,
14159 LONG64 aTimestamp,
14160 const com::Utf8Str &aFlags,
14161 BOOL fWasDeleted)
14162{
14163 LogFlowThisFunc(("\n"));
14164
14165#ifdef VBOX_WITH_GUEST_PROPS
14166 try
14167 {
14168 /*
14169 * Convert input up front.
14170 */
14171 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
14172 if (aFlags.length())
14173 {
14174 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
14175 AssertRCReturn(vrc, E_INVALIDARG);
14176 }
14177
14178 /*
14179 * Now grab the object lock, validate the state and do the update.
14180 */
14181
14182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14183
14184 if (!Global::IsOnline(mData->mMachineState))
14185 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
14186
14187 i_setModified(IsModified_MachineData);
14188 mHWData.backup();
14189
14190 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
14191 if (it != mHWData->mGuestProperties.end())
14192 {
14193 if (!fWasDeleted)
14194 {
14195 it->second.strValue = aValue;
14196 it->second.mTimestamp = aTimestamp;
14197 it->second.mFlags = fFlags;
14198 }
14199 else
14200 mHWData->mGuestProperties.erase(it);
14201
14202 mData->mGuestPropertiesModified = TRUE;
14203 }
14204 else if (!fWasDeleted)
14205 {
14206 HWData::GuestProperty prop;
14207 prop.strValue = aValue;
14208 prop.mTimestamp = aTimestamp;
14209 prop.mFlags = fFlags;
14210
14211 mHWData->mGuestProperties[aName] = prop;
14212 mData->mGuestPropertiesModified = TRUE;
14213 }
14214
14215 alock.release();
14216
14217 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fWasDeleted);
14218 }
14219 catch (...)
14220 {
14221 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
14222 }
14223 return S_OK;
14224#else
14225 ReturnComNotImplemented();
14226#endif
14227}
14228
14229
14230HRESULT SessionMachine::lockMedia()
14231{
14232 AutoMultiWriteLock2 alock(this->lockHandle(),
14233 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14234
14235 AssertReturn( mData->mMachineState == MachineState_Starting
14236 || mData->mMachineState == MachineState_Restoring
14237 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
14238
14239 clearError();
14240 alock.release();
14241 return i_lockMedia();
14242}
14243
14244HRESULT SessionMachine::unlockMedia()
14245{
14246 HRESULT hrc = i_unlockMedia();
14247 return hrc;
14248}
14249
14250HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14251 ComPtr<IMediumAttachment> &aNewAttachment)
14252{
14253 // request the host lock first, since might be calling Host methods for getting host drives;
14254 // next, protect the media tree all the while we're in here, as well as our member variables
14255 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
14256 this->lockHandle(),
14257 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14258
14259 IMediumAttachment *iAttach = aAttachment;
14260 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
14261
14262 Utf8Str ctrlName;
14263 LONG lPort;
14264 LONG lDevice;
14265 bool fTempEject;
14266 {
14267 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14268
14269 /* Need to query the details first, as the IMediumAttachment reference
14270 * might be to the original settings, which we are going to change. */
14271 ctrlName = pAttach->i_getControllerName();
14272 lPort = pAttach->i_getPort();
14273 lDevice = pAttach->i_getDevice();
14274 fTempEject = pAttach->i_getTempEject();
14275 }
14276
14277 if (!fTempEject)
14278 {
14279 /* Remember previously mounted medium. The medium before taking the
14280 * backup is not necessarily the same thing. */
14281 ComObjPtr<Medium> oldmedium;
14282 oldmedium = pAttach->i_getMedium();
14283
14284 i_setModified(IsModified_Storage);
14285 mMediumAttachments.backup();
14286
14287 // The backup operation makes the pAttach reference point to the
14288 // old settings. Re-get the correct reference.
14289 pAttach = i_findAttachment(*mMediumAttachments.data(),
14290 ctrlName,
14291 lPort,
14292 lDevice);
14293
14294 {
14295 AutoCaller autoAttachCaller(this);
14296 if (FAILED(autoAttachCaller.hrc())) return autoAttachCaller.hrc();
14297
14298 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14299 if (!oldmedium.isNull())
14300 oldmedium->i_removeBackReference(mData->mUuid);
14301
14302 pAttach->i_updateMedium(NULL);
14303 pAttach->i_updateEjected();
14304 }
14305
14306 i_setModified(IsModified_Storage);
14307 }
14308 else
14309 {
14310 {
14311 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14312 pAttach->i_updateEjected();
14313 }
14314 }
14315
14316 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
14317
14318 return S_OK;
14319}
14320
14321HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
14322 com::Utf8Str &aResult)
14323{
14324 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14325
14326 HRESULT hrc = S_OK;
14327
14328 if (!mAuthLibCtx.hAuthLibrary)
14329 {
14330 /* Load the external authentication library. */
14331 Bstr authLibrary;
14332 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
14333
14334 Utf8Str filename = authLibrary;
14335
14336 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
14337 if (RT_FAILURE(vrc))
14338 hrc = setErrorBoth(E_FAIL, vrc,
14339 tr("Could not load the external authentication library '%s' (%Rrc)"),
14340 filename.c_str(), vrc);
14341 }
14342
14343 /* The auth library might need the machine lock. */
14344 alock.release();
14345
14346 if (FAILED(hrc))
14347 return hrc;
14348
14349 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
14350 {
14351 enum VRDEAuthParams
14352 {
14353 parmUuid = 1,
14354 parmGuestJudgement,
14355 parmUser,
14356 parmPassword,
14357 parmDomain,
14358 parmClientId
14359 };
14360
14361 AuthResult result = AuthResultAccessDenied;
14362
14363 Guid uuid(aAuthParams[parmUuid]);
14364 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
14365 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
14366
14367 result = AuthLibAuthenticate(&mAuthLibCtx,
14368 uuid.raw(), guestJudgement,
14369 aAuthParams[parmUser].c_str(),
14370 aAuthParams[parmPassword].c_str(),
14371 aAuthParams[parmDomain].c_str(),
14372 u32ClientId);
14373
14374 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
14375 size_t cbPassword = aAuthParams[parmPassword].length();
14376 if (cbPassword)
14377 {
14378 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
14379 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
14380 }
14381
14382 if (result == AuthResultAccessGranted)
14383 aResult = "granted";
14384 else
14385 aResult = "denied";
14386
14387 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
14388 aAuthParams[parmUser].c_str(), aResult.c_str()));
14389 }
14390 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
14391 {
14392 enum VRDEAuthDisconnectParams
14393 {
14394 parmUuid = 1,
14395 parmClientId
14396 };
14397
14398 Guid uuid(aAuthParams[parmUuid]);
14399 uint32_t u32ClientId = 0;
14400 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
14401 }
14402 else
14403 {
14404 hrc = E_INVALIDARG;
14405 }
14406
14407 return hrc;
14408}
14409
14410// public methods only for internal purposes
14411/////////////////////////////////////////////////////////////////////////////
14412
14413#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14414/**
14415 * Called from the client watcher thread to check for expected or unexpected
14416 * death of the client process that has a direct session to this machine.
14417 *
14418 * On Win32 and on OS/2, this method is called only when we've got the
14419 * mutex (i.e. the client has either died or terminated normally) so it always
14420 * returns @c true (the client is terminated, the session machine is
14421 * uninitialized).
14422 *
14423 * On other platforms, the method returns @c true if the client process has
14424 * terminated normally or abnormally and the session machine was uninitialized,
14425 * and @c false if the client process is still alive.
14426 *
14427 * @note Locks this object for writing.
14428 */
14429bool SessionMachine::i_checkForDeath()
14430{
14431 Uninit::Reason reason;
14432 bool terminated = false;
14433
14434 /* Enclose autoCaller with a block because calling uninit() from under it
14435 * will deadlock. */
14436 {
14437 AutoCaller autoCaller(this);
14438 if (!autoCaller.isOk())
14439 {
14440 /* return true if not ready, to cause the client watcher to exclude
14441 * the corresponding session from watching */
14442 LogFlowThisFunc(("Already uninitialized!\n"));
14443 return true;
14444 }
14445
14446 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14447
14448 /* Determine the reason of death: if the session state is Closing here,
14449 * everything is fine. Otherwise it means that the client did not call
14450 * OnSessionEnd() before it released the IPC semaphore. This may happen
14451 * either because the client process has abnormally terminated, or
14452 * because it simply forgot to call ISession::Close() before exiting. We
14453 * threat the latter also as an abnormal termination (see
14454 * Session::uninit() for details). */
14455 reason = mData->mSession.mState == SessionState_Unlocking ?
14456 Uninit::Normal :
14457 Uninit::Abnormal;
14458
14459 if (mClientToken)
14460 terminated = mClientToken->release();
14461 } /* AutoCaller block */
14462
14463 if (terminated)
14464 uninit(reason);
14465
14466 return terminated;
14467}
14468
14469void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14470{
14471 LogFlowThisFunc(("\n"));
14472
14473 strTokenId.setNull();
14474
14475 AutoCaller autoCaller(this);
14476 AssertComRCReturnVoid(autoCaller.hrc());
14477
14478 Assert(mClientToken);
14479 if (mClientToken)
14480 mClientToken->getId(strTokenId);
14481}
14482#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14483IToken *SessionMachine::i_getToken()
14484{
14485 LogFlowThisFunc(("\n"));
14486
14487 AutoCaller autoCaller(this);
14488 AssertComRCReturn(autoCaller.hrc(), NULL);
14489
14490 Assert(mClientToken);
14491 if (mClientToken)
14492 return mClientToken->getToken();
14493 else
14494 return NULL;
14495}
14496#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14497
14498Machine::ClientToken *SessionMachine::i_getClientToken()
14499{
14500 LogFlowThisFunc(("\n"));
14501
14502 AutoCaller autoCaller(this);
14503 AssertComRCReturn(autoCaller.hrc(), NULL);
14504
14505 return mClientToken;
14506}
14507
14508
14509/**
14510 * @note Locks this object for reading.
14511 */
14512HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14513{
14514 LogFlowThisFunc(("\n"));
14515
14516 AutoCaller autoCaller(this);
14517 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14518
14519 ComPtr<IInternalSessionControl> directControl;
14520 {
14521 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14522 if (mData->mSession.mLockType == LockType_VM)
14523 directControl = mData->mSession.mDirectControl;
14524 }
14525
14526 /* ignore notifications sent after #OnSessionEnd() is called */
14527 if (!directControl)
14528 return S_OK;
14529
14530 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14531}
14532
14533/**
14534 * @note Locks this object for reading.
14535 */
14536HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
14537 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
14538 const Utf8Str &aGuestIp, LONG aGuestPort)
14539{
14540 LogFlowThisFunc(("\n"));
14541
14542 AutoCaller autoCaller(this);
14543 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14544
14545 ComPtr<IInternalSessionControl> directControl;
14546 {
14547 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14548 if (mData->mSession.mLockType == LockType_VM)
14549 directControl = mData->mSession.mDirectControl;
14550 }
14551
14552 /* ignore notifications sent after #OnSessionEnd() is called */
14553 if (!directControl)
14554 return S_OK;
14555 /*
14556 * instead acting like callback we ask IVirtualBox deliver corresponding event
14557 */
14558
14559 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14560 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14561 return S_OK;
14562}
14563
14564/**
14565 * @note Locks this object for reading.
14566 */
14567HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14568{
14569 LogFlowThisFunc(("\n"));
14570
14571 AutoCaller autoCaller(this);
14572 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14573
14574 ComPtr<IInternalSessionControl> directControl;
14575 {
14576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14577 if (mData->mSession.mLockType == LockType_VM)
14578 directControl = mData->mSession.mDirectControl;
14579 }
14580
14581 /* ignore notifications sent after #OnSessionEnd() is called */
14582 if (!directControl)
14583 return S_OK;
14584
14585 return directControl->OnAudioAdapterChange(audioAdapter);
14586}
14587
14588/**
14589 * @note Locks this object for reading.
14590 */
14591HRESULT SessionMachine::i_onHostAudioDeviceChange(IHostAudioDevice *aDevice, BOOL aNew, AudioDeviceState_T aState, IVirtualBoxErrorInfo *aErrInfo)
14592{
14593 LogFlowThisFunc(("\n"));
14594
14595 AutoCaller autoCaller(this);
14596 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
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 return directControl->OnHostAudioDeviceChange(aDevice, aNew, aState, aErrInfo);
14610}
14611
14612/**
14613 * @note Locks this object for reading.
14614 */
14615HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14616{
14617 LogFlowThisFunc(("\n"));
14618
14619 AutoCaller autoCaller(this);
14620 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14621
14622 ComPtr<IInternalSessionControl> directControl;
14623 {
14624 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14625 if (mData->mSession.mLockType == LockType_VM)
14626 directControl = mData->mSession.mDirectControl;
14627 }
14628
14629 /* ignore notifications sent after #OnSessionEnd() is called */
14630 if (!directControl)
14631 return S_OK;
14632
14633 return directControl->OnSerialPortChange(serialPort);
14634}
14635
14636/**
14637 * @note Locks this object for reading.
14638 */
14639HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14640{
14641 LogFlowThisFunc(("\n"));
14642
14643 AutoCaller autoCaller(this);
14644 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14645
14646 ComPtr<IInternalSessionControl> directControl;
14647 {
14648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14649 if (mData->mSession.mLockType == LockType_VM)
14650 directControl = mData->mSession.mDirectControl;
14651 }
14652
14653 /* ignore notifications sent after #OnSessionEnd() is called */
14654 if (!directControl)
14655 return S_OK;
14656
14657 return directControl->OnParallelPortChange(parallelPort);
14658}
14659
14660/**
14661 * @note Locks this object for reading.
14662 */
14663HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14664{
14665 LogFlowThisFunc(("\n"));
14666
14667 AutoCaller autoCaller(this);
14668 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14669
14670 ComPtr<IInternalSessionControl> directControl;
14671 {
14672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14673 if (mData->mSession.mLockType == LockType_VM)
14674 directControl = mData->mSession.mDirectControl;
14675 }
14676
14677 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14678
14679 /* ignore notifications sent after #OnSessionEnd() is called */
14680 if (!directControl)
14681 return S_OK;
14682
14683 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14684}
14685
14686/**
14687 * @note Locks this object for reading.
14688 */
14689HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14690{
14691 LogFlowThisFunc(("\n"));
14692
14693 AutoCaller autoCaller(this);
14694 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14695
14696 ComPtr<IInternalSessionControl> directControl;
14697 {
14698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14699 if (mData->mSession.mLockType == LockType_VM)
14700 directControl = mData->mSession.mDirectControl;
14701 }
14702
14703 mParent->i_onMediumChanged(aAttachment);
14704
14705 /* ignore notifications sent after #OnSessionEnd() is called */
14706 if (!directControl)
14707 return S_OK;
14708
14709 return directControl->OnMediumChange(aAttachment, aForce);
14710}
14711
14712HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14713{
14714 LogFlowThisFunc(("\n"));
14715
14716 AutoCaller autoCaller(this);
14717 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14718
14719 ComPtr<IInternalSessionControl> directControl;
14720 {
14721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14722 if (mData->mSession.mLockType == LockType_VM)
14723 directControl = mData->mSession.mDirectControl;
14724 }
14725
14726 /* ignore notifications sent after #OnSessionEnd() is called */
14727 if (!directControl)
14728 return S_OK;
14729
14730 return directControl->OnVMProcessPriorityChange(aPriority);
14731}
14732
14733/**
14734 * @note Locks this object for reading.
14735 */
14736HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14737{
14738 LogFlowThisFunc(("\n"));
14739
14740 AutoCaller autoCaller(this);
14741 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14742
14743 ComPtr<IInternalSessionControl> directControl;
14744 {
14745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14746 if (mData->mSession.mLockType == LockType_VM)
14747 directControl = mData->mSession.mDirectControl;
14748 }
14749
14750 /* ignore notifications sent after #OnSessionEnd() is called */
14751 if (!directControl)
14752 return S_OK;
14753
14754 return directControl->OnCPUChange(aCPU, aRemove);
14755}
14756
14757HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14758{
14759 LogFlowThisFunc(("\n"));
14760
14761 AutoCaller autoCaller(this);
14762 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14763
14764 ComPtr<IInternalSessionControl> directControl;
14765 {
14766 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14767 if (mData->mSession.mLockType == LockType_VM)
14768 directControl = mData->mSession.mDirectControl;
14769 }
14770
14771 /* ignore notifications sent after #OnSessionEnd() is called */
14772 if (!directControl)
14773 return S_OK;
14774
14775 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14776}
14777
14778/**
14779 * @note Locks this object for reading.
14780 */
14781HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14782{
14783 LogFlowThisFunc(("\n"));
14784
14785 AutoCaller autoCaller(this);
14786 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14787
14788 ComPtr<IInternalSessionControl> directControl;
14789 {
14790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14791 if (mData->mSession.mLockType == LockType_VM)
14792 directControl = mData->mSession.mDirectControl;
14793 }
14794
14795 /* ignore notifications sent after #OnSessionEnd() is called */
14796 if (!directControl)
14797 return S_OK;
14798
14799 return directControl->OnVRDEServerChange(aRestart);
14800}
14801
14802/**
14803 * @note Locks this object for reading.
14804 */
14805HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14806{
14807 LogFlowThisFunc(("\n"));
14808
14809 AutoCaller autoCaller(this);
14810 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14811
14812 ComPtr<IInternalSessionControl> directControl;
14813 {
14814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14815 if (mData->mSession.mLockType == LockType_VM)
14816 directControl = mData->mSession.mDirectControl;
14817 }
14818
14819 /* ignore notifications sent after #OnSessionEnd() is called */
14820 if (!directControl)
14821 return S_OK;
14822
14823 return directControl->OnRecordingChange(aEnable);
14824}
14825
14826/**
14827 * @note Locks this object for reading.
14828 */
14829HRESULT SessionMachine::i_onUSBControllerChange()
14830{
14831 LogFlowThisFunc(("\n"));
14832
14833 AutoCaller autoCaller(this);
14834 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14835
14836 ComPtr<IInternalSessionControl> directControl;
14837 {
14838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14839 if (mData->mSession.mLockType == LockType_VM)
14840 directControl = mData->mSession.mDirectControl;
14841 }
14842
14843 /* ignore notifications sent after #OnSessionEnd() is called */
14844 if (!directControl)
14845 return S_OK;
14846
14847 return directControl->OnUSBControllerChange();
14848}
14849
14850/**
14851 * @note Locks this object for reading.
14852 */
14853HRESULT SessionMachine::i_onSharedFolderChange()
14854{
14855 LogFlowThisFunc(("\n"));
14856
14857 AutoCaller autoCaller(this);
14858 AssertComRCReturnRC(autoCaller.hrc());
14859
14860 ComPtr<IInternalSessionControl> directControl;
14861 {
14862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14863 if (mData->mSession.mLockType == LockType_VM)
14864 directControl = mData->mSession.mDirectControl;
14865 }
14866
14867 /* ignore notifications sent after #OnSessionEnd() is called */
14868 if (!directControl)
14869 return S_OK;
14870
14871 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14872}
14873
14874/**
14875 * @note Locks this object for reading.
14876 */
14877HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14878{
14879 LogFlowThisFunc(("\n"));
14880
14881 AutoCaller autoCaller(this);
14882 AssertComRCReturnRC(autoCaller.hrc());
14883
14884 ComPtr<IInternalSessionControl> directControl;
14885 {
14886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14887 if (mData->mSession.mLockType == LockType_VM)
14888 directControl = mData->mSession.mDirectControl;
14889 }
14890
14891 /* ignore notifications sent after #OnSessionEnd() is called */
14892 if (!directControl)
14893 return S_OK;
14894
14895 return directControl->OnClipboardModeChange(aClipboardMode);
14896}
14897
14898/**
14899 * @note Locks this object for reading.
14900 */
14901HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14902{
14903 LogFlowThisFunc(("\n"));
14904
14905 AutoCaller autoCaller(this);
14906 AssertComRCReturnRC(autoCaller.hrc());
14907
14908 ComPtr<IInternalSessionControl> directControl;
14909 {
14910 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14911 if (mData->mSession.mLockType == LockType_VM)
14912 directControl = mData->mSession.mDirectControl;
14913 }
14914
14915 /* ignore notifications sent after #OnSessionEnd() is called */
14916 if (!directControl)
14917 return S_OK;
14918
14919 return directControl->OnClipboardFileTransferModeChange(aEnable);
14920}
14921
14922/**
14923 * @note Locks this object for reading.
14924 */
14925HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14926{
14927 LogFlowThisFunc(("\n"));
14928
14929 AutoCaller autoCaller(this);
14930 AssertComRCReturnRC(autoCaller.hrc());
14931
14932 ComPtr<IInternalSessionControl> directControl;
14933 {
14934 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14935 if (mData->mSession.mLockType == LockType_VM)
14936 directControl = mData->mSession.mDirectControl;
14937 }
14938
14939 /* ignore notifications sent after #OnSessionEnd() is called */
14940 if (!directControl)
14941 return S_OK;
14942
14943 return directControl->OnDnDModeChange(aDnDMode);
14944}
14945
14946/**
14947 * @note Locks this object for reading.
14948 */
14949HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14950{
14951 LogFlowThisFunc(("\n"));
14952
14953 AutoCaller autoCaller(this);
14954 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14955
14956 ComPtr<IInternalSessionControl> directControl;
14957 {
14958 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14959 if (mData->mSession.mLockType == LockType_VM)
14960 directControl = mData->mSession.mDirectControl;
14961 }
14962
14963 /* ignore notifications sent after #OnSessionEnd() is called */
14964 if (!directControl)
14965 return S_OK;
14966
14967 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14968}
14969
14970/**
14971 * @note Locks this object for reading.
14972 */
14973HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14974{
14975 LogFlowThisFunc(("\n"));
14976
14977 AutoCaller autoCaller(this);
14978 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
14979
14980 ComPtr<IInternalSessionControl> directControl;
14981 {
14982 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14983 if (mData->mSession.mLockType == LockType_VM)
14984 directControl = mData->mSession.mDirectControl;
14985 }
14986
14987 /* ignore notifications sent after #OnSessionEnd() is called */
14988 if (!directControl)
14989 return S_OK;
14990
14991 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14992}
14993
14994/**
14995 * @note Locks this object for reading.
14996 */
14997HRESULT SessionMachine::i_onGuestDebugControlChange(IGuestDebugControl *guestDebugControl)
14998{
14999 LogFlowThisFunc(("\n"));
15000
15001 AutoCaller autoCaller(this);
15002 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
15003
15004 ComPtr<IInternalSessionControl> directControl;
15005 {
15006 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15007 if (mData->mSession.mLockType == LockType_VM)
15008 directControl = mData->mSession.mDirectControl;
15009 }
15010
15011 /* ignore notifications sent after #OnSessionEnd() is called */
15012 if (!directControl)
15013 return S_OK;
15014
15015 return directControl->OnGuestDebugControlChange(guestDebugControl);
15016}
15017
15018/**
15019 * Returns @c true if this machine's USB controller reports it has a matching
15020 * filter for the given USB device and @c false otherwise.
15021 *
15022 * @note locks this object for reading.
15023 */
15024bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
15025{
15026 AutoCaller autoCaller(this);
15027 /* silently return if not ready -- this method may be called after the
15028 * direct machine session has been called */
15029 if (!autoCaller.isOk())
15030 return false;
15031
15032#ifdef VBOX_WITH_USB
15033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15034
15035 switch (mData->mMachineState)
15036 {
15037 case MachineState_Starting:
15038 case MachineState_Restoring:
15039 case MachineState_TeleportingIn:
15040 case MachineState_Paused:
15041 case MachineState_Running:
15042 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
15043 * elsewhere... */
15044 alock.release();
15045 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
15046 default: break;
15047 }
15048#else
15049 NOREF(aDevice);
15050 NOREF(aMaskedIfs);
15051#endif
15052 return false;
15053}
15054
15055/**
15056 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
15057 */
15058HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
15059 IVirtualBoxErrorInfo *aError,
15060 ULONG aMaskedIfs,
15061 const com::Utf8Str &aCaptureFilename)
15062{
15063 LogFlowThisFunc(("\n"));
15064
15065 AutoCaller autoCaller(this);
15066
15067 /* This notification may happen after the machine object has been
15068 * uninitialized (the session was closed), so don't assert. */
15069 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
15070
15071 ComPtr<IInternalSessionControl> directControl;
15072 {
15073 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15074 if (mData->mSession.mLockType == LockType_VM)
15075 directControl = mData->mSession.mDirectControl;
15076 }
15077
15078 /* fail on notifications sent after #OnSessionEnd() is called, it is
15079 * expected by the caller */
15080 if (!directControl)
15081 return E_FAIL;
15082
15083 /* No locks should be held at this point. */
15084 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
15085 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
15086
15087 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
15088}
15089
15090/**
15091 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
15092 */
15093HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
15094 IVirtualBoxErrorInfo *aError)
15095{
15096 LogFlowThisFunc(("\n"));
15097
15098 AutoCaller autoCaller(this);
15099
15100 /* This notification may happen after the machine object has been
15101 * uninitialized (the session was closed), so don't assert. */
15102 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
15103
15104 ComPtr<IInternalSessionControl> directControl;
15105 {
15106 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15107 if (mData->mSession.mLockType == LockType_VM)
15108 directControl = mData->mSession.mDirectControl;
15109 }
15110
15111 /* fail on notifications sent after #OnSessionEnd() is called, it is
15112 * expected by the caller */
15113 if (!directControl)
15114 return E_FAIL;
15115
15116 /* No locks should be held at this point. */
15117 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
15118 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
15119
15120 return directControl->OnUSBDeviceDetach(aId, aError);
15121}
15122
15123// protected methods
15124/////////////////////////////////////////////////////////////////////////////
15125
15126/**
15127 * Deletes the given file if it is no longer in use by either the current machine state
15128 * (if the machine is "saved") or any of the machine's snapshots.
15129 *
15130 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
15131 * but is different for each SnapshotMachine. When calling this, the order of calling this
15132 * function on the one hand and changing that variable OR the snapshots tree on the other hand
15133 * is therefore critical. I know, it's all rather messy.
15134 *
15135 * @param strStateFile
15136 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
15137 * the test for whether the saved state file is in use.
15138 */
15139void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
15140 Snapshot *pSnapshotToIgnore)
15141{
15142 // it is safe to delete this saved state file if it is not currently in use by the machine ...
15143 if ( (strStateFile.isNotEmpty())
15144 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
15145 )
15146 // ... and it must also not be shared with other snapshots
15147 if ( !mData->mFirstSnapshot
15148 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
15149 // this checks the SnapshotMachine's state file paths
15150 )
15151 i_deleteFile(strStateFile, true /* fIgnoreFailures */);
15152}
15153
15154/**
15155 * Locks the attached media.
15156 *
15157 * All attached hard disks are locked for writing and DVD/floppy are locked for
15158 * reading. Parents of attached hard disks (if any) are locked for reading.
15159 *
15160 * This method also performs accessibility check of all media it locks: if some
15161 * media is inaccessible, the method will return a failure and a bunch of
15162 * extended error info objects per each inaccessible medium.
15163 *
15164 * Note that this method is atomic: if it returns a success, all media are
15165 * locked as described above; on failure no media is locked at all (all
15166 * succeeded individual locks will be undone).
15167 *
15168 * The caller is responsible for doing the necessary state sanity checks.
15169 *
15170 * The locks made by this method must be undone by calling #unlockMedia() when
15171 * no more needed.
15172 */
15173HRESULT SessionMachine::i_lockMedia()
15174{
15175 AutoCaller autoCaller(this);
15176 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
15177
15178 AutoMultiWriteLock2 alock(this->lockHandle(),
15179 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
15180
15181 /* bail out if trying to lock things with already set up locking */
15182 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
15183
15184 MultiResult hrcMult(S_OK);
15185
15186 /* Collect locking information for all medium objects attached to the VM. */
15187 for (MediumAttachmentList::const_iterator
15188 it = mMediumAttachments->begin();
15189 it != mMediumAttachments->end();
15190 ++it)
15191 {
15192 MediumAttachment *pAtt = *it;
15193 DeviceType_T devType = pAtt->i_getType();
15194 Medium *pMedium = pAtt->i_getMedium();
15195
15196 MediumLockList *pMediumLockList(new MediumLockList());
15197 // There can be attachments without a medium (floppy/dvd), and thus
15198 // it's impossible to create a medium lock list. It still makes sense
15199 // to have the empty medium lock list in the map in case a medium is
15200 // attached later.
15201 if (pMedium != NULL)
15202 {
15203 MediumType_T mediumType = pMedium->i_getType();
15204 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
15205 || mediumType == MediumType_Shareable;
15206 bool fIsVitalImage = (devType == DeviceType_HardDisk);
15207
15208 alock.release();
15209 hrcMult = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
15210 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
15211 false /* fMediumLockWriteAll */,
15212 NULL,
15213 *pMediumLockList);
15214 alock.acquire();
15215 if (FAILED(hrcMult))
15216 {
15217 delete pMediumLockList;
15218 mData->mSession.mLockedMedia.Clear();
15219 break;
15220 }
15221 }
15222
15223 HRESULT hrc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
15224 if (FAILED(hrc))
15225 {
15226 mData->mSession.mLockedMedia.Clear();
15227 hrcMult = setError(hrc, tr("Collecting locking information for all attached media failed"));
15228 break;
15229 }
15230 }
15231
15232 if (SUCCEEDED(hrcMult))
15233 {
15234 /* Now lock all media. If this fails, nothing is locked. */
15235 alock.release();
15236 HRESULT hrc = mData->mSession.mLockedMedia.Lock();
15237 alock.acquire();
15238 if (FAILED(hrc))
15239 hrcMult = setError(hrc,
15240 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
15241 }
15242
15243 return hrcMult;
15244}
15245
15246/**
15247 * Undoes the locks made by by #lockMedia().
15248 */
15249HRESULT SessionMachine::i_unlockMedia()
15250{
15251 AutoCaller autoCaller(this);
15252 AssertComRCReturn(autoCaller.hrc(),autoCaller.hrc());
15253
15254 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15255
15256 /* we may be holding important error info on the current thread;
15257 * preserve it */
15258 ErrorInfoKeeper eik;
15259
15260 HRESULT hrc = mData->mSession.mLockedMedia.Clear();
15261 AssertComRC(hrc);
15262 return hrc;
15263}
15264
15265/**
15266 * Helper to change the machine state (reimplementation).
15267 *
15268 * @note Locks this object for writing.
15269 * @note This method must not call i_saveSettings or SaveSettings, otherwise
15270 * it can cause crashes in random places due to unexpectedly committing
15271 * the current settings. The caller is responsible for that. The call
15272 * to saveStateSettings is fine, because this method does not commit.
15273 */
15274HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
15275{
15276 LogFlowThisFuncEnter();
15277
15278 AutoCaller autoCaller(this);
15279 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
15280
15281 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15282
15283 MachineState_T oldMachineState = mData->mMachineState;
15284
15285 AssertMsgReturn(oldMachineState != aMachineState,
15286 ("oldMachineState=%s, aMachineState=%s\n",
15287 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
15288 E_FAIL);
15289
15290 HRESULT hrc = S_OK;
15291
15292 int stsFlags = 0;
15293 bool deleteSavedState = false;
15294
15295 /* detect some state transitions */
15296
15297 if ( ( ( oldMachineState == MachineState_Saved
15298 || oldMachineState == MachineState_AbortedSaved
15299 )
15300 && aMachineState == MachineState_Restoring
15301 )
15302 || ( ( oldMachineState == MachineState_PoweredOff
15303 || oldMachineState == MachineState_Teleported
15304 || oldMachineState == MachineState_Aborted
15305 )
15306 && ( aMachineState == MachineState_TeleportingIn
15307 || aMachineState == MachineState_Starting
15308 )
15309 )
15310 )
15311 {
15312 /* The EMT thread is about to start */
15313
15314 /* Nothing to do here for now... */
15315
15316 /// @todo NEWMEDIA don't let mDVDDrive and other children
15317 /// change anything when in the Starting/Restoring state
15318 }
15319 else if ( ( oldMachineState == MachineState_Running
15320 || oldMachineState == MachineState_Paused
15321 || oldMachineState == MachineState_Teleporting
15322 || oldMachineState == MachineState_OnlineSnapshotting
15323 || oldMachineState == MachineState_LiveSnapshotting
15324 || oldMachineState == MachineState_Stuck
15325 || oldMachineState == MachineState_Starting
15326 || oldMachineState == MachineState_Stopping
15327 || oldMachineState == MachineState_Saving
15328 || oldMachineState == MachineState_Restoring
15329 || oldMachineState == MachineState_TeleportingPausedVM
15330 || oldMachineState == MachineState_TeleportingIn
15331 )
15332 && ( aMachineState == MachineState_PoweredOff
15333 || aMachineState == MachineState_Saved
15334 || aMachineState == MachineState_Teleported
15335 || aMachineState == MachineState_Aborted
15336 || aMachineState == MachineState_AbortedSaved
15337 )
15338 )
15339 {
15340 /* The EMT thread has just stopped, unlock attached media. Note that as
15341 * opposed to locking that is done from Console, we do unlocking here
15342 * because the VM process may have aborted before having a chance to
15343 * properly unlock all media it locked. */
15344
15345 unlockMedia();
15346 }
15347
15348 if (oldMachineState == MachineState_Restoring)
15349 {
15350 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
15351 {
15352 /*
15353 * delete the saved state file once the machine has finished
15354 * restoring from it (note that Console sets the state from
15355 * Restoring to AbortedSaved if the VM couldn't restore successfully,
15356 * to give the user an ability to fix an error and retry --
15357 * we keep the saved state file in this case)
15358 */
15359 deleteSavedState = true;
15360 }
15361 }
15362 else if ( oldMachineState == MachineState_Saved
15363 && ( aMachineState == MachineState_PoweredOff
15364 || aMachineState == MachineState_Teleported
15365 )
15366 )
15367 {
15368 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
15369 deleteSavedState = true;
15370 mData->mCurrentStateModified = TRUE;
15371 stsFlags |= SaveSTS_CurStateModified;
15372 }
15373 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
15374 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
15375
15376 if ( aMachineState == MachineState_Starting
15377 || aMachineState == MachineState_Restoring
15378 || aMachineState == MachineState_TeleportingIn
15379 )
15380 {
15381 /* set the current state modified flag to indicate that the current
15382 * state is no more identical to the state in the
15383 * current snapshot */
15384 if (!mData->mCurrentSnapshot.isNull())
15385 {
15386 mData->mCurrentStateModified = TRUE;
15387 stsFlags |= SaveSTS_CurStateModified;
15388 }
15389 }
15390
15391 if (deleteSavedState)
15392 {
15393 if (mRemoveSavedState)
15394 {
15395 Assert(!mSSData->strStateFilePath.isEmpty());
15396
15397 // it is safe to delete the saved state file if ...
15398 if ( !mData->mFirstSnapshot // ... we have no snapshots or
15399 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
15400 // ... none of the snapshots share the saved state file
15401 )
15402 i_deleteFile(mSSData->strStateFilePath, true /* fIgnoreFailures */);
15403 }
15404
15405 mSSData->strStateFilePath.setNull();
15406 stsFlags |= SaveSTS_StateFilePath;
15407 }
15408
15409 /* redirect to the underlying peer machine */
15410 mPeer->i_setMachineState(aMachineState);
15411
15412 if ( oldMachineState != MachineState_RestoringSnapshot
15413 && ( aMachineState == MachineState_PoweredOff
15414 || aMachineState == MachineState_Teleported
15415 || aMachineState == MachineState_Aborted
15416 || aMachineState == MachineState_AbortedSaved
15417 || aMachineState == MachineState_Saved))
15418 {
15419 /* the machine has stopped execution
15420 * (or the saved state file was adopted) */
15421 stsFlags |= SaveSTS_StateTimeStamp;
15422 }
15423
15424 if ( ( oldMachineState == MachineState_PoweredOff
15425 || oldMachineState == MachineState_Aborted
15426 || oldMachineState == MachineState_Teleported
15427 )
15428 && aMachineState == MachineState_Saved)
15429 {
15430 /* the saved state file was adopted */
15431 Assert(!mSSData->strStateFilePath.isEmpty());
15432 stsFlags |= SaveSTS_StateFilePath;
15433 }
15434
15435#ifdef VBOX_WITH_GUEST_PROPS
15436 if ( aMachineState == MachineState_PoweredOff
15437 || aMachineState == MachineState_Aborted
15438 || aMachineState == MachineState_Teleported)
15439 {
15440 /* Make sure any transient guest properties get removed from the
15441 * property store on shutdown. */
15442 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
15443
15444 /* remove it from the settings representation */
15445 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
15446 for (settings::GuestPropertiesList::iterator
15447 it = llGuestProperties.begin();
15448 it != llGuestProperties.end();
15449 /*nothing*/)
15450 {
15451 const settings::GuestProperty &prop = *it;
15452 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
15453 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
15454 {
15455 it = llGuestProperties.erase(it);
15456 fNeedsSaving = true;
15457 }
15458 else
15459 {
15460 ++it;
15461 }
15462 }
15463
15464 /* Additionally remove it from the HWData representation. Required to
15465 * keep everything in sync, as this is what the API keeps using. */
15466 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
15467 for (HWData::GuestPropertyMap::iterator
15468 it = llHWGuestProperties.begin();
15469 it != llHWGuestProperties.end();
15470 /*nothing*/)
15471 {
15472 uint32_t fFlags = it->second.mFlags;
15473 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
15474 {
15475 /* iterator where we need to continue after the erase call
15476 * (C++03 is a fact still, and it doesn't return the iterator
15477 * which would allow continuing) */
15478 HWData::GuestPropertyMap::iterator it2 = it;
15479 ++it2;
15480 llHWGuestProperties.erase(it);
15481 it = it2;
15482 fNeedsSaving = true;
15483 }
15484 else
15485 {
15486 ++it;
15487 }
15488 }
15489
15490 if (fNeedsSaving)
15491 {
15492 mData->mCurrentStateModified = TRUE;
15493 stsFlags |= SaveSTS_CurStateModified;
15494 }
15495 }
15496#endif /* VBOX_WITH_GUEST_PROPS */
15497
15498 hrc = i_saveStateSettings(stsFlags);
15499
15500 if ( ( oldMachineState != MachineState_PoweredOff
15501 && oldMachineState != MachineState_Aborted
15502 && oldMachineState != MachineState_Teleported
15503 )
15504 && ( aMachineState == MachineState_PoweredOff
15505 || aMachineState == MachineState_Aborted
15506 || aMachineState == MachineState_Teleported
15507 )
15508 )
15509 {
15510 /* we've been shut down for any reason */
15511 /* no special action so far */
15512 }
15513
15514 LogFlowThisFunc(("hrc=%Rhrc [%s]\n", hrc, ::stringifyMachineState(mData->mMachineState) ));
15515 LogFlowThisFuncLeave();
15516 return hrc;
15517}
15518
15519/**
15520 * Sends the current machine state value to the VM process.
15521 *
15522 * @note Locks this object for reading, then calls a client process.
15523 */
15524HRESULT SessionMachine::i_updateMachineStateOnClient()
15525{
15526 AutoCaller autoCaller(this);
15527 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
15528
15529 ComPtr<IInternalSessionControl> directControl;
15530 {
15531 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15532 AssertReturn(!!mData, E_FAIL);
15533 if (mData->mSession.mLockType == LockType_VM)
15534 directControl = mData->mSession.mDirectControl;
15535
15536 /* directControl may be already set to NULL here in #OnSessionEnd()
15537 * called too early by the direct session process while there is still
15538 * some operation (like deleting the snapshot) in progress. The client
15539 * process in this case is waiting inside Session::close() for the
15540 * "end session" process object to complete, while #uninit() called by
15541 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15542 * operation to complete. For now, we accept this inconsistent behavior
15543 * and simply do nothing here. */
15544
15545 if (mData->mSession.mState == SessionState_Unlocking)
15546 return S_OK;
15547 }
15548
15549 /* ignore notifications sent after #OnSessionEnd() is called */
15550 if (!directControl)
15551 return S_OK;
15552
15553 return directControl->UpdateMachineState(mData->mMachineState);
15554}
15555
15556
15557/*static*/
15558HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15559{
15560 va_list args;
15561 va_start(args, pcszMsg);
15562 HRESULT hrc = setErrorInternalV(aResultCode,
15563 getStaticClassIID(),
15564 getStaticComponentName(),
15565 pcszMsg, args,
15566 false /* aWarning */,
15567 true /* aLogIt */);
15568 va_end(args);
15569 return hrc;
15570}
15571
15572
15573HRESULT Machine::updateState(MachineState_T aState)
15574{
15575 NOREF(aState);
15576 ReturnComNotImplemented();
15577}
15578
15579HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15580{
15581 NOREF(aProgress);
15582 ReturnComNotImplemented();
15583}
15584
15585HRESULT Machine::endPowerUp(LONG aResult)
15586{
15587 NOREF(aResult);
15588 ReturnComNotImplemented();
15589}
15590
15591HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15592{
15593 NOREF(aProgress);
15594 ReturnComNotImplemented();
15595}
15596
15597HRESULT Machine::endPoweringDown(LONG aResult,
15598 const com::Utf8Str &aErrMsg)
15599{
15600 NOREF(aResult);
15601 NOREF(aErrMsg);
15602 ReturnComNotImplemented();
15603}
15604
15605HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15606 BOOL *aMatched,
15607 ULONG *aMaskedInterfaces)
15608{
15609 NOREF(aDevice);
15610 NOREF(aMatched);
15611 NOREF(aMaskedInterfaces);
15612 ReturnComNotImplemented();
15613
15614}
15615
15616HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15617{
15618 NOREF(aId); NOREF(aCaptureFilename);
15619 ReturnComNotImplemented();
15620}
15621
15622HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15623 BOOL aDone)
15624{
15625 NOREF(aId);
15626 NOREF(aDone);
15627 ReturnComNotImplemented();
15628}
15629
15630HRESULT Machine::autoCaptureUSBDevices()
15631{
15632 ReturnComNotImplemented();
15633}
15634
15635HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15636{
15637 NOREF(aDone);
15638 ReturnComNotImplemented();
15639}
15640
15641HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15642 ComPtr<IProgress> &aProgress)
15643{
15644 NOREF(aSession);
15645 NOREF(aProgress);
15646 ReturnComNotImplemented();
15647}
15648
15649HRESULT Machine::finishOnlineMergeMedium()
15650{
15651 ReturnComNotImplemented();
15652}
15653
15654HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15655 std::vector<com::Utf8Str> &aValues,
15656 std::vector<LONG64> &aTimestamps,
15657 std::vector<com::Utf8Str> &aFlags)
15658{
15659 NOREF(aNames);
15660 NOREF(aValues);
15661 NOREF(aTimestamps);
15662 NOREF(aFlags);
15663 ReturnComNotImplemented();
15664}
15665
15666HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15667 const com::Utf8Str &aValue,
15668 LONG64 aTimestamp,
15669 const com::Utf8Str &aFlags,
15670 BOOL fWasDeleted)
15671{
15672 NOREF(aName);
15673 NOREF(aValue);
15674 NOREF(aTimestamp);
15675 NOREF(aFlags);
15676 NOREF(fWasDeleted);
15677 ReturnComNotImplemented();
15678}
15679
15680HRESULT Machine::lockMedia()
15681{
15682 ReturnComNotImplemented();
15683}
15684
15685HRESULT Machine::unlockMedia()
15686{
15687 ReturnComNotImplemented();
15688}
15689
15690HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15691 ComPtr<IMediumAttachment> &aNewAttachment)
15692{
15693 NOREF(aAttachment);
15694 NOREF(aNewAttachment);
15695 ReturnComNotImplemented();
15696}
15697
15698HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15699 ULONG aCpuUser,
15700 ULONG aCpuKernel,
15701 ULONG aCpuIdle,
15702 ULONG aMemTotal,
15703 ULONG aMemFree,
15704 ULONG aMemBalloon,
15705 ULONG aMemShared,
15706 ULONG aMemCache,
15707 ULONG aPagedTotal,
15708 ULONG aMemAllocTotal,
15709 ULONG aMemFreeTotal,
15710 ULONG aMemBalloonTotal,
15711 ULONG aMemSharedTotal,
15712 ULONG aVmNetRx,
15713 ULONG aVmNetTx)
15714{
15715 NOREF(aValidStats);
15716 NOREF(aCpuUser);
15717 NOREF(aCpuKernel);
15718 NOREF(aCpuIdle);
15719 NOREF(aMemTotal);
15720 NOREF(aMemFree);
15721 NOREF(aMemBalloon);
15722 NOREF(aMemShared);
15723 NOREF(aMemCache);
15724 NOREF(aPagedTotal);
15725 NOREF(aMemAllocTotal);
15726 NOREF(aMemFreeTotal);
15727 NOREF(aMemBalloonTotal);
15728 NOREF(aMemSharedTotal);
15729 NOREF(aVmNetRx);
15730 NOREF(aVmNetTx);
15731 ReturnComNotImplemented();
15732}
15733
15734HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15735 com::Utf8Str &aResult)
15736{
15737 NOREF(aAuthParams);
15738 NOREF(aResult);
15739 ReturnComNotImplemented();
15740}
15741
15742com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15743{
15744 com::Utf8Str strControllerName = "Unknown";
15745 switch (aBusType)
15746 {
15747 case StorageBus_IDE:
15748 {
15749 strControllerName = "IDE";
15750 break;
15751 }
15752 case StorageBus_SATA:
15753 {
15754 strControllerName = "SATA";
15755 break;
15756 }
15757 case StorageBus_SCSI:
15758 {
15759 strControllerName = "SCSI";
15760 break;
15761 }
15762 case StorageBus_Floppy:
15763 {
15764 strControllerName = "Floppy";
15765 break;
15766 }
15767 case StorageBus_SAS:
15768 {
15769 strControllerName = "SAS";
15770 break;
15771 }
15772 case StorageBus_USB:
15773 {
15774 strControllerName = "USB";
15775 break;
15776 }
15777 default:
15778 break;
15779 }
15780 return strControllerName;
15781}
15782
15783HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15784{
15785 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15786
15787 AutoCaller autoCaller(this);
15788 AssertComRCReturn(autoCaller.hrc(),autoCaller.hrc());
15789
15790 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15791 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15792 HRESULT hrc = getUSBDeviceFilters(usbDeviceFilters);
15793 if (FAILED(hrc)) return hrc;
15794
15795 NOREF(aFlags);
15796 com::Utf8Str osTypeId;
15797 ComObjPtr<GuestOSType> osType = NULL;
15798
15799 /* Get the guest os type as a string from the VB. */
15800 hrc = getOSTypeId(osTypeId);
15801 if (FAILED(hrc)) return hrc;
15802
15803 /* Get the os type obj that coresponds, can be used to get
15804 * the defaults for this guest OS. */
15805 hrc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15806 if (FAILED(hrc)) return hrc;
15807
15808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15809
15810 /* Let the OS type select 64-bit ness. */
15811 mHWData->mLongMode = osType->i_is64Bit()
15812 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15813
15814 /* Let the OS type enable the X2APIC */
15815 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15816
15817 /* This one covers IOAPICEnabled. */
15818 mBIOSSettings->i_applyDefaults(osType);
15819
15820 /* Initialize default record settings. */
15821 mRecordingSettings->i_applyDefaults();
15822
15823 /* Initialize default BIOS settings here */
15824 /* Hardware virtualization must be ON by default */
15825 mHWData->mAPIC = true;
15826 mHWData->mHWVirtExEnabled = true;
15827
15828 hrc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15829 if (FAILED(hrc)) return hrc;
15830
15831 hrc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15832 if (FAILED(hrc)) return hrc;
15833
15834 /* Graphics stuff. */
15835 GraphicsControllerType_T graphicsController;
15836 hrc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15837 if (FAILED(hrc)) return hrc;
15838
15839 hrc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15840 if (FAILED(hrc)) return hrc;
15841
15842 ULONG vramSize;
15843 hrc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15844 if (FAILED(hrc)) return hrc;
15845
15846 hrc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15847 if (FAILED(hrc)) return hrc;
15848
15849 BOOL fAccelerate2DVideoEnabled;
15850 hrc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15851 if (FAILED(hrc)) return hrc;
15852
15853 hrc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15854 if (FAILED(hrc)) return hrc;
15855
15856 BOOL fAccelerate3DEnabled;
15857 hrc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15858 if (FAILED(hrc)) return hrc;
15859
15860 hrc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15861 if (FAILED(hrc)) return hrc;
15862
15863 hrc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15864 if (FAILED(hrc)) return hrc;
15865
15866 hrc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15867 if (FAILED(hrc)) return hrc;
15868
15869 hrc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15870 if (FAILED(hrc)) return hrc;
15871
15872 BOOL mRTCUseUTC;
15873 hrc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15874 if (FAILED(hrc)) return hrc;
15875
15876 setRTCUseUTC(mRTCUseUTC);
15877 if (FAILED(hrc)) return hrc;
15878
15879 /* the setter does more than just the assignment, so use it */
15880 ChipsetType_T enmChipsetType;
15881 hrc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15882 if (FAILED(hrc)) return hrc;
15883
15884 hrc = COMSETTER(ChipsetType)(enmChipsetType);
15885 if (FAILED(hrc)) return hrc;
15886
15887 hrc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15888 if (FAILED(hrc)) return hrc;
15889
15890 /* Apply IOMMU defaults. */
15891 IommuType_T enmIommuType;
15892 hrc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15893 if (FAILED(hrc)) return hrc;
15894
15895 hrc = COMSETTER(IommuType)(enmIommuType);
15896 if (FAILED(hrc)) return hrc;
15897
15898 /* Apply network adapters defaults */
15899 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15900 mNetworkAdapters[slot]->i_applyDefaults(osType);
15901
15902 /* Apply serial port defaults */
15903 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15904 mSerialPorts[slot]->i_applyDefaults(osType);
15905
15906 /* Apply parallel port defaults - not OS dependent*/
15907 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15908 mParallelPorts[slot]->i_applyDefaults();
15909
15910 /* This one covers the TPM type. */
15911 mTrustedPlatformModule->i_applyDefaults(osType);
15912
15913 /* This one covers secure boot. */
15914 hrc = mNvramStore->i_applyDefaults(osType);
15915 if (FAILED(hrc)) return hrc;
15916
15917 /* Audio stuff. */
15918 hrc = mAudioSettings->i_applyDefaults(osType);
15919 if (FAILED(hrc)) return hrc;
15920
15921 /* Storage Controllers */
15922 StorageControllerType_T hdStorageControllerType;
15923 StorageBus_T hdStorageBusType;
15924 StorageControllerType_T dvdStorageControllerType;
15925 StorageBus_T dvdStorageBusType;
15926 BOOL recommendedFloppy;
15927 ComPtr<IStorageController> floppyController;
15928 ComPtr<IStorageController> hdController;
15929 ComPtr<IStorageController> dvdController;
15930 Utf8Str strFloppyName, strDVDName, strHDName;
15931
15932 /* GUI auto generates controller names using bus type. Do the same*/
15933 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15934
15935 /* Floppy recommended? add one. */
15936 hrc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15937 if (FAILED(hrc)) return hrc;
15938 if (recommendedFloppy)
15939 {
15940 hrc = addStorageController(strFloppyName, StorageBus_Floppy, floppyController);
15941 if (FAILED(hrc)) return hrc;
15942 }
15943
15944 /* Setup one DVD storage controller. */
15945 hrc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15946 if (FAILED(hrc)) return hrc;
15947
15948 hrc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15949 if (FAILED(hrc)) return hrc;
15950
15951 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15952
15953 hrc = addStorageController(strDVDName, dvdStorageBusType, dvdController);
15954 if (FAILED(hrc)) return hrc;
15955
15956 hrc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15957 if (FAILED(hrc)) return hrc;
15958
15959 /* Setup one HDD storage controller. */
15960 hrc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15961 if (FAILED(hrc)) return hrc;
15962
15963 hrc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15964 if (FAILED(hrc)) return hrc;
15965
15966 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15967
15968 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15969 {
15970 hrc = addStorageController(strHDName, hdStorageBusType, hdController);
15971 if (FAILED(hrc)) return hrc;
15972
15973 hrc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15974 if (FAILED(hrc)) return hrc;
15975 }
15976 else
15977 {
15978 /* The HD controller is the same as DVD: */
15979 hdController = dvdController;
15980 }
15981
15982 /* Limit the AHCI port count if it's used because windows has trouble with
15983 * too many ports and other guest (OS X in particular) may take extra long
15984 * boot: */
15985
15986 // pParent = static_cast<Medium*>(aP)
15987 IStorageController *temp = hdController;
15988 ComObjPtr<StorageController> storageController;
15989 storageController = static_cast<StorageController *>(temp);
15990
15991 // tempHDController = aHDController;
15992 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15993 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15994 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15995 storageController->COMSETTER(PortCount)(1);
15996
15997 /* USB stuff */
15998
15999 bool ohciEnabled = false;
16000
16001 ComPtr<IUSBController> usbController;
16002 BOOL recommendedUSB3;
16003 BOOL recommendedUSB;
16004 BOOL usbProxyAvailable;
16005
16006 getUSBProxyAvailable(&usbProxyAvailable);
16007 if (FAILED(hrc)) return hrc;
16008
16009 hrc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
16010 if (FAILED(hrc)) return hrc;
16011 hrc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
16012 if (FAILED(hrc)) return hrc;
16013
16014 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
16015 {
16016 hrc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
16017 if (FAILED(hrc)) return hrc;
16018
16019 /* xHci includes OHCI */
16020 ohciEnabled = true;
16021 }
16022 if ( !ohciEnabled
16023 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
16024 {
16025 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16026 if (FAILED(hrc)) return hrc;
16027 ohciEnabled = true;
16028
16029 hrc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
16030 if (FAILED(hrc)) return hrc;
16031 }
16032
16033 /* Set recommended human interface device types: */
16034 BOOL recommendedUSBHID;
16035 hrc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
16036 if (FAILED(hrc)) return hrc;
16037
16038 if (recommendedUSBHID)
16039 {
16040 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
16041 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
16042 if (!ohciEnabled && !usbDeviceFilters.isNull())
16043 {
16044 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16045 if (FAILED(hrc)) return hrc;
16046 }
16047 }
16048
16049 BOOL recommendedUSBTablet;
16050 hrc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
16051 if (FAILED(hrc)) return hrc;
16052
16053 if (recommendedUSBTablet)
16054 {
16055 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
16056 if (!ohciEnabled && !usbDeviceFilters.isNull())
16057 {
16058 hrc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16059 if (FAILED(hrc)) return hrc;
16060 }
16061 }
16062
16063 /* Enable the VMMDev testing feature for bootsector VMs: */
16064 if (osTypeId == "VBoxBS_64")
16065 {
16066 hrc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
16067 if (FAILED(hrc))
16068 return hrc;
16069 }
16070
16071 return S_OK;
16072}
16073
16074#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
16075/**
16076 * Task record for change encryption settins.
16077 */
16078class Machine::ChangeEncryptionTask
16079 : public Machine::Task
16080{
16081public:
16082 ChangeEncryptionTask(Machine *m,
16083 Progress *p,
16084 const Utf8Str &t,
16085 const com::Utf8Str &aCurrentPassword,
16086 const com::Utf8Str &aCipher,
16087 const com::Utf8Str &aNewPassword,
16088 const com::Utf8Str &aNewPasswordId,
16089 const BOOL aForce,
16090 const MediaList &llMedia)
16091 : Task(m, p, t),
16092 mstrNewPassword(aNewPassword),
16093 mstrCurrentPassword(aCurrentPassword),
16094 mstrCipher(aCipher),
16095 mstrNewPasswordId(aNewPasswordId),
16096 mForce(aForce),
16097 mllMedia(llMedia)
16098 {}
16099
16100 ~ChangeEncryptionTask()
16101 {
16102 if (mstrNewPassword.length())
16103 RTMemWipeThoroughly(mstrNewPassword.mutableRaw(), mstrNewPassword.length(), 10 /* cPasses */);
16104 if (mstrCurrentPassword.length())
16105 RTMemWipeThoroughly(mstrCurrentPassword.mutableRaw(), mstrCurrentPassword.length(), 10 /* cPasses */);
16106 if (m_pCryptoIf)
16107 {
16108 m_pMachine->i_getVirtualBox()->i_releaseCryptoIf(m_pCryptoIf);
16109 m_pCryptoIf = NULL;
16110 }
16111 }
16112
16113 Utf8Str mstrNewPassword;
16114 Utf8Str mstrCurrentPassword;
16115 Utf8Str mstrCipher;
16116 Utf8Str mstrNewPasswordId;
16117 BOOL mForce;
16118 MediaList mllMedia;
16119 PCVBOXCRYPTOIF m_pCryptoIf;
16120private:
16121 void handler()
16122 {
16123 try
16124 {
16125 m_pMachine->i_changeEncryptionHandler(*this);
16126 }
16127 catch (...)
16128 {
16129 LogRel(("Some exception in the function Machine::i_changeEncryptionHandler()\n"));
16130 }
16131 }
16132
16133 friend void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task);
16134};
16135
16136/**
16137 * Scans specified directory and fills list by files found
16138 *
16139 * @returns VBox status code.
16140 * @param lstFiles
16141 * @param strDir
16142 * @param filePattern
16143 */
16144int Machine::i_findFiles(std::list<com::Utf8Str> &lstFiles, const com::Utf8Str &strDir,
16145 const com::Utf8Str &strPattern)
16146{
16147 /* To get all entries including subdirectories. */
16148 char *pszFilePattern = RTPathJoinA(strDir.c_str(), "*");
16149 if (!pszFilePattern)
16150 return VERR_NO_STR_MEMORY;
16151
16152 PRTDIRENTRYEX pDirEntry = NULL;
16153 RTDIR hDir;
16154 size_t cbDirEntry = sizeof(RTDIRENTRYEX);
16155 int vrc = RTDirOpenFiltered(&hDir, pszFilePattern, RTDIRFILTER_WINNT, 0 /*fFlags*/);
16156 if (RT_SUCCESS(vrc))
16157 {
16158 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
16159 if (pDirEntry)
16160 {
16161 while ( (vrc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK))
16162 != VERR_NO_MORE_FILES)
16163 {
16164 char *pszFilePath = NULL;
16165
16166 if (vrc == VERR_BUFFER_OVERFLOW)
16167 {
16168 /* allocate new buffer. */
16169 RTMemFree(pDirEntry);
16170 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbDirEntry);
16171 if (!pDirEntry)
16172 {
16173 vrc = VERR_NO_MEMORY;
16174 break;
16175 }
16176 /* Retry. */
16177 vrc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
16178 if (RT_FAILURE(vrc))
16179 break;
16180 }
16181 else if (RT_FAILURE(vrc))
16182 break;
16183
16184 /* Exclude . and .. */
16185 if ( (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '\0')
16186 || (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '.' && pDirEntry->szName[2] == '\0'))
16187 continue;
16188 if (RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode))
16189 {
16190 char *pszSubDirPath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
16191 if (!pszSubDirPath)
16192 {
16193 vrc = VERR_NO_STR_MEMORY;
16194 break;
16195 }
16196 vrc = i_findFiles(lstFiles, pszSubDirPath, strPattern);
16197 RTMemFree(pszSubDirPath);
16198 if (RT_FAILURE(vrc))
16199 break;
16200 continue;
16201 }
16202
16203 /* We got the new entry. */
16204 if (!RTFS_IS_FILE(pDirEntry->Info.Attr.fMode))
16205 continue;
16206
16207 if (!RTStrSimplePatternMatch(strPattern.c_str(), pDirEntry->szName))
16208 continue;
16209
16210 /* Prepend the path to the libraries. */
16211 pszFilePath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
16212 if (!pszFilePath)
16213 {
16214 vrc = VERR_NO_STR_MEMORY;
16215 break;
16216 }
16217
16218 lstFiles.push_back(pszFilePath);
16219 RTStrFree(pszFilePath);
16220 }
16221
16222 RTMemFree(pDirEntry);
16223 }
16224 else
16225 vrc = VERR_NO_MEMORY;
16226
16227 RTDirClose(hDir);
16228 }
16229 else
16230 {
16231 /* On Windows the above immediately signals that there are no
16232 * files matching, while on other platforms enumerating the
16233 * files below fails. Either way: stop searching. */
16234 }
16235
16236 if ( vrc == VERR_NO_MORE_FILES
16237 || vrc == VERR_FILE_NOT_FOUND
16238 || vrc == VERR_PATH_NOT_FOUND)
16239 vrc = VINF_SUCCESS;
16240 RTStrFree(pszFilePattern);
16241 return vrc;
16242}
16243
16244/**
16245 * Helper to set up an I/O stream to read or write a possibly encrypted file.
16246 *
16247 * @returns VBox status code.
16248 * @param pszFilename The file to open.
16249 * @param pCryptoIf Pointer to the cryptographic interface if the file should be encrypted or contains encrypted data.
16250 * @param pszKeyStore The keystore if the file should be encrypted or contains encrypted data.
16251 * @param pszPassword The password if the file should be encrypted or contains encrypted data.
16252 * @param fOpen The open flags for the file.
16253 * @param phVfsIos Where to store the handle to the I/O stream on success.
16254 */
16255int Machine::i_createIoStreamForFile(const char *pszFilename, PCVBOXCRYPTOIF pCryptoIf,
16256 const char *pszKeyStore, const char *pszPassword,
16257 uint64_t fOpen, PRTVFSIOSTREAM phVfsIos)
16258{
16259 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
16260 int vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
16261 if (RT_SUCCESS(vrc))
16262 {
16263 if (pCryptoIf)
16264 {
16265 RTVFSFILE hVfsFileCrypto = NIL_RTVFSFILE;
16266 vrc = pCryptoIf->pfnCryptoFileFromVfsFile(hVfsFile, pszKeyStore, pszPassword, &hVfsFileCrypto);
16267 if (RT_SUCCESS(vrc))
16268 {
16269 RTVfsFileRelease(hVfsFile);
16270 hVfsFile = hVfsFileCrypto;
16271 }
16272 }
16273
16274 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
16275 RTVfsFileRelease(hVfsFile);
16276 }
16277
16278 return vrc;
16279}
16280
16281/**
16282 * Helper function processing all actions for one component (saved state files,
16283 * NVRAM files, etc). Used by Machine::i_changeEncryptionHandler only.
16284 *
16285 * @param task
16286 * @param strDirectory
16287 * @param strFilePattern
16288 * @param strMagic
16289 * @param strKeyStore
16290 * @param strKeyId
16291 * @return
16292 */
16293HRESULT Machine::i_changeEncryptionForComponent(ChangeEncryptionTask &task, const com::Utf8Str strDirectory,
16294 const com::Utf8Str strFilePattern, com::Utf8Str &strKeyStore,
16295 com::Utf8Str &strKeyId, int iCipherMode)
16296{
16297 bool fDecrypt = task.mstrCurrentPassword.isNotEmpty()
16298 && task.mstrCipher.isEmpty()
16299 && task.mstrNewPassword.isEmpty()
16300 && task.mstrNewPasswordId.isEmpty();
16301 bool fEncrypt = task.mstrCurrentPassword.isEmpty()
16302 && task.mstrCipher.isNotEmpty()
16303 && task.mstrNewPassword.isNotEmpty()
16304 && task.mstrNewPasswordId.isNotEmpty();
16305
16306 /* check if the cipher is changed which causes the reencryption*/
16307
16308 const char *pszTaskCipher = NULL;
16309 if (task.mstrCipher.isNotEmpty())
16310 pszTaskCipher = getCipherString(task.mstrCipher.c_str(), iCipherMode);
16311
16312 if (!task.mForce && !fDecrypt && !fEncrypt)
16313 {
16314 char *pszCipher = NULL;
16315 int vrc = task.m_pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(strKeyStore.c_str(),
16316 NULL /*pszPassword*/,
16317 NULL /*ppbKey*/,
16318 NULL /*pcbKey*/,
16319 &pszCipher);
16320 if (RT_SUCCESS(vrc))
16321 {
16322 task.mForce = strcmp(pszTaskCipher, pszCipher) != 0;
16323 RTMemFree(pszCipher);
16324 }
16325 else
16326 return setErrorBoth(E_FAIL, vrc, tr("Obtain cipher for '%s' files failed (%Rrc)"),
16327 strFilePattern.c_str(), vrc);
16328 }
16329
16330 /* Only the password needs to be changed */
16331 if (!task.mForce && !fDecrypt && !fEncrypt)
16332 {
16333 Assert(task.m_pCryptoIf);
16334
16335 VBOXCRYPTOCTX hCryptoCtx;
16336 int vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(strKeyStore.c_str(), task.mstrCurrentPassword.c_str(), &hCryptoCtx);
16337 if (RT_FAILURE(vrc))
16338 return setErrorBoth(E_FAIL, vrc, tr("Loading old key store for '%s' files failed, (%Rrc)"),
16339 strFilePattern.c_str(), vrc);
16340 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
16341 if (RT_FAILURE(vrc))
16342 return setErrorBoth(E_FAIL, vrc, tr("Changing the password for '%s' files failed, (%Rrc)"),
16343 strFilePattern.c_str(), vrc);
16344
16345 char *pszKeyStore = NULL;
16346 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16347 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16348 if (RT_FAILURE(vrc))
16349 return setErrorBoth(E_FAIL, vrc, tr("Saving the key store for '%s' files failed, (%Rrc)"),
16350 strFilePattern.c_str(), vrc);
16351 strKeyStore = pszKeyStore;
16352 RTMemFree(pszKeyStore);
16353 strKeyId = task.mstrNewPasswordId;
16354 return S_OK;
16355 }
16356
16357 /* Reencryption required */
16358 HRESULT hrc = S_OK;
16359 int vrc = VINF_SUCCESS;
16360
16361 std::list<com::Utf8Str> lstFiles;
16362 if (SUCCEEDED(hrc))
16363 {
16364 vrc = i_findFiles(lstFiles, strDirectory, strFilePattern);
16365 if (RT_FAILURE(vrc))
16366 hrc = setErrorBoth(E_FAIL, vrc, tr("Getting file list for '%s' files failed, (%Rrc)"), strFilePattern.c_str(), vrc);
16367 }
16368 com::Utf8Str strNewKeyStore;
16369 if (SUCCEEDED(hrc))
16370 {
16371 if (!fDecrypt)
16372 {
16373 VBOXCRYPTOCTX hCryptoCtx;
16374 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(pszTaskCipher, task.mstrNewPassword.c_str(), &hCryptoCtx);
16375 if (RT_FAILURE(vrc))
16376 return setErrorBoth(E_FAIL, vrc, tr("Create new key store for '%s' files failed, (%Rrc)"),
16377 strFilePattern.c_str(), vrc);
16378
16379 char *pszKeyStore = NULL;
16380 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16381 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16382 if (RT_FAILURE(vrc))
16383 return setErrorBoth(E_FAIL, vrc, tr("Saving the new key store for '%s' files failed, (%Rrc)"),
16384 strFilePattern.c_str(), vrc);
16385 strNewKeyStore = pszKeyStore;
16386 RTMemFree(pszKeyStore);
16387 }
16388
16389 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
16390 it != lstFiles.end();
16391 ++it)
16392 {
16393 RTVFSIOSTREAM hVfsIosOld = NIL_RTVFSIOSTREAM;
16394 RTVFSIOSTREAM hVfsIosNew = NIL_RTVFSIOSTREAM;
16395
16396 uint64_t fOpenForRead = RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE;
16397 uint64_t fOpenForWrite = RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE;
16398
16399 vrc = i_createIoStreamForFile((*it).c_str(),
16400 fEncrypt ? NULL : task.m_pCryptoIf,
16401 fEncrypt ? NULL : strKeyStore.c_str(),
16402 fEncrypt ? NULL : task.mstrCurrentPassword.c_str(),
16403 fOpenForRead, &hVfsIosOld);
16404 if (RT_SUCCESS(vrc))
16405 {
16406 vrc = i_createIoStreamForFile((*it + ".tmp").c_str(),
16407 fDecrypt ? NULL : task.m_pCryptoIf,
16408 fDecrypt ? NULL : strNewKeyStore.c_str(),
16409 fDecrypt ? NULL : task.mstrNewPassword.c_str(),
16410 fOpenForWrite, &hVfsIosNew);
16411 if (RT_FAILURE(vrc))
16412 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
16413 (*it + ".tmp").c_str(), vrc);
16414 }
16415 else
16416 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"), (*it).c_str(), vrc);
16417
16418 if (RT_SUCCESS(vrc))
16419 {
16420 vrc = RTVfsUtilPumpIoStreams(hVfsIosOld, hVfsIosNew, BUF_DATA_SIZE);
16421 if (RT_FAILURE(vrc))
16422 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Changing encryption of the file '%s' failed with %Rrc"),
16423 (*it).c_str(), vrc);
16424 }
16425
16426 if (hVfsIosOld != NIL_RTVFSIOSTREAM)
16427 RTVfsIoStrmRelease(hVfsIosOld);
16428 if (hVfsIosNew != NIL_RTVFSIOSTREAM)
16429 RTVfsIoStrmRelease(hVfsIosNew);
16430 }
16431 }
16432
16433 if (SUCCEEDED(hrc))
16434 {
16435 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
16436 it != lstFiles.end();
16437 ++it)
16438 {
16439 vrc = RTFileRename((*it + ".tmp").c_str(), (*it).c_str(), RTPATHRENAME_FLAGS_REPLACE);
16440 if (RT_FAILURE(vrc))
16441 {
16442 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming the file '%s' failed, (%Rrc)"), (*it + ".tmp").c_str(), vrc);
16443 break;
16444 }
16445 }
16446 }
16447
16448 if (SUCCEEDED(hrc))
16449 {
16450 strKeyStore = strNewKeyStore;
16451 strKeyId = task.mstrNewPasswordId;
16452 }
16453
16454 return hrc;
16455}
16456
16457/**
16458 * Task thread implementation for Machine::changeEncryption(), called from
16459 * Machine::taskHandler().
16460 *
16461 * @note Locks this object for writing.
16462 *
16463 * @param task
16464 * @return
16465 */
16466void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task)
16467{
16468 LogFlowThisFuncEnter();
16469
16470 AutoCaller autoCaller(this);
16471 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
16472 if (FAILED(autoCaller.hrc()))
16473 {
16474 /* we might have been uninitialized because the session was accidentally
16475 * closed by the client, so don't assert */
16476 HRESULT hrc = setError(E_FAIL, tr("The session has been accidentally closed"));
16477 task.m_pProgress->i_notifyComplete(hrc);
16478 LogFlowThisFuncLeave();
16479 return;
16480 }
16481
16482 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16483
16484 HRESULT hrc = S_OK;
16485 com::Utf8Str strOldKeyId = mData->mstrKeyId;
16486 com::Utf8Str strOldKeyStore = mData->mstrKeyStore;
16487 try
16488 {
16489 hrc = this->i_getVirtualBox()->i_retainCryptoIf(&task.m_pCryptoIf);
16490 if (FAILED(hrc))
16491 throw hrc;
16492
16493 if (task.mstrCurrentPassword.isEmpty())
16494 {
16495 if (mData->mstrKeyStore.isNotEmpty())
16496 throw setError(VBOX_E_PASSWORD_INCORRECT,
16497 tr("The password given for the encrypted VM is incorrect"));
16498 }
16499 else
16500 {
16501 if (mData->mstrKeyStore.isEmpty())
16502 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16503 tr("The VM is not configured for encryption"));
16504 hrc = checkEncryptionPassword(task.mstrCurrentPassword);
16505 if (hrc == VBOX_E_PASSWORD_INCORRECT)
16506 throw setError(VBOX_E_PASSWORD_INCORRECT,
16507 tr("The password to decrypt the VM is incorrect"));
16508 }
16509
16510 if (task.mstrCipher.isNotEmpty())
16511 {
16512 if ( task.mstrNewPassword.isEmpty()
16513 && task.mstrNewPasswordId.isEmpty()
16514 && task.mstrCurrentPassword.isNotEmpty())
16515 {
16516 /* An empty password and password ID will default to the current password. */
16517 task.mstrNewPassword = task.mstrCurrentPassword;
16518 }
16519 else if (task.mstrNewPassword.isEmpty())
16520 throw setError(VBOX_E_OBJECT_NOT_FOUND,
16521 tr("A password must be given for the VM encryption"));
16522 else if (task.mstrNewPasswordId.isEmpty())
16523 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16524 tr("A valid identifier for the password must be given"));
16525 }
16526 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
16527 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16528 tr("The password and password identifier must be empty if the output should be unencrypted"));
16529
16530 /*
16531 * Save config.
16532 * Must be first operation to prevent making encrypted copies
16533 * for old version of the config file.
16534 */
16535 int fSave = Machine::SaveS_Force;
16536 if (task.mstrNewPassword.isNotEmpty())
16537 {
16538 VBOXCRYPTOCTX hCryptoCtx;
16539
16540 int vrc = VINF_SUCCESS;
16541 if (task.mForce || task.mstrCurrentPassword.isEmpty() || task.mstrCipher.isNotEmpty())
16542 {
16543 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(getCipherString(task.mstrCipher.c_str(), CipherModeGcm),
16544 task.mstrNewPassword.c_str(), &hCryptoCtx);
16545 if (RT_FAILURE(vrc))
16546 throw setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
16547 }
16548 else
16549 {
16550 vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(mData->mstrKeyStore.c_str(),
16551 task.mstrCurrentPassword.c_str(),
16552 &hCryptoCtx);
16553 if (RT_FAILURE(vrc))
16554 throw setErrorBoth(E_FAIL, vrc, tr("Loading old key store failed, (%Rrc)"), vrc);
16555 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
16556 if (RT_FAILURE(vrc))
16557 throw setErrorBoth(E_FAIL, vrc, tr("Changing the password failed, (%Rrc)"), vrc);
16558 }
16559
16560 char *pszKeyStore;
16561 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16562 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16563 if (RT_FAILURE(vrc))
16564 throw setErrorBoth(E_FAIL, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
16565 mData->mstrKeyStore = pszKeyStore;
16566 RTStrFree(pszKeyStore);
16567 mData->mstrKeyId = task.mstrNewPasswordId;
16568 size_t cbPassword = task.mstrNewPassword.length() + 1;
16569 uint8_t *pbPassword = (uint8_t *)task.mstrNewPassword.c_str();
16570 mData->mpKeyStore->deleteSecretKey(task.mstrNewPasswordId);
16571 mData->mpKeyStore->addSecretKey(task.mstrNewPasswordId, pbPassword, cbPassword);
16572 mNvramStore->i_addPassword(task.mstrNewPasswordId, task.mstrNewPassword);
16573
16574 /*
16575 * Remove backuped config after saving because it can contain
16576 * unencrypted version of the config
16577 */
16578 fSave |= Machine::SaveS_RemoveBackup;
16579 }
16580 else
16581 {
16582 mData->mstrKeyId.setNull();
16583 mData->mstrKeyStore.setNull();
16584 }
16585
16586 Bstr bstrCurrentPassword(task.mstrCurrentPassword);
16587 Bstr bstrCipher(getCipherString(task.mstrCipher.c_str(), CipherModeXts));
16588 Bstr bstrNewPassword(task.mstrNewPassword);
16589 Bstr bstrNewPasswordId(task.mstrNewPasswordId);
16590 /* encrypt media */
16591 alock.release();
16592 for (MediaList::iterator it = task.mllMedia.begin();
16593 it != task.mllMedia.end();
16594 ++it)
16595 {
16596 ComPtr<IProgress> pProgress1;
16597 hrc = (*it)->ChangeEncryption(bstrCurrentPassword.raw(), bstrCipher.raw(),
16598 bstrNewPassword.raw(), bstrNewPasswordId.raw(),
16599 pProgress1.asOutParam());
16600 if (FAILED(hrc)) throw hrc;
16601 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress1, 0 /* indefinite wait */);
16602 if (FAILED(hrc)) throw hrc;
16603 }
16604 alock.acquire();
16605
16606 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the SAV files")).raw(), 1);
16607
16608 Utf8Str strFullSnapshotFolder;
16609 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
16610
16611 /* .sav files (main and snapshots) */
16612 hrc = i_changeEncryptionForComponent(task, strFullSnapshotFolder, "*.sav",
16613 mSSData->strStateKeyStore, mSSData->strStateKeyId, CipherModeGcm);
16614 if (FAILED(hrc))
16615 /* the helper function already sets error object */
16616 throw hrc;
16617
16618 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the NVRAM files")).raw(), 1);
16619
16620 /* .nvram files */
16621 com::Utf8Str strNVRAMKeyId;
16622 com::Utf8Str strNVRAMKeyStore;
16623 hrc = mNvramStore->i_getEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16624 if (FAILED(hrc))
16625 throw setError(hrc, tr("Getting NVRAM encryption settings failed (%Rhrc)"), hrc);
16626
16627 Utf8Str strMachineFolder;
16628 i_calculateFullPath(".", strMachineFolder);
16629
16630 hrc = i_changeEncryptionForComponent(task, strMachineFolder, "*.nvram", strNVRAMKeyStore, strNVRAMKeyId, CipherModeGcm);
16631 if (FAILED(hrc))
16632 /* the helper function already sets error object */
16633 throw hrc;
16634
16635 hrc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16636 if (FAILED(hrc))
16637 throw setError(hrc, tr("Setting NVRAM encryption settings failed (%Rhrc)"), hrc);
16638
16639 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of log files")).raw(), 1);
16640
16641 /* .log files */
16642 com::Utf8Str strLogFolder;
16643 i_getLogFolder(strLogFolder);
16644 hrc = i_changeEncryptionForComponent(task, strLogFolder, "VBox.log*",
16645 mData->mstrLogKeyStore, mData->mstrLogKeyId, CipherModeCtr);
16646 if (FAILED(hrc))
16647 /* the helper function already sets error object */
16648 throw hrc;
16649
16650 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the config file")).raw(), 1);
16651
16652 i_saveSettings(NULL, alock, fSave);
16653 }
16654 catch (HRESULT hrcXcpt)
16655 {
16656 hrc = hrcXcpt;
16657 mData->mstrKeyId = strOldKeyId;
16658 mData->mstrKeyStore = strOldKeyStore;
16659 }
16660
16661 task.m_pProgress->i_notifyComplete(hrc);
16662
16663 LogFlowThisFuncLeave();
16664}
16665#endif /*!VBOX_WITH_FULL_VM_ENCRYPTION*/
16666
16667HRESULT Machine::changeEncryption(const com::Utf8Str &aCurrentPassword,
16668 const com::Utf8Str &aCipher,
16669 const com::Utf8Str &aNewPassword,
16670 const com::Utf8Str &aNewPasswordId,
16671 BOOL aForce,
16672 ComPtr<IProgress> &aProgress)
16673{
16674 LogFlowFuncEnter();
16675
16676#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16677 RT_NOREF(aCurrentPassword, aCipher, aNewPassword, aNewPasswordId, aForce, aProgress);
16678 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16679#else
16680 /* make the VM accessible */
16681 if (!mData->mAccessible)
16682 {
16683 if ( aCurrentPassword.isEmpty()
16684 || mData->mstrKeyId.isEmpty())
16685 return setError(E_ACCESSDENIED, tr("Machine is inaccessible"));
16686
16687 HRESULT hrc = addEncryptionPassword(mData->mstrKeyId, aCurrentPassword);
16688 if (FAILED(hrc))
16689 return hrc;
16690 }
16691
16692 AutoLimitedCaller autoCaller(this);
16693 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16694
16695 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16696
16697 /* define media to be change encryption */
16698
16699 MediaList llMedia;
16700 for (MediumAttachmentList::iterator
16701 it = mMediumAttachments->begin();
16702 it != mMediumAttachments->end();
16703 ++it)
16704 {
16705 ComObjPtr<MediumAttachment> &pAttach = *it;
16706 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
16707
16708 if (!pMedium.isNull())
16709 {
16710 AutoCaller mac(pMedium);
16711 if (FAILED(mac.hrc())) return mac.hrc();
16712 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
16713 DeviceType_T devType = pMedium->i_getDeviceType();
16714 if (devType == DeviceType_HardDisk)
16715 {
16716 /*
16717 * We need to move to last child because the Medium::changeEncryption
16718 * encrypts all chain of specified medium with its parents.
16719 * Also we perform cheking of back reference and children for
16720 * all media in the chain to raise error before we start any action.
16721 * So, we first move into root parent and then we will move to last child
16722 * keeping latter in the list for encryption.
16723 */
16724
16725 /* move to root parent */
16726 ComObjPtr<Medium> pTmpMedium = pMedium;
16727 while (pTmpMedium.isNotNull())
16728 {
16729 AutoCaller mediumAC(pTmpMedium);
16730 if (FAILED(mediumAC.hrc())) return mediumAC.hrc();
16731 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16732
16733 /* Cannot encrypt media which are attached to more than one virtual machine. */
16734 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16735 if (cBackRefs > 1)
16736 return setError(VBOX_E_INVALID_OBJECT_STATE,
16737 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16738 pTmpMedium->i_getName().c_str(), cBackRefs);
16739
16740 size_t cChildren = pTmpMedium->i_getChildren().size();
16741 if (cChildren > 1)
16742 return setError(VBOX_E_INVALID_OBJECT_STATE,
16743 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16744 pTmpMedium->i_getName().c_str(), cChildren);
16745
16746 pTmpMedium = pTmpMedium->i_getParent();
16747 }
16748 /* move to last child */
16749 pTmpMedium = pMedium;
16750 while (pTmpMedium.isNotNull() && pTmpMedium->i_getChildren().size() != 0)
16751 {
16752 AutoCaller mediumAC(pTmpMedium);
16753 if (FAILED(mediumAC.hrc())) return mediumAC.hrc();
16754 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16755
16756 /* Cannot encrypt media which are attached to more than one virtual machine. */
16757 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16758 if (cBackRefs > 1)
16759 return setError(VBOX_E_INVALID_OBJECT_STATE,
16760 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16761 pTmpMedium->i_getName().c_str(), cBackRefs);
16762
16763 size_t cChildren = pTmpMedium->i_getChildren().size();
16764 if (cChildren > 1)
16765 return setError(VBOX_E_INVALID_OBJECT_STATE,
16766 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16767 pTmpMedium->i_getName().c_str(), cChildren);
16768
16769 pTmpMedium = pTmpMedium->i_getChildren().front();
16770 }
16771 llMedia.push_back(pTmpMedium);
16772 }
16773 }
16774 }
16775
16776 ComObjPtr<Progress> pProgress;
16777 pProgress.createObject();
16778 HRESULT hrc = pProgress->init(i_getVirtualBox(),
16779 static_cast<IMachine*>(this) /* aInitiator */,
16780 tr("Change encryption"),
16781 TRUE /* fCancellable */,
16782 (ULONG)(4 + + llMedia.size()), // cOperations
16783 tr("Change encryption of the mediuma"));
16784 if (FAILED(hrc))
16785 return hrc;
16786
16787 /* create and start the task on a separate thread (note that it will not
16788 * start working until we release alock) */
16789 ChangeEncryptionTask *pTask = new ChangeEncryptionTask(this, pProgress, "VM encryption",
16790 aCurrentPassword, aCipher, aNewPassword,
16791 aNewPasswordId, aForce, llMedia);
16792 hrc = pTask->createThread();
16793 pTask = NULL;
16794 if (FAILED(hrc))
16795 return hrc;
16796
16797 pProgress.queryInterfaceTo(aProgress.asOutParam());
16798
16799 LogFlowFuncLeave();
16800
16801 return S_OK;
16802#endif
16803}
16804
16805HRESULT Machine::getEncryptionSettings(com::Utf8Str &aCipher,
16806 com::Utf8Str &aPasswordId)
16807{
16808#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16809 RT_NOREF(aCipher, aPasswordId);
16810 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16811#else
16812 AutoLimitedCaller autoCaller(this);
16813 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16814
16815 PCVBOXCRYPTOIF pCryptoIf = NULL;
16816 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16817 if (FAILED(hrc)) return hrc; /* Error is set */
16818
16819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16820
16821 if (mData->mstrKeyStore.isNotEmpty())
16822 {
16823 char *pszCipher = NULL;
16824 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), NULL /*pszPassword*/,
16825 NULL /*ppbKey*/, NULL /*pcbKey*/, &pszCipher);
16826 if (RT_SUCCESS(vrc))
16827 {
16828 aCipher = getCipherStringWithoutMode(pszCipher);
16829 RTStrFree(pszCipher);
16830 aPasswordId = mData->mstrKeyId;
16831 }
16832 else
16833 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
16834 tr("Failed to query the encryption settings with %Rrc"),
16835 vrc);
16836 }
16837 else
16838 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16839
16840 mParent->i_releaseCryptoIf(pCryptoIf);
16841
16842 return hrc;
16843#endif
16844}
16845
16846HRESULT Machine::checkEncryptionPassword(const com::Utf8Str &aPassword)
16847{
16848#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16849 RT_NOREF(aPassword);
16850 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16851#else
16852 AutoLimitedCaller autoCaller(this);
16853 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16854
16855 PCVBOXCRYPTOIF pCryptoIf = NULL;
16856 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16857 if (FAILED(hrc)) return hrc; /* Error is set */
16858
16859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16860
16861 if (mData->mstrKeyStore.isNotEmpty())
16862 {
16863 char *pszCipher = NULL;
16864 uint8_t *pbDek = NULL;
16865 size_t cbDek = 0;
16866 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), aPassword.c_str(),
16867 &pbDek, &cbDek, &pszCipher);
16868 if (RT_SUCCESS(vrc))
16869 {
16870 RTStrFree(pszCipher);
16871 RTMemSaferFree(pbDek, cbDek);
16872 }
16873 else
16874 hrc = setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc,
16875 tr("The password supplied for the encrypted machine is incorrect"));
16876 }
16877 else
16878 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16879
16880 mParent->i_releaseCryptoIf(pCryptoIf);
16881
16882 return hrc;
16883#endif
16884}
16885
16886HRESULT Machine::addEncryptionPassword(const com::Utf8Str &aId,
16887 const com::Utf8Str &aPassword)
16888{
16889#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16890 RT_NOREF(aId, aPassword);
16891 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16892#else
16893 AutoLimitedCaller autoCaller(this);
16894 AssertComRCReturn(autoCaller.hrc(), autoCaller.hrc());
16895
16896 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16897
16898 size_t cbPassword = aPassword.length() + 1;
16899 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
16900
16901 mData->mpKeyStore->addSecretKey(aId, pbPassword, cbPassword);
16902
16903 if ( mData->mAccessible
16904 && mData->mSession.mState == SessionState_Locked
16905 && mData->mSession.mLockType == LockType_VM
16906 && mData->mSession.mDirectControl != NULL)
16907 {
16908 /* get the console from the direct session */
16909 ComPtr<IConsole> console;
16910 HRESULT hrc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16911 ComAssertComRC(hrc);
16912 /* send passsword to console */
16913 console->AddEncryptionPassword(Bstr(aId).raw(),
16914 Bstr(aPassword).raw(),
16915 TRUE);
16916 }
16917
16918 if (mData->mstrKeyId == aId)
16919 {
16920 HRESULT hrc = checkEncryptionPassword(aPassword);
16921 if (FAILED(hrc))
16922 return hrc;
16923
16924 if (SUCCEEDED(hrc))
16925 {
16926 /*
16927 * Encryption is used and password is correct,
16928 * Reinit the machine if required.
16929 */
16930 BOOL fAccessible;
16931 alock.release();
16932 getAccessible(&fAccessible);
16933 alock.acquire();
16934 }
16935 }
16936
16937 /*
16938 * Add the password into the NvramStore only after
16939 * the machine becomes accessible and the NvramStore
16940 * contains key id and key store.
16941 */
16942 if (mNvramStore.isNotNull())
16943 mNvramStore->i_addPassword(aId, aPassword);
16944
16945 return S_OK;
16946#endif
16947}
16948
16949HRESULT Machine::addEncryptionPasswords(const std::vector<com::Utf8Str> &aIds,
16950 const std::vector<com::Utf8Str> &aPasswords)
16951{
16952#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16953 RT_NOREF(aIds, aPasswords);
16954 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16955#else
16956 if (aIds.size() != aPasswords.size())
16957 return setError(E_INVALIDARG, tr("Id and passwords arrays must have the same size"));
16958
16959 HRESULT hrc = S_OK;
16960 for (size_t i = 0; i < aIds.size() && SUCCEEDED(hrc); ++i)
16961 hrc = addEncryptionPassword(aIds[i], aPasswords[i]);
16962
16963 return hrc;
16964#endif
16965}
16966
16967HRESULT Machine::removeEncryptionPassword(AutoCaller &autoCaller, const com::Utf8Str &aId)
16968{
16969#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16970 RT_NOREF(autoCaller, aId);
16971 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16972#else
16973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16974
16975 if ( mData->mAccessible
16976 && mData->mSession.mState == SessionState_Locked
16977 && mData->mSession.mLockType == LockType_VM
16978 && mData->mSession.mDirectControl != NULL)
16979 {
16980 /* get the console from the direct session */
16981 ComPtr<IConsole> console;
16982 HRESULT hrc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16983 ComAssertComRC(hrc);
16984 /* send passsword to console */
16985 console->RemoveEncryptionPassword(Bstr(aId).raw());
16986 }
16987
16988 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty() && mData->mstrKeyId == aId)
16989 {
16990 if (Global::IsOnlineOrTransient(mData->mMachineState))
16991 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
16992 alock.release();
16993 autoCaller.release();
16994 /* return because all passwords are purged when machine becomes inaccessible; */
16995 return i_setInaccessible();
16996 }
16997
16998 if (mNvramStore.isNotNull())
16999 mNvramStore->i_removePassword(aId);
17000 mData->mpKeyStore->deleteSecretKey(aId);
17001 return S_OK;
17002#endif
17003}
17004
17005HRESULT Machine::clearAllEncryptionPasswords(AutoCaller &autoCaller)
17006{
17007#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
17008 RT_NOREF(autoCaller);
17009 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
17010#else
17011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
17012
17013 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty())
17014 {
17015 if (Global::IsOnlineOrTransient(mData->mMachineState))
17016 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
17017 alock.release();
17018 autoCaller.release();
17019 /* return because all passwords are purged when machine becomes inaccessible; */
17020 return i_setInaccessible();
17021 }
17022
17023 mNvramStore->i_removeAllPasswords();
17024 mData->mpKeyStore->deleteAllSecretKeys(false, true);
17025 return S_OK;
17026#endif
17027}
17028
17029#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
17030HRESULT Machine::i_setInaccessible()
17031{
17032 if (!mData->mAccessible)
17033 return S_OK;
17034
17035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
17036 VirtualBox *pParent = mParent;
17037 com::Utf8Str strConfigFile = mData->m_strConfigFile;
17038 Guid id(i_getId());
17039
17040 alock.release();
17041
17042 uninit();
17043 HRESULT hrc = initFromSettings(pParent, strConfigFile, &id, com::Utf8Str());
17044
17045 alock.acquire();
17046 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
17047 return hrc;
17048}
17049#endif
17050
17051/* This isn't handled entirely by the wrapper generator yet. */
17052#ifdef VBOX_WITH_XPCOM
17053NS_DECL_CLASSINFO(SessionMachine)
17054NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
17055
17056NS_DECL_CLASSINFO(SnapshotMachine)
17057NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
17058#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