VirtualBox

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

Last change on this file since 97168 was 96888, checked in by vboxsync, 2 years ago

Main,FE/VBoxManage: Implement possiblity to configure some of the guest debugging facilities to make it more accessible, bugref:1098

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 600.9 KB
Line 
1/* $Id: MachineImpl.cpp 96888 2022-09-26 19:29:50Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
29
30/* Make sure all the stdint.h macros are included - must come first! */
31#ifndef __STDC_LIMIT_MACROS
32# define __STDC_LIMIT_MACROS
33#endif
34#ifndef __STDC_CONSTANT_MACROS
35# define __STDC_CONSTANT_MACROS
36#endif
37
38#include "LoggingNew.h"
39#include "VirtualBoxImpl.h"
40#include "MachineImpl.h"
41#include "SnapshotImpl.h"
42#include "ClientToken.h"
43#include "ProgressImpl.h"
44#include "ProgressProxyImpl.h"
45#include "MediumAttachmentImpl.h"
46#include "MediumImpl.h"
47#include "MediumLock.h"
48#include "USBControllerImpl.h"
49#include "USBDeviceFiltersImpl.h"
50#include "HostImpl.h"
51#include "SharedFolderImpl.h"
52#include "GuestOSTypeImpl.h"
53#include "VirtualBoxErrorInfoImpl.h"
54#include "StorageControllerImpl.h"
55#include "DisplayImpl.h"
56#include "DisplayUtils.h"
57#include "MachineImplCloneVM.h"
58#include "AutostartDb.h"
59#include "SystemPropertiesImpl.h"
60#include "MachineImplMoveVM.h"
61#include "ExtPackManagerImpl.h"
62#include "MachineLaunchVMCommonWorker.h"
63#include "CryptoUtils.h"
64
65// generated header
66#include "VBoxEvents.h"
67
68#ifdef VBOX_WITH_USB
69# include "USBProxyService.h"
70#endif
71
72#include "AutoCaller.h"
73#include "HashedPw.h"
74#include "Performance.h"
75#include "StringifyEnums.h"
76
77#include <iprt/asm.h>
78#include <iprt/path.h>
79#include <iprt/dir.h>
80#include <iprt/env.h>
81#include <iprt/lockvalidator.h>
82#include <iprt/memsafer.h>
83#include <iprt/process.h>
84#include <iprt/cpp/utils.h>
85#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
86#include <iprt/sha.h>
87#include <iprt/string.h>
88#include <iprt/ctype.h>
89
90#include <VBox/com/array.h>
91#include <VBox/com/list.h>
92#include <VBox/VBoxCryptoIf.h>
93
94#include <VBox/err.h>
95#include <VBox/param.h>
96#include <VBox/settings.h>
97#include <VBox/VMMDev.h>
98#include <VBox/vmm/ssm.h>
99
100#ifdef VBOX_WITH_GUEST_PROPS
101# include <VBox/HostServices/GuestPropertySvc.h>
102# include <VBox/com/array.h>
103#endif
104
105#ifdef VBOX_WITH_SHARED_CLIPBOARD
106# include <VBox/HostServices/VBoxClipboardSvc.h>
107#endif
108
109#include "VBox/com/MultiResult.h"
110
111#include <algorithm>
112
113#ifdef VBOX_WITH_DTRACE_R3_MAIN
114# include "dtrace/VBoxAPI.h"
115#endif
116
117#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
118# define HOSTSUFF_EXE ".exe"
119#else /* !RT_OS_WINDOWS */
120# define HOSTSUFF_EXE ""
121#endif /* !RT_OS_WINDOWS */
122
123// defines / prototypes
124/////////////////////////////////////////////////////////////////////////////
125
126#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
127# define BUF_DATA_SIZE _64K
128
129enum CipherMode
130{
131 CipherModeGcm = 0,
132 CipherModeCtr,
133 CipherModeXts,
134 CipherModeMax
135};
136
137enum AesSize
138{
139 Aes128 = 0,
140 Aes256,
141 AesMax
142};
143
144const char *g_apszCipher[AesMax][CipherModeMax] =
145{
146 {"AES-GCM128", "AES-CTR128", "AES-XTS128-PLAIN64"},
147 {"AES-GCM256", "AES-CTR256", "AES-XTS256-PLAIN64"}
148};
149const char *g_apszCipherAlgo[AesMax] = {"AES-128", "AES-256"};
150
151static const char *getCipherString(const char *pszAlgo, const int iMode)
152{
153 if (iMode >= CipherModeMax)
154 return pszAlgo;
155
156 for (int i = 0; i < AesMax; i++)
157 {
158 if (strcmp(pszAlgo, g_apszCipherAlgo[i]) == 0)
159 return g_apszCipher[i][iMode];
160 }
161 return pszAlgo;
162}
163
164static const char *getCipherStringWithoutMode(const char *pszAlgo)
165{
166 for (int i = 0; i < AesMax; i++)
167 {
168 for (int j = 0; j < CipherModeMax; j++)
169 {
170 if (strcmp(pszAlgo, g_apszCipher[i][j]) == 0)
171 return g_apszCipherAlgo[i];
172 }
173 }
174 return pszAlgo;
175}
176#endif
177
178/////////////////////////////////////////////////////////////////////////////
179// Machine::Data structure
180/////////////////////////////////////////////////////////////////////////////
181
182Machine::Data::Data()
183{
184 mRegistered = FALSE;
185 pMachineConfigFile = NULL;
186 /* Contains hints on what has changed when the user is using the VM (config
187 * changes, running the VM, ...). This is used to decide if a config needs
188 * to be written to disk. */
189 flModifications = 0;
190 /* VM modification usually also trigger setting the current state to
191 * "Modified". Although this is not always the case. An e.g. is the VM
192 * initialization phase or when snapshot related data is changed. The
193 * actually behavior is controlled by the following flag. */
194 m_fAllowStateModification = false;
195 mAccessible = FALSE;
196 /* mUuid is initialized in Machine::init() */
197
198 mMachineState = MachineState_PoweredOff;
199 RTTimeNow(&mLastStateChange);
200
201 mMachineStateDeps = 0;
202 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
203 mMachineStateChangePending = 0;
204
205 mCurrentStateModified = TRUE;
206 mGuestPropertiesModified = FALSE;
207
208 mSession.mPID = NIL_RTPROCESS;
209 mSession.mLockType = LockType_Null;
210 mSession.mState = SessionState_Unlocked;
211
212#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
213 mpKeyStore = NULL;
214#endif
215}
216
217Machine::Data::~Data()
218{
219 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
220 {
221 RTSemEventMultiDestroy(mMachineStateDepsSem);
222 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
223 }
224 if (pMachineConfigFile)
225 {
226 delete pMachineConfigFile;
227 pMachineConfigFile = NULL;
228 }
229}
230
231/////////////////////////////////////////////////////////////////////////////
232// Machine::HWData structure
233/////////////////////////////////////////////////////////////////////////////
234
235Machine::HWData::HWData()
236{
237 /* default values for a newly created machine */
238 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
239 mMemorySize = 128;
240 mCPUCount = 1;
241 mCPUHotPlugEnabled = false;
242 mMemoryBalloonSize = 0;
243 mPageFusionEnabled = false;
244 mHWVirtExEnabled = true;
245 mHWVirtExNestedPagingEnabled = true;
246 mHWVirtExLargePagesEnabled = HC_ARCH_BITS == 64; /* Not supported on 32 bits hosts. */
247 mHWVirtExVPIDEnabled = true;
248 mHWVirtExUXEnabled = true;
249 mHWVirtExForceEnabled = false;
250 mHWVirtExUseNativeApi = false;
251 mHWVirtExVirtVmsaveVmload = true;
252#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
253 mPAEEnabled = true;
254#else
255 mPAEEnabled = false;
256#endif
257 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
258 mTripleFaultReset = false;
259 mAPIC = true;
260 mX2APIC = false;
261 mIBPBOnVMExit = false;
262 mIBPBOnVMEntry = false;
263 mSpecCtrl = false;
264 mSpecCtrlByHost = false;
265 mL1DFlushOnSched = true;
266 mL1DFlushOnVMEntry = false;
267 mMDSClearOnSched = true;
268 mMDSClearOnVMEntry = false;
269 mNestedHWVirt = false;
270 mHPETEnabled = false;
271 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
272 mCpuIdPortabilityLevel = 0;
273 mCpuProfile = "host";
274
275 /* default boot order: floppy - DVD - HDD */
276 mBootOrder[0] = DeviceType_Floppy;
277 mBootOrder[1] = DeviceType_DVD;
278 mBootOrder[2] = DeviceType_HardDisk;
279 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
280 mBootOrder[i] = DeviceType_Null;
281
282 mClipboardMode = ClipboardMode_Disabled;
283 mClipboardFileTransfersEnabled = FALSE;
284
285 mDnDMode = DnDMode_Disabled;
286
287 mFirmwareType = FirmwareType_BIOS;
288 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
289 mPointingHIDType = PointingHIDType_PS2Mouse;
290 mChipsetType = ChipsetType_PIIX3;
291 mIommuType = IommuType_None;
292 mParavirtProvider = ParavirtProvider_Default;
293 mEmulatedUSBCardReaderEnabled = FALSE;
294
295 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
296 mCPUAttached[i] = false;
297
298 mIOCacheEnabled = true;
299 mIOCacheSize = 5; /* 5MB */
300}
301
302Machine::HWData::~HWData()
303{
304}
305
306/////////////////////////////////////////////////////////////////////////////
307// Machine class
308/////////////////////////////////////////////////////////////////////////////
309
310// constructor / destructor
311/////////////////////////////////////////////////////////////////////////////
312
313Machine::Machine() :
314#ifdef VBOX_WITH_RESOURCE_USAGE_API
315 mCollectorGuest(NULL),
316#endif
317 mPeer(NULL),
318 mParent(NULL),
319 mSerialPorts(),
320 mParallelPorts(),
321 uRegistryNeedsSaving(0)
322{}
323
324Machine::~Machine()
325{}
326
327HRESULT Machine::FinalConstruct()
328{
329 LogFlowThisFunc(("\n"));
330 return BaseFinalConstruct();
331}
332
333void Machine::FinalRelease()
334{
335 LogFlowThisFunc(("\n"));
336 uninit();
337 BaseFinalRelease();
338}
339
340/**
341 * Initializes a new machine instance; this init() variant creates a new, empty machine.
342 * This gets called from VirtualBox::CreateMachine().
343 *
344 * @param aParent Associated parent object
345 * @param strConfigFile Local file system path to the VM settings file (can
346 * be relative to the VirtualBox config directory).
347 * @param strName name for the machine
348 * @param llGroups list of groups for the machine
349 * @param strOsType OS Type string (stored as is if aOsType is NULL).
350 * @param aOsType OS Type of this machine or NULL.
351 * @param aId UUID for the new machine.
352 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
353 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
354 * scheme (includes the UUID).
355 * @param aCipher The cipher to encrypt the VM with.
356 * @param aPasswordId The password ID, empty if the VM should not be encrypted.
357 * @param aPassword The password to encrypt the VM with.
358 *
359 * @return Success indicator. if not S_OK, the machine object is invalid
360 */
361HRESULT Machine::init(VirtualBox *aParent,
362 const Utf8Str &strConfigFile,
363 const Utf8Str &strName,
364 const StringsList &llGroups,
365 const Utf8Str &strOsType,
366 GuestOSType *aOsType,
367 const Guid &aId,
368 bool fForceOverwrite,
369 bool fDirectoryIncludesUUID,
370 const com::Utf8Str &aCipher,
371 const com::Utf8Str &aPasswordId,
372 const com::Utf8Str &aPassword)
373{
374 LogFlowThisFuncEnter();
375 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
376
377#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
378 RT_NOREF(aCipher);
379 if (aPassword.isNotEmpty() || aPasswordId.isNotEmpty())
380 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
381#endif
382
383 /* Enclose the state transition NotReady->InInit->Ready */
384 AutoInitSpan autoInitSpan(this);
385 AssertReturn(autoInitSpan.isOk(), E_FAIL);
386
387 HRESULT rc = initImpl(aParent, strConfigFile);
388 if (FAILED(rc)) return rc;
389
390#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
391 com::Utf8Str strSsmKeyId;
392 com::Utf8Str strSsmKeyStore;
393 com::Utf8Str strNVRAMKeyId;
394 com::Utf8Str strNVRAMKeyStore;
395
396 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
397 {
398 /* Resolve the cryptographic interface. */
399 PCVBOXCRYPTOIF pCryptoIf = NULL;
400 HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
401 if (SUCCEEDED(hrc))
402 {
403 CipherMode aenmMode[] = {CipherModeGcm, CipherModeGcm, CipherModeGcm, CipherModeCtr};
404 com::Utf8Str *astrKeyId[] = {&mData->mstrKeyId, &strSsmKeyId, &strNVRAMKeyId, &mData->mstrLogKeyId};
405 com::Utf8Str *astrKeyStore[] = {&mData->mstrKeyStore, &strSsmKeyStore, &strNVRAMKeyStore, &mData->mstrLogKeyStore};
406
407 for (uint32_t i = 0; i < RT_ELEMENTS(astrKeyId); i++)
408 {
409 const char *pszCipher = getCipherString(aCipher.c_str(), aenmMode[i]);
410 if (!pszCipher)
411 {
412 hrc = setError(VBOX_E_NOT_SUPPORTED,
413 tr("The cipher '%s' is not supported"), aCipher.c_str());
414 break;
415 }
416
417 VBOXCRYPTOCTX hCryptoCtx;
418 int vrc = pCryptoIf->pfnCryptoCtxCreate(pszCipher, aPassword.c_str(), &hCryptoCtx);
419 if (RT_FAILURE(vrc))
420 {
421 hrc = setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
422 break;
423 }
424
425 char *pszKeyStore;
426 vrc = pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
427 int vrc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
428 AssertRC(vrc2);
429
430 if (RT_FAILURE(vrc))
431 {
432 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
433 break;
434 }
435
436 *(astrKeyStore[i]) = pszKeyStore;
437 RTMemFree(pszKeyStore);
438 *(astrKeyId[i]) = aPasswordId;
439 }
440
441 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
442 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
443
444 if (FAILED(hrc))
445 return hrc; /* Error is set. */
446 }
447 else
448 return hrc; /* Error is set. */
449 }
450#endif
451
452 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
453 if (FAILED(rc)) return rc;
454
455 if (SUCCEEDED(rc))
456 {
457 // create an empty machine config
458 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
459
460 rc = initDataAndChildObjects();
461 }
462
463 if (SUCCEEDED(rc))
464 {
465#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
466 mSSData->strStateKeyId = strSsmKeyId;
467 mSSData->strStateKeyStore = strSsmKeyStore;
468#endif
469
470 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
471 mData->mAccessible = TRUE;
472
473 unconst(mData->mUuid) = aId;
474
475 mUserData->s.strName = strName;
476
477 if (llGroups.size())
478 mUserData->s.llGroups = llGroups;
479
480 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
481 // the "name sync" flag determines whether the machine directory gets renamed along
482 // with the machine file; say so if the settings file name is the same as the
483 // settings file parent directory (machine directory)
484 mUserData->s.fNameSync = i_isInOwnDir();
485
486 // initialize the default snapshots folder
487 rc = COMSETTER(SnapshotFolder)(NULL);
488 AssertComRC(rc);
489
490 if (aOsType)
491 {
492 /* Store OS type */
493 mUserData->s.strOsType = aOsType->i_id();
494
495 /* Let the OS type select 64-bit ness. */
496 mHWData->mLongMode = aOsType->i_is64Bit()
497 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
498
499 /* Let the OS type enable the X2APIC */
500 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
501
502 rc = aOsType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
503 AssertComRC(rc);
504 }
505 else if (!strOsType.isEmpty())
506 {
507 /* Store OS type */
508 mUserData->s.strOsType = strOsType;
509
510 /* No guest OS type object. Pick some plausible defaults which the
511 * host can handle. There's no way to know or validate anything. */
512 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
513 mHWData->mX2APIC = false;
514 }
515
516 /* Apply BIOS defaults. */
517 mBIOSSettings->i_applyDefaults(aOsType);
518
519 /* Apply TPM defaults. */
520 mTrustedPlatformModule->i_applyDefaults(aOsType);
521
522 /* Apply recording defaults. */
523 mRecordingSettings->i_applyDefaults();
524
525 /* Apply network adapters defaults */
526 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
527 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
528
529 /* Apply serial port defaults */
530 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
531 mSerialPorts[slot]->i_applyDefaults(aOsType);
532
533 /* Apply parallel port defaults */
534 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
535 mParallelPorts[slot]->i_applyDefaults();
536
537 /* Enable the VMMDev testing feature for bootsector VMs: */
538 if (aOsType && aOsType->i_id() == "VBoxBS_64")
539 mData->pMachineConfigFile->mapExtraDataItems["VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled"] = "1";
540
541#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
542 rc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
543#endif
544 if (SUCCEEDED(rc))
545 {
546 /* At this point the changing of the current state modification
547 * flag is allowed. */
548 i_allowStateModification();
549
550 /* commit all changes made during the initialization */
551 i_commit();
552 }
553 }
554
555 /* Confirm a successful initialization when it's the case */
556 if (SUCCEEDED(rc))
557 {
558#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
559 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
560 {
561 size_t cbPassword = aPassword.length() + 1;
562 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
563 mData->mpKeyStore->addSecretKey(aPasswordId, pbPassword, cbPassword);
564 }
565#endif
566
567 if (mData->mAccessible)
568 autoInitSpan.setSucceeded();
569 else
570 autoInitSpan.setLimited();
571 }
572
573 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
574 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
575 mData->mRegistered,
576 mData->mAccessible,
577 rc));
578
579 LogFlowThisFuncLeave();
580
581 return rc;
582}
583
584/**
585 * Initializes a new instance with data from machine XML (formerly Init_Registered).
586 * Gets called in two modes:
587 *
588 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
589 * UUID is specified and we mark the machine as "registered";
590 *
591 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
592 * and the machine remains unregistered until RegisterMachine() is called.
593 *
594 * @param aParent Associated parent object
595 * @param strConfigFile Local file system path to the VM settings file (can
596 * be relative to the VirtualBox config directory).
597 * @param aId UUID of the machine or NULL (see above).
598 * @param strPassword Password for decrypting the config
599 *
600 * @return Success indicator. if not S_OK, the machine object is invalid
601 */
602HRESULT Machine::initFromSettings(VirtualBox *aParent,
603 const Utf8Str &strConfigFile,
604 const Guid *aId,
605 const com::Utf8Str &strPassword)
606{
607 LogFlowThisFuncEnter();
608 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
609
610 PCVBOXCRYPTOIF pCryptoIf = NULL;
611#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
612 if (strPassword.isNotEmpty())
613 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
614#else
615 if (strPassword.isNotEmpty())
616 {
617 /* Get at the crpytographic interface. */
618 HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
619 if (FAILED(hrc))
620 return hrc; /* Error is set. */
621 }
622#endif
623
624 /* Enclose the state transition NotReady->InInit->Ready */
625 AutoInitSpan autoInitSpan(this);
626 AssertReturn(autoInitSpan.isOk(), E_FAIL);
627
628 HRESULT rc = initImpl(aParent, strConfigFile);
629 if (FAILED(rc)) return rc;
630
631 if (aId)
632 {
633 // loading a registered VM:
634 unconst(mData->mUuid) = *aId;
635 mData->mRegistered = TRUE;
636 // now load the settings from XML:
637 rc = i_registeredInit();
638 // this calls initDataAndChildObjects() and loadSettings()
639 }
640 else
641 {
642 // opening an unregistered VM (VirtualBox::OpenMachine()):
643 rc = initDataAndChildObjects();
644
645 if (SUCCEEDED(rc))
646 {
647 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
648 mData->mAccessible = TRUE;
649
650 try
651 {
652 // load and parse machine XML; this will throw on XML or logic errors
653 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
654 pCryptoIf,
655 strPassword.c_str());
656
657 // reject VM UUID duplicates, they can happen if someone
658 // tries to register an already known VM config again
659 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
660 true /* fPermitInaccessible */,
661 false /* aDoSetError */,
662 NULL) != VBOX_E_OBJECT_NOT_FOUND)
663 {
664 throw setError(E_FAIL,
665 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
666 mData->m_strConfigFile.c_str());
667 }
668
669 // use UUID from machine config
670 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
671
672#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
673 // No exception is thrown if config is encrypted, allowing us to get the uuid and the encryption fields.
674 // We fill in the encryptions fields, and the rest will be filled in if all data parsed.
675 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
676 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
677#endif
678
679 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
680 {
681 // We just set the inaccessible state and fill the error info allowing the caller
682 // to register the machine with encrypted config even if the password is incorrect
683 mData->mAccessible = FALSE;
684
685 /* fetch the current error info */
686 mData->mAccessError = com::ErrorInfo();
687
688 setError(VBOX_E_PASSWORD_INCORRECT,
689 tr("Decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
690 mData->pMachineConfigFile->uuid.raw());
691 }
692 else
693 {
694#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
695 if (strPassword.isNotEmpty())
696 {
697 size_t cbKey = strPassword.length() + 1; /* Include terminator */
698 const uint8_t *pbKey = (const uint8_t *)strPassword.c_str();
699 mData->mpKeyStore->addSecretKey(mData->mstrKeyId, pbKey, cbKey);
700 }
701#endif
702
703 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
704 NULL /* puuidRegistry */);
705 if (FAILED(rc)) throw rc;
706
707 /* At this point the changing of the current state modification
708 * flag is allowed. */
709 i_allowStateModification();
710
711 i_commit();
712 }
713 }
714 catch (HRESULT err)
715 {
716 /* we assume that error info is set by the thrower */
717 rc = err;
718 }
719 catch (...)
720 {
721 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
722 }
723 }
724 }
725
726 /* Confirm a successful initialization when it's the case */
727 if (SUCCEEDED(rc))
728 {
729 if (mData->mAccessible)
730 autoInitSpan.setSucceeded();
731 else
732 {
733 autoInitSpan.setLimited();
734
735 // uninit media from this machine's media registry, or else
736 // reloading the settings will fail
737 mParent->i_unregisterMachineMedia(i_getId());
738 }
739 }
740
741#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
742 if (pCryptoIf)
743 {
744 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
745 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
746 }
747#endif
748
749 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
750 "rc=%08X\n",
751 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
752 mData->mRegistered, mData->mAccessible, rc));
753
754 LogFlowThisFuncLeave();
755
756 return rc;
757}
758
759/**
760 * Initializes a new instance from a machine config that is already in memory
761 * (import OVF case). Since we are importing, the UUID in the machine
762 * config is ignored and we always generate a fresh one.
763 *
764 * @param aParent Associated parent object.
765 * @param strName Name for the new machine; this overrides what is specified in config.
766 * @param strSettingsFilename File name of .vbox file.
767 * @param config Machine configuration loaded and parsed from XML.
768 *
769 * @return Success indicator. if not S_OK, the machine object is invalid
770 */
771HRESULT Machine::init(VirtualBox *aParent,
772 const Utf8Str &strName,
773 const Utf8Str &strSettingsFilename,
774 const settings::MachineConfigFile &config)
775{
776 LogFlowThisFuncEnter();
777
778 /* Enclose the state transition NotReady->InInit->Ready */
779 AutoInitSpan autoInitSpan(this);
780 AssertReturn(autoInitSpan.isOk(), E_FAIL);
781
782 HRESULT rc = initImpl(aParent, strSettingsFilename);
783 if (FAILED(rc)) return rc;
784
785 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
786 if (FAILED(rc)) return rc;
787
788 rc = initDataAndChildObjects();
789
790 if (SUCCEEDED(rc))
791 {
792 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
793 mData->mAccessible = TRUE;
794
795 // create empty machine config for instance data
796 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
797
798 // generate fresh UUID, ignore machine config
799 unconst(mData->mUuid).create();
800
801 rc = i_loadMachineDataFromSettings(config,
802 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
803
804 // override VM name as well, it may be different
805 mUserData->s.strName = strName;
806
807 if (SUCCEEDED(rc))
808 {
809 /* At this point the changing of the current state modification
810 * flag is allowed. */
811 i_allowStateModification();
812
813 /* commit all changes made during the initialization */
814 i_commit();
815 }
816 }
817
818 /* Confirm a successful initialization when it's the case */
819 if (SUCCEEDED(rc))
820 {
821 if (mData->mAccessible)
822 autoInitSpan.setSucceeded();
823 else
824 {
825 /* Ignore all errors from unregistering, they would destroy
826- * the more interesting error information we already have,
827- * pinpointing the issue with the VM config. */
828 ErrorInfoKeeper eik;
829
830 autoInitSpan.setLimited();
831
832 // uninit media from this machine's media registry, or else
833 // reloading the settings will fail
834 mParent->i_unregisterMachineMedia(i_getId());
835 }
836 }
837
838 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
839 "rc=%08X\n",
840 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
841 mData->mRegistered, mData->mAccessible, rc));
842
843 LogFlowThisFuncLeave();
844
845 return rc;
846}
847
848/**
849 * Shared code between the various init() implementations.
850 * @param aParent The VirtualBox object.
851 * @param strConfigFile Settings file.
852 * @return
853 */
854HRESULT Machine::initImpl(VirtualBox *aParent,
855 const Utf8Str &strConfigFile)
856{
857 LogFlowThisFuncEnter();
858
859 AssertReturn(aParent, E_INVALIDARG);
860 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
861
862 HRESULT rc = S_OK;
863
864 /* share the parent weakly */
865 unconst(mParent) = aParent;
866
867 /* allocate the essential machine data structure (the rest will be
868 * allocated later by initDataAndChildObjects() */
869 mData.allocate();
870
871 /* memorize the config file name (as provided) */
872 mData->m_strConfigFile = strConfigFile;
873
874 /* get the full file name */
875 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
876 if (RT_FAILURE(vrc1))
877 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
878 tr("Invalid machine settings file name '%s' (%Rrc)"),
879 strConfigFile.c_str(),
880 vrc1);
881
882#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
883 /** @todo Only create when the machine is going to be encrypted. */
884 /* Non-pageable memory is not accessible for non-VM process */
885 mData->mpKeyStore = new SecretKeyStore(false /* fKeyBufNonPageable */);
886 AssertReturn(mData->mpKeyStore, E_OUTOFMEMORY);
887#endif
888
889 LogFlowThisFuncLeave();
890
891 return rc;
892}
893
894/**
895 * Tries to create a machine settings file in the path stored in the machine
896 * instance data. Used when a new machine is created to fail gracefully if
897 * the settings file could not be written (e.g. because machine dir is read-only).
898 * @return
899 */
900HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
901{
902 HRESULT rc = S_OK;
903
904 // when we create a new machine, we must be able to create the settings file
905 RTFILE f = NIL_RTFILE;
906 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
907 if ( RT_SUCCESS(vrc)
908 || vrc == VERR_SHARING_VIOLATION
909 )
910 {
911 if (RT_SUCCESS(vrc))
912 RTFileClose(f);
913 if (!fForceOverwrite)
914 rc = setError(VBOX_E_FILE_ERROR,
915 tr("Machine settings file '%s' already exists"),
916 mData->m_strConfigFileFull.c_str());
917 else
918 {
919 /* try to delete the config file, as otherwise the creation
920 * of a new settings file will fail. */
921 i_deleteFile(mData->m_strConfigFileFull.c_str(), false /* fIgnoreFailures */, tr("existing settings file"));
922 }
923 }
924 else if ( vrc != VERR_FILE_NOT_FOUND
925 && vrc != VERR_PATH_NOT_FOUND
926 )
927 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
928 tr("Invalid machine settings file name '%s' (%Rrc)"),
929 mData->m_strConfigFileFull.c_str(),
930 vrc);
931 return rc;
932}
933
934/**
935 * Initializes the registered machine by loading the settings file.
936 * This method is separated from #init() in order to make it possible to
937 * retry the operation after VirtualBox startup instead of refusing to
938 * startup the whole VirtualBox server in case if the settings file of some
939 * registered VM is invalid or inaccessible.
940 *
941 * @note Must be always called from this object's write lock
942 * (unless called from #init() that doesn't need any locking).
943 * @note Locks the mUSBController method for writing.
944 * @note Subclasses must not call this method.
945 */
946HRESULT Machine::i_registeredInit()
947{
948 AssertReturn(!i_isSessionMachine(), E_FAIL);
949 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
950 AssertReturn(mData->mUuid.isValid(), E_FAIL);
951 AssertReturn(!mData->mAccessible, E_FAIL);
952
953 HRESULT rc = initDataAndChildObjects();
954
955 if (SUCCEEDED(rc))
956 {
957 /* Temporarily reset the registered flag in order to let setters
958 * potentially called from loadSettings() succeed (isMutable() used in
959 * all setters will return FALSE for a Machine instance if mRegistered
960 * is TRUE). */
961 mData->mRegistered = FALSE;
962
963 PCVBOXCRYPTOIF pCryptoIf = NULL;
964 SecretKey *pKey = NULL;
965 const char *pszPassword = NULL;
966#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
967 /* Resolve password and cryptographic support interface if machine is encrypted. */
968 if (mData->mstrKeyId.isNotEmpty())
969 {
970 /* Get at the crpytographic interface. */
971 rc = mParent->i_retainCryptoIf(&pCryptoIf);
972 if (SUCCEEDED(rc))
973 {
974 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
975 if (RT_SUCCESS(vrc))
976 pszPassword = (const char *)pKey->getKeyBuffer();
977 else
978 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to retain key for key ID '%s' with %Rrc"),
979 mData->mstrKeyId.c_str(), vrc);
980 }
981 }
982#else
983 RT_NOREF(pKey);
984#endif
985
986 if (SUCCEEDED(rc))
987 {
988 try
989 {
990 // load and parse machine XML; this will throw on XML or logic errors
991 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
992 pCryptoIf, pszPassword);
993
994 if (mData->mUuid != mData->pMachineConfigFile->uuid)
995 throw setError(E_FAIL,
996 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
997 mData->pMachineConfigFile->uuid.raw(),
998 mData->m_strConfigFileFull.c_str(),
999 mData->mUuid.toString().c_str(),
1000 mParent->i_settingsFilePath().c_str());
1001
1002#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1003 // If config is encrypted, no exception is thrown allowing us to get the uuid and the encryption fields.
1004 // We fill in the encryptions fields, and the rest will be filled in if all data parsed
1005 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
1006 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
1007
1008 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
1009 rc = setError(VBOX_E_PASSWORD_INCORRECT,
1010 tr("Config decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
1011 mData->pMachineConfigFile->uuid.raw());
1012 else
1013#endif
1014 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
1015 NULL /* const Guid *puuidRegistry */);
1016 if (FAILED(rc)) throw rc;
1017 }
1018 catch (HRESULT err)
1019 {
1020 /* we assume that error info is set by the thrower */
1021 rc = err;
1022 }
1023 catch (...)
1024 {
1025 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
1026 }
1027
1028 /* Restore the registered flag (even on failure) */
1029 mData->mRegistered = TRUE;
1030 }
1031
1032#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1033 if (pCryptoIf)
1034 mParent->i_releaseCryptoIf(pCryptoIf);
1035 if (pKey)
1036 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
1037#endif
1038 }
1039
1040 if (SUCCEEDED(rc))
1041 {
1042 /* Set mAccessible to TRUE only if we successfully locked and loaded
1043 * the settings file */
1044 mData->mAccessible = TRUE;
1045
1046 /* commit all changes made during loading the settings file */
1047 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
1048 /// @todo r=klaus for some reason the settings loading logic backs up
1049 // the settings, and therefore a commit is needed. Should probably be changed.
1050 }
1051 else
1052 {
1053 /* If the machine is registered, then, instead of returning a
1054 * failure, we mark it as inaccessible and set the result to
1055 * success to give it a try later */
1056
1057 /* fetch the current error info */
1058 mData->mAccessError = com::ErrorInfo();
1059 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
1060
1061 /* rollback all changes */
1062 i_rollback(false /* aNotify */);
1063
1064 // uninit media from this machine's media registry, or else
1065 // reloading the settings will fail
1066 mParent->i_unregisterMachineMedia(i_getId());
1067
1068 /* uninitialize the common part to make sure all data is reset to
1069 * default (null) values */
1070 uninitDataAndChildObjects();
1071
1072 rc = S_OK;
1073 }
1074
1075 return rc;
1076}
1077
1078/**
1079 * Uninitializes the instance.
1080 * Called either from FinalRelease() or by the parent when it gets destroyed.
1081 *
1082 * @note The caller of this method must make sure that this object
1083 * a) doesn't have active callers on the current thread and b) is not locked
1084 * by the current thread; otherwise uninit() will hang either a) due to
1085 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
1086 * a dead-lock caused by this thread waiting for all callers on the other
1087 * threads are done but preventing them from doing so by holding a lock.
1088 */
1089void Machine::uninit()
1090{
1091 LogFlowThisFuncEnter();
1092
1093 Assert(!isWriteLockOnCurrentThread());
1094
1095 Assert(!uRegistryNeedsSaving);
1096 if (uRegistryNeedsSaving)
1097 {
1098 AutoCaller autoCaller(this);
1099 if (SUCCEEDED(autoCaller.rc()))
1100 {
1101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1102 i_saveSettings(NULL, alock, Machine::SaveS_Force);
1103 }
1104 }
1105
1106 /* Enclose the state transition Ready->InUninit->NotReady */
1107 AutoUninitSpan autoUninitSpan(this);
1108 if (autoUninitSpan.uninitDone())
1109 return;
1110
1111 Assert(!i_isSnapshotMachine());
1112 Assert(!i_isSessionMachine());
1113 Assert(!!mData);
1114
1115 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
1116 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
1117
1118 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1119
1120 if (!mData->mSession.mMachine.isNull())
1121 {
1122 /* Theoretically, this can only happen if the VirtualBox server has been
1123 * terminated while there were clients running that owned open direct
1124 * sessions. Since in this case we are definitely called by
1125 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
1126 * won't happen on the client watcher thread (because it has a
1127 * VirtualBox caller for the duration of the
1128 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
1129 * cannot happen until the VirtualBox caller is released). This is
1130 * important, because SessionMachine::uninit() cannot correctly operate
1131 * after we return from this method (it expects the Machine instance is
1132 * still valid). We'll call it ourselves below.
1133 */
1134 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
1135 (SessionMachine*)mData->mSession.mMachine));
1136
1137 if (Global::IsOnlineOrTransient(mData->mMachineState))
1138 {
1139 Log1WarningThisFunc(("Setting state to Aborted!\n"));
1140 /* set machine state using SessionMachine reimplementation */
1141 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
1142 }
1143
1144 /*
1145 * Uninitialize SessionMachine using public uninit() to indicate
1146 * an unexpected uninitialization.
1147 */
1148 mData->mSession.mMachine->uninit();
1149 /* SessionMachine::uninit() must set mSession.mMachine to null */
1150 Assert(mData->mSession.mMachine.isNull());
1151 }
1152
1153 // uninit media from this machine's media registry, if they're still there
1154 Guid uuidMachine(i_getId());
1155
1156 /* the lock is no more necessary (SessionMachine is uninitialized) */
1157 alock.release();
1158
1159 /* XXX This will fail with
1160 * "cannot be closed because it is still attached to 1 virtual machines"
1161 * because at this point we did not call uninitDataAndChildObjects() yet
1162 * and therefore also removeBackReference() for all these mediums was not called! */
1163
1164 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
1165 mParent->i_unregisterMachineMedia(uuidMachine);
1166
1167 // has machine been modified?
1168 if (mData->flModifications)
1169 {
1170 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
1171 i_rollback(false /* aNotify */);
1172 }
1173
1174 if (mData->mAccessible)
1175 uninitDataAndChildObjects();
1176
1177#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1178 if (mData->mpKeyStore != NULL)
1179 delete mData->mpKeyStore;
1180#endif
1181
1182 /* free the essential data structure last */
1183 mData.free();
1184
1185 LogFlowThisFuncLeave();
1186}
1187
1188// Wrapped IMachine properties
1189/////////////////////////////////////////////////////////////////////////////
1190HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
1191{
1192 /* mParent is constant during life time, no need to lock */
1193 ComObjPtr<VirtualBox> pVirtualBox(mParent);
1194 aParent = pVirtualBox;
1195
1196 return S_OK;
1197}
1198
1199
1200HRESULT Machine::getAccessible(BOOL *aAccessible)
1201{
1202 /* In some cases (medium registry related), it is necessary to be able to
1203 * go through the list of all machines. Happens when an inaccessible VM
1204 * has a sensible medium registry. */
1205 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
1206 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1207
1208 HRESULT rc = S_OK;
1209
1210 if (!mData->mAccessible)
1211 {
1212 /* try to initialize the VM once more if not accessible */
1213
1214 AutoReinitSpan autoReinitSpan(this);
1215 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
1216
1217#ifdef DEBUG
1218 LogFlowThisFunc(("Dumping media backreferences\n"));
1219 mParent->i_dumpAllBackRefs();
1220#endif
1221
1222 if (mData->pMachineConfigFile)
1223 {
1224 // reset the XML file to force loadSettings() (called from i_registeredInit())
1225 // to parse it again; the file might have changed
1226 delete mData->pMachineConfigFile;
1227 mData->pMachineConfigFile = NULL;
1228 }
1229
1230 rc = i_registeredInit();
1231
1232 if (SUCCEEDED(rc) && mData->mAccessible)
1233 {
1234 autoReinitSpan.setSucceeded();
1235
1236 /* make sure interesting parties will notice the accessibility
1237 * state change */
1238 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
1239 mParent->i_onMachineDataChanged(mData->mUuid);
1240 }
1241 }
1242
1243 if (SUCCEEDED(rc))
1244 *aAccessible = mData->mAccessible;
1245
1246 LogFlowThisFuncLeave();
1247
1248 return rc;
1249}
1250
1251HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
1252{
1253 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1254
1255 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
1256 {
1257 /* return shortly */
1258 aAccessError = NULL;
1259 return S_OK;
1260 }
1261
1262 HRESULT rc = S_OK;
1263
1264 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1265 rc = errorInfo.createObject();
1266 if (SUCCEEDED(rc))
1267 {
1268 errorInfo->init(mData->mAccessError.getResultCode(),
1269 mData->mAccessError.getInterfaceID().ref(),
1270 Utf8Str(mData->mAccessError.getComponent()).c_str(),
1271 Utf8Str(mData->mAccessError.getText()));
1272 aAccessError = errorInfo;
1273 }
1274
1275 return rc;
1276}
1277
1278HRESULT Machine::getName(com::Utf8Str &aName)
1279{
1280 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1281
1282 aName = mUserData->s.strName;
1283
1284 return S_OK;
1285}
1286
1287HRESULT Machine::setName(const com::Utf8Str &aName)
1288{
1289 // prohibit setting a UUID only as the machine name, or else it can
1290 // never be found by findMachine()
1291 Guid test(aName);
1292
1293 if (test.isValid())
1294 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1295
1296 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1297
1298 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1299 if (FAILED(rc)) return rc;
1300
1301 i_setModified(IsModified_MachineData);
1302 mUserData.backup();
1303 mUserData->s.strName = aName;
1304
1305 return S_OK;
1306}
1307
1308HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1309{
1310 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1311
1312 aDescription = mUserData->s.strDescription;
1313
1314 return S_OK;
1315}
1316
1317HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1318{
1319 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1320
1321 // this can be done in principle in any state as it doesn't affect the VM
1322 // significantly, but play safe by not messing around while complex
1323 // activities are going on
1324 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1325 if (FAILED(rc)) return rc;
1326
1327 i_setModified(IsModified_MachineData);
1328 mUserData.backup();
1329 mUserData->s.strDescription = aDescription;
1330
1331 return S_OK;
1332}
1333
1334HRESULT Machine::getId(com::Guid &aId)
1335{
1336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1337
1338 aId = mData->mUuid;
1339
1340 return S_OK;
1341}
1342
1343HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1344{
1345 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1346 aGroups.resize(mUserData->s.llGroups.size());
1347 size_t i = 0;
1348 for (StringsList::const_iterator
1349 it = mUserData->s.llGroups.begin();
1350 it != mUserData->s.llGroups.end();
1351 ++it, ++i)
1352 aGroups[i] = (*it);
1353
1354 return S_OK;
1355}
1356
1357HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1358{
1359 StringsList llGroups;
1360 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1361 if (FAILED(rc))
1362 return rc;
1363
1364 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1365
1366 rc = i_checkStateDependency(MutableOrSavedStateDep);
1367 if (FAILED(rc)) return rc;
1368
1369 i_setModified(IsModified_MachineData);
1370 mUserData.backup();
1371 mUserData->s.llGroups = llGroups;
1372
1373 return S_OK;
1374}
1375
1376HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1377{
1378 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1379
1380 aOSTypeId = mUserData->s.strOsType;
1381
1382 return S_OK;
1383}
1384
1385HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1386{
1387 /* look up the object by Id to check it is valid */
1388 ComObjPtr<GuestOSType> pGuestOSType;
1389 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1390
1391 /* when setting, always use the "etalon" value for consistency -- lookup
1392 * by ID is case-insensitive and the input value may have different case */
1393 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1394
1395 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1396
1397 HRESULT rc = i_checkStateDependency(MutableStateDep);
1398 if (FAILED(rc)) return rc;
1399
1400 i_setModified(IsModified_MachineData);
1401 mUserData.backup();
1402 mUserData->s.strOsType = osTypeId;
1403
1404 return S_OK;
1405}
1406
1407HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1408{
1409 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1410
1411 *aFirmwareType = mHWData->mFirmwareType;
1412
1413 return S_OK;
1414}
1415
1416HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1417{
1418 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1419
1420 HRESULT rc = i_checkStateDependency(MutableStateDep);
1421 if (FAILED(rc)) return rc;
1422
1423 i_setModified(IsModified_MachineData);
1424 mHWData.backup();
1425 mHWData->mFirmwareType = aFirmwareType;
1426 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1427 alock.release();
1428
1429 mNvramStore->i_updateNonVolatileStorageFile(strNVRAM);
1430
1431 return S_OK;
1432}
1433
1434HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1435{
1436 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1437
1438 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1439
1440 return S_OK;
1441}
1442
1443HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1444{
1445 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1446
1447 HRESULT rc = i_checkStateDependency(MutableStateDep);
1448 if (FAILED(rc)) return rc;
1449
1450 i_setModified(IsModified_MachineData);
1451 mHWData.backup();
1452 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1453
1454 return S_OK;
1455}
1456
1457HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1458{
1459 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1460
1461 *aPointingHIDType = mHWData->mPointingHIDType;
1462
1463 return S_OK;
1464}
1465
1466HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1467{
1468 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1469
1470 HRESULT rc = i_checkStateDependency(MutableStateDep);
1471 if (FAILED(rc)) return rc;
1472
1473 i_setModified(IsModified_MachineData);
1474 mHWData.backup();
1475 mHWData->mPointingHIDType = aPointingHIDType;
1476
1477 return S_OK;
1478}
1479
1480HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1481{
1482 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1483
1484 *aChipsetType = mHWData->mChipsetType;
1485
1486 return S_OK;
1487}
1488
1489HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1490{
1491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1492
1493 HRESULT rc = i_checkStateDependency(MutableStateDep);
1494 if (FAILED(rc)) return rc;
1495
1496 if (aChipsetType != mHWData->mChipsetType)
1497 {
1498 i_setModified(IsModified_MachineData);
1499 mHWData.backup();
1500 mHWData->mChipsetType = aChipsetType;
1501
1502 // Resize network adapter array, to be finalized on commit/rollback.
1503 // We must not throw away entries yet, otherwise settings are lost
1504 // without a way to roll back.
1505 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1506 size_t oldCount = mNetworkAdapters.size();
1507 if (newCount > oldCount)
1508 {
1509 mNetworkAdapters.resize(newCount);
1510 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1511 {
1512 unconst(mNetworkAdapters[slot]).createObject();
1513 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1514 }
1515 }
1516 }
1517
1518 return S_OK;
1519}
1520
1521HRESULT Machine::getIommuType(IommuType_T *aIommuType)
1522{
1523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1524
1525 *aIommuType = mHWData->mIommuType;
1526
1527 return S_OK;
1528}
1529
1530HRESULT Machine::setIommuType(IommuType_T aIommuType)
1531{
1532 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1533
1534 HRESULT rc = i_checkStateDependency(MutableStateDep);
1535 if (FAILED(rc)) return rc;
1536
1537 if (aIommuType != mHWData->mIommuType)
1538 {
1539 if (aIommuType == IommuType_Intel)
1540 {
1541#ifndef VBOX_WITH_IOMMU_INTEL
1542 LogRelFunc(("Setting Intel IOMMU when Intel IOMMU support not available!\n"));
1543 return E_UNEXPECTED;
1544#endif
1545 }
1546
1547 i_setModified(IsModified_MachineData);
1548 mHWData.backup();
1549 mHWData->mIommuType = aIommuType;
1550 }
1551
1552 return S_OK;
1553}
1554
1555HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1556{
1557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1558
1559 aParavirtDebug = mHWData->mParavirtDebug;
1560 return S_OK;
1561}
1562
1563HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1564{
1565 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1566
1567 HRESULT rc = i_checkStateDependency(MutableStateDep);
1568 if (FAILED(rc)) return rc;
1569
1570 /** @todo Parse/validate options? */
1571 if (aParavirtDebug != mHWData->mParavirtDebug)
1572 {
1573 i_setModified(IsModified_MachineData);
1574 mHWData.backup();
1575 mHWData->mParavirtDebug = aParavirtDebug;
1576 }
1577
1578 return S_OK;
1579}
1580
1581HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1582{
1583 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1584
1585 *aParavirtProvider = mHWData->mParavirtProvider;
1586
1587 return S_OK;
1588}
1589
1590HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1591{
1592 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1593
1594 HRESULT rc = i_checkStateDependency(MutableStateDep);
1595 if (FAILED(rc)) return rc;
1596
1597 if (aParavirtProvider != mHWData->mParavirtProvider)
1598 {
1599 i_setModified(IsModified_MachineData);
1600 mHWData.backup();
1601 mHWData->mParavirtProvider = aParavirtProvider;
1602 }
1603
1604 return S_OK;
1605}
1606
1607HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1608{
1609 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1610
1611 *aParavirtProvider = mHWData->mParavirtProvider;
1612 switch (mHWData->mParavirtProvider)
1613 {
1614 case ParavirtProvider_None:
1615 case ParavirtProvider_HyperV:
1616 case ParavirtProvider_KVM:
1617 case ParavirtProvider_Minimal:
1618 break;
1619
1620 /* Resolve dynamic provider types to the effective types. */
1621 default:
1622 {
1623 ComObjPtr<GuestOSType> pGuestOSType;
1624 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1625 pGuestOSType);
1626 if (FAILED(hrc2) || pGuestOSType.isNull())
1627 {
1628 *aParavirtProvider = ParavirtProvider_None;
1629 break;
1630 }
1631
1632 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1633 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1634
1635 switch (mHWData->mParavirtProvider)
1636 {
1637 case ParavirtProvider_Legacy:
1638 {
1639 if (fOsXGuest)
1640 *aParavirtProvider = ParavirtProvider_Minimal;
1641 else
1642 *aParavirtProvider = ParavirtProvider_None;
1643 break;
1644 }
1645
1646 case ParavirtProvider_Default:
1647 {
1648 if (fOsXGuest)
1649 *aParavirtProvider = ParavirtProvider_Minimal;
1650 else if ( mUserData->s.strOsType == "Windows11_64"
1651 || mUserData->s.strOsType == "Windows10"
1652 || mUserData->s.strOsType == "Windows10_64"
1653 || mUserData->s.strOsType == "Windows81"
1654 || mUserData->s.strOsType == "Windows81_64"
1655 || mUserData->s.strOsType == "Windows8"
1656 || mUserData->s.strOsType == "Windows8_64"
1657 || mUserData->s.strOsType == "Windows7"
1658 || mUserData->s.strOsType == "Windows7_64"
1659 || mUserData->s.strOsType == "WindowsVista"
1660 || mUserData->s.strOsType == "WindowsVista_64"
1661 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1662 || mUserData->s.strOsType.startsWith("Windows201"))
1663 && mUserData->s.strOsType.endsWith("_64"))
1664 || mUserData->s.strOsType == "Windows2012"
1665 || mUserData->s.strOsType == "Windows2012_64"
1666 || mUserData->s.strOsType == "Windows2008"
1667 || mUserData->s.strOsType == "Windows2008_64")
1668 {
1669 *aParavirtProvider = ParavirtProvider_HyperV;
1670 }
1671 else if (guestTypeFamilyId == "Linux" &&
1672 mUserData->s.strOsType != "Linux22" && // Linux22 and Linux24{_64} excluded as they're too old
1673 mUserData->s.strOsType != "Linux24" && // to have any KVM paravirtualization support.
1674 mUserData->s.strOsType != "Linux24_64")
1675 {
1676 *aParavirtProvider = ParavirtProvider_KVM;
1677 }
1678 else
1679 *aParavirtProvider = ParavirtProvider_None;
1680 break;
1681 }
1682
1683 default: AssertFailedBreak(); /* Shut up MSC. */
1684 }
1685 break;
1686 }
1687 }
1688
1689 Assert( *aParavirtProvider == ParavirtProvider_None
1690 || *aParavirtProvider == ParavirtProvider_Minimal
1691 || *aParavirtProvider == ParavirtProvider_HyperV
1692 || *aParavirtProvider == ParavirtProvider_KVM);
1693 return S_OK;
1694}
1695
1696HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1697{
1698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1699
1700 aHardwareVersion = mHWData->mHWVersion;
1701
1702 return S_OK;
1703}
1704
1705HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1706{
1707 /* check known version */
1708 Utf8Str hwVersion = aHardwareVersion;
1709 if ( hwVersion.compare("1") != 0
1710 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1711 return setError(E_INVALIDARG,
1712 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1713
1714 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1715
1716 HRESULT rc = i_checkStateDependency(MutableStateDep);
1717 if (FAILED(rc)) return rc;
1718
1719 i_setModified(IsModified_MachineData);
1720 mHWData.backup();
1721 mHWData->mHWVersion = aHardwareVersion;
1722
1723 return S_OK;
1724}
1725
1726HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1727{
1728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1729
1730 if (!mHWData->mHardwareUUID.isZero())
1731 aHardwareUUID = mHWData->mHardwareUUID;
1732 else
1733 aHardwareUUID = mData->mUuid;
1734
1735 return S_OK;
1736}
1737
1738HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1739{
1740 if (!aHardwareUUID.isValid())
1741 return E_INVALIDARG;
1742
1743 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1744
1745 HRESULT rc = i_checkStateDependency(MutableStateDep);
1746 if (FAILED(rc)) return rc;
1747
1748 i_setModified(IsModified_MachineData);
1749 mHWData.backup();
1750 if (aHardwareUUID == mData->mUuid)
1751 mHWData->mHardwareUUID.clear();
1752 else
1753 mHWData->mHardwareUUID = aHardwareUUID;
1754
1755 return S_OK;
1756}
1757
1758HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1759{
1760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1761
1762 *aMemorySize = mHWData->mMemorySize;
1763
1764 return S_OK;
1765}
1766
1767HRESULT Machine::setMemorySize(ULONG aMemorySize)
1768{
1769 /* check RAM limits */
1770 if ( aMemorySize < MM_RAM_MIN_IN_MB
1771 || aMemorySize > MM_RAM_MAX_IN_MB
1772 )
1773 return setError(E_INVALIDARG,
1774 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1775 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1776
1777 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1778
1779 HRESULT rc = i_checkStateDependency(MutableStateDep);
1780 if (FAILED(rc)) return rc;
1781
1782 i_setModified(IsModified_MachineData);
1783 mHWData.backup();
1784 mHWData->mMemorySize = aMemorySize;
1785
1786 return S_OK;
1787}
1788
1789HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1790{
1791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1792
1793 *aCPUCount = mHWData->mCPUCount;
1794
1795 return S_OK;
1796}
1797
1798HRESULT Machine::setCPUCount(ULONG aCPUCount)
1799{
1800 /* check CPU limits */
1801 if ( aCPUCount < SchemaDefs::MinCPUCount
1802 || aCPUCount > SchemaDefs::MaxCPUCount
1803 )
1804 return setError(E_INVALIDARG,
1805 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1806 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1807
1808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1809
1810 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1811 if (mHWData->mCPUHotPlugEnabled)
1812 {
1813 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1814 {
1815 if (mHWData->mCPUAttached[idx])
1816 return setError(E_INVALIDARG,
1817 tr("There is still a CPU attached to socket %lu."
1818 "Detach the CPU before removing the socket"),
1819 aCPUCount, idx+1);
1820 }
1821 }
1822
1823 HRESULT rc = i_checkStateDependency(MutableStateDep);
1824 if (FAILED(rc)) return rc;
1825
1826 i_setModified(IsModified_MachineData);
1827 mHWData.backup();
1828 mHWData->mCPUCount = aCPUCount;
1829
1830 return S_OK;
1831}
1832
1833HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1834{
1835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1836
1837 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1838
1839 return S_OK;
1840}
1841
1842HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1843{
1844 HRESULT rc = S_OK;
1845
1846 /* check throttle limits */
1847 if ( aCPUExecutionCap < 1
1848 || aCPUExecutionCap > 100
1849 )
1850 return setError(E_INVALIDARG,
1851 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1852 aCPUExecutionCap, 1, 100);
1853
1854 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1855
1856 rc = i_checkStateDependency(MutableOrRunningStateDep);
1857 if (FAILED(rc)) return rc;
1858
1859 alock.release();
1860 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1861 alock.acquire();
1862 if (FAILED(rc)) return rc;
1863
1864 i_setModified(IsModified_MachineData);
1865 mHWData.backup();
1866 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1867
1868 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1869 if (Global::IsOnline(mData->mMachineState))
1870 i_saveSettings(NULL, alock);
1871
1872 return S_OK;
1873}
1874
1875HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1876{
1877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1878
1879 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1880
1881 return S_OK;
1882}
1883
1884HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1885{
1886 HRESULT rc = S_OK;
1887
1888 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1889
1890 rc = i_checkStateDependency(MutableStateDep);
1891 if (FAILED(rc)) return rc;
1892
1893 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1894 {
1895 if (aCPUHotPlugEnabled)
1896 {
1897 i_setModified(IsModified_MachineData);
1898 mHWData.backup();
1899
1900 /* Add the amount of CPUs currently attached */
1901 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1902 mHWData->mCPUAttached[i] = true;
1903 }
1904 else
1905 {
1906 /*
1907 * We can disable hotplug only if the amount of maximum CPUs is equal
1908 * to the amount of attached CPUs
1909 */
1910 unsigned cCpusAttached = 0;
1911 unsigned iHighestId = 0;
1912
1913 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1914 {
1915 if (mHWData->mCPUAttached[i])
1916 {
1917 cCpusAttached++;
1918 iHighestId = i;
1919 }
1920 }
1921
1922 if ( (cCpusAttached != mHWData->mCPUCount)
1923 || (iHighestId >= mHWData->mCPUCount))
1924 return setError(E_INVALIDARG,
1925 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1926
1927 i_setModified(IsModified_MachineData);
1928 mHWData.backup();
1929 }
1930 }
1931
1932 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1933
1934 return rc;
1935}
1936
1937HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1938{
1939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1940
1941 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1942
1943 return S_OK;
1944}
1945
1946HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1947{
1948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1949
1950 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1951 if (SUCCEEDED(hrc))
1952 {
1953 i_setModified(IsModified_MachineData);
1954 mHWData.backup();
1955 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1956 }
1957 return hrc;
1958}
1959
1960HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1961{
1962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1963 aCPUProfile = mHWData->mCpuProfile;
1964 return S_OK;
1965}
1966
1967HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1968{
1969 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1970 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1971 if (SUCCEEDED(hrc))
1972 {
1973 i_setModified(IsModified_MachineData);
1974 mHWData.backup();
1975 /* Empty equals 'host'. */
1976 if (aCPUProfile.isNotEmpty())
1977 mHWData->mCpuProfile = aCPUProfile;
1978 else
1979 mHWData->mCpuProfile = "host";
1980 }
1981 return hrc;
1982}
1983
1984HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1985{
1986#ifdef VBOX_WITH_USB_CARDREADER
1987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1988
1989 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1990
1991 return S_OK;
1992#else
1993 NOREF(aEmulatedUSBCardReaderEnabled);
1994 return E_NOTIMPL;
1995#endif
1996}
1997
1998HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1999{
2000#ifdef VBOX_WITH_USB_CARDREADER
2001 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2002
2003 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2004 if (FAILED(rc)) return rc;
2005
2006 i_setModified(IsModified_MachineData);
2007 mHWData.backup();
2008 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
2009
2010 return S_OK;
2011#else
2012 NOREF(aEmulatedUSBCardReaderEnabled);
2013 return E_NOTIMPL;
2014#endif
2015}
2016
2017HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
2018{
2019 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2020
2021 *aHPETEnabled = mHWData->mHPETEnabled;
2022
2023 return S_OK;
2024}
2025
2026HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
2027{
2028 HRESULT rc = S_OK;
2029
2030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2031
2032 rc = i_checkStateDependency(MutableStateDep);
2033 if (FAILED(rc)) return rc;
2034
2035 i_setModified(IsModified_MachineData);
2036 mHWData.backup();
2037
2038 mHWData->mHPETEnabled = aHPETEnabled;
2039
2040 return rc;
2041}
2042
2043/** @todo this method should not be public */
2044HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2045{
2046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2047
2048 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2049
2050 return S_OK;
2051}
2052
2053/**
2054 * Set the memory balloon size.
2055 *
2056 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2057 * we have to make sure that we never call IGuest from here.
2058 */
2059HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2060{
2061 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2062#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2063 /* check limits */
2064 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2065 return setError(E_INVALIDARG,
2066 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2067 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2068
2069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2070
2071 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
2072 if (FAILED(rc)) return rc;
2073
2074 i_setModified(IsModified_MachineData);
2075 mHWData.backup();
2076 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2077
2078 return S_OK;
2079#else
2080 NOREF(aMemoryBalloonSize);
2081 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2082#endif
2083}
2084
2085HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2086{
2087 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2088
2089 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2090 return S_OK;
2091}
2092
2093HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2094{
2095#ifdef VBOX_WITH_PAGE_SHARING
2096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2097
2098 HRESULT rc = i_checkStateDependency(MutableStateDep);
2099 if (FAILED(rc)) return rc;
2100
2101 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2102 i_setModified(IsModified_MachineData);
2103 mHWData.backup();
2104 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2105 return S_OK;
2106#else
2107 NOREF(aPageFusionEnabled);
2108 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2109#endif
2110}
2111
2112HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2113{
2114 /* mBIOSSettings is constant during life time, no need to lock */
2115 aBIOSSettings = mBIOSSettings;
2116
2117 return S_OK;
2118}
2119
2120HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
2121{
2122 /* mTrustedPlatformModule is constant during life time, no need to lock */
2123 aTrustedPlatformModule = mTrustedPlatformModule;
2124
2125 return S_OK;
2126}
2127
2128HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
2129{
2130 /* mNvramStore is constant during life time, no need to lock */
2131 aNvramStore = mNvramStore;
2132
2133 return S_OK;
2134}
2135
2136HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
2137{
2138 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2139
2140 aRecordingSettings = mRecordingSettings;
2141
2142 return S_OK;
2143}
2144
2145HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
2146{
2147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2148
2149 aGraphicsAdapter = mGraphicsAdapter;
2150
2151 return S_OK;
2152}
2153
2154HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2155{
2156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2157
2158 switch (aProperty)
2159 {
2160 case CPUPropertyType_PAE:
2161 *aValue = mHWData->mPAEEnabled;
2162 break;
2163
2164 case CPUPropertyType_LongMode:
2165 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2166 *aValue = TRUE;
2167 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2168 *aValue = FALSE;
2169#if HC_ARCH_BITS == 64
2170 else
2171 *aValue = TRUE;
2172#else
2173 else
2174 {
2175 *aValue = FALSE;
2176
2177 ComObjPtr<GuestOSType> pGuestOSType;
2178 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2179 pGuestOSType);
2180 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
2181 {
2182 if (pGuestOSType->i_is64Bit())
2183 {
2184 ComObjPtr<Host> pHost = mParent->i_host();
2185 alock.release();
2186
2187 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2188 if (FAILED(hrc2))
2189 *aValue = FALSE;
2190 }
2191 }
2192 }
2193#endif
2194 break;
2195
2196 case CPUPropertyType_TripleFaultReset:
2197 *aValue = mHWData->mTripleFaultReset;
2198 break;
2199
2200 case CPUPropertyType_APIC:
2201 *aValue = mHWData->mAPIC;
2202 break;
2203
2204 case CPUPropertyType_X2APIC:
2205 *aValue = mHWData->mX2APIC;
2206 break;
2207
2208 case CPUPropertyType_IBPBOnVMExit:
2209 *aValue = mHWData->mIBPBOnVMExit;
2210 break;
2211
2212 case CPUPropertyType_IBPBOnVMEntry:
2213 *aValue = mHWData->mIBPBOnVMEntry;
2214 break;
2215
2216 case CPUPropertyType_SpecCtrl:
2217 *aValue = mHWData->mSpecCtrl;
2218 break;
2219
2220 case CPUPropertyType_SpecCtrlByHost:
2221 *aValue = mHWData->mSpecCtrlByHost;
2222 break;
2223
2224 case CPUPropertyType_HWVirt:
2225 *aValue = mHWData->mNestedHWVirt;
2226 break;
2227
2228 case CPUPropertyType_L1DFlushOnEMTScheduling:
2229 *aValue = mHWData->mL1DFlushOnSched;
2230 break;
2231
2232 case CPUPropertyType_L1DFlushOnVMEntry:
2233 *aValue = mHWData->mL1DFlushOnVMEntry;
2234 break;
2235
2236 case CPUPropertyType_MDSClearOnEMTScheduling:
2237 *aValue = mHWData->mMDSClearOnSched;
2238 break;
2239
2240 case CPUPropertyType_MDSClearOnVMEntry:
2241 *aValue = mHWData->mMDSClearOnVMEntry;
2242 break;
2243
2244 default:
2245 return E_INVALIDARG;
2246 }
2247 return S_OK;
2248}
2249
2250HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2251{
2252 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2253
2254 HRESULT rc = i_checkStateDependency(MutableStateDep);
2255 if (FAILED(rc)) return rc;
2256
2257 switch (aProperty)
2258 {
2259 case CPUPropertyType_PAE:
2260 i_setModified(IsModified_MachineData);
2261 mHWData.backup();
2262 mHWData->mPAEEnabled = !!aValue;
2263 break;
2264
2265 case CPUPropertyType_LongMode:
2266 i_setModified(IsModified_MachineData);
2267 mHWData.backup();
2268 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2269 break;
2270
2271 case CPUPropertyType_TripleFaultReset:
2272 i_setModified(IsModified_MachineData);
2273 mHWData.backup();
2274 mHWData->mTripleFaultReset = !!aValue;
2275 break;
2276
2277 case CPUPropertyType_APIC:
2278 if (mHWData->mX2APIC)
2279 aValue = TRUE;
2280 i_setModified(IsModified_MachineData);
2281 mHWData.backup();
2282 mHWData->mAPIC = !!aValue;
2283 break;
2284
2285 case CPUPropertyType_X2APIC:
2286 i_setModified(IsModified_MachineData);
2287 mHWData.backup();
2288 mHWData->mX2APIC = !!aValue;
2289 if (aValue)
2290 mHWData->mAPIC = !!aValue;
2291 break;
2292
2293 case CPUPropertyType_IBPBOnVMExit:
2294 i_setModified(IsModified_MachineData);
2295 mHWData.backup();
2296 mHWData->mIBPBOnVMExit = !!aValue;
2297 break;
2298
2299 case CPUPropertyType_IBPBOnVMEntry:
2300 i_setModified(IsModified_MachineData);
2301 mHWData.backup();
2302 mHWData->mIBPBOnVMEntry = !!aValue;
2303 break;
2304
2305 case CPUPropertyType_SpecCtrl:
2306 i_setModified(IsModified_MachineData);
2307 mHWData.backup();
2308 mHWData->mSpecCtrl = !!aValue;
2309 break;
2310
2311 case CPUPropertyType_SpecCtrlByHost:
2312 i_setModified(IsModified_MachineData);
2313 mHWData.backup();
2314 mHWData->mSpecCtrlByHost = !!aValue;
2315 break;
2316
2317 case CPUPropertyType_HWVirt:
2318 i_setModified(IsModified_MachineData);
2319 mHWData.backup();
2320 mHWData->mNestedHWVirt = !!aValue;
2321 break;
2322
2323 case CPUPropertyType_L1DFlushOnEMTScheduling:
2324 i_setModified(IsModified_MachineData);
2325 mHWData.backup();
2326 mHWData->mL1DFlushOnSched = !!aValue;
2327 break;
2328
2329 case CPUPropertyType_L1DFlushOnVMEntry:
2330 i_setModified(IsModified_MachineData);
2331 mHWData.backup();
2332 mHWData->mL1DFlushOnVMEntry = !!aValue;
2333 break;
2334
2335 case CPUPropertyType_MDSClearOnEMTScheduling:
2336 i_setModified(IsModified_MachineData);
2337 mHWData.backup();
2338 mHWData->mMDSClearOnSched = !!aValue;
2339 break;
2340
2341 case CPUPropertyType_MDSClearOnVMEntry:
2342 i_setModified(IsModified_MachineData);
2343 mHWData.backup();
2344 mHWData->mMDSClearOnVMEntry = !!aValue;
2345 break;
2346
2347 default:
2348 return E_INVALIDARG;
2349 }
2350 return S_OK;
2351}
2352
2353HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2354 ULONG *aValEcx, ULONG *aValEdx)
2355{
2356 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2357 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2358 {
2359 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2360 it != mHWData->mCpuIdLeafList.end();
2361 ++it)
2362 {
2363 if (aOrdinal == 0)
2364 {
2365 const settings::CpuIdLeaf &rLeaf= *it;
2366 *aIdx = rLeaf.idx;
2367 *aSubIdx = rLeaf.idxSub;
2368 *aValEax = rLeaf.uEax;
2369 *aValEbx = rLeaf.uEbx;
2370 *aValEcx = rLeaf.uEcx;
2371 *aValEdx = rLeaf.uEdx;
2372 return S_OK;
2373 }
2374 aOrdinal--;
2375 }
2376 }
2377 return E_INVALIDARG;
2378}
2379
2380HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2381{
2382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2383
2384 /*
2385 * Search the list.
2386 */
2387 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2388 {
2389 const settings::CpuIdLeaf &rLeaf= *it;
2390 if ( rLeaf.idx == aIdx
2391 && ( aSubIdx == UINT32_MAX
2392 || rLeaf.idxSub == aSubIdx) )
2393 {
2394 *aValEax = rLeaf.uEax;
2395 *aValEbx = rLeaf.uEbx;
2396 *aValEcx = rLeaf.uEcx;
2397 *aValEdx = rLeaf.uEdx;
2398 return S_OK;
2399 }
2400 }
2401
2402 return E_INVALIDARG;
2403}
2404
2405
2406HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2407{
2408 /*
2409 * Validate input before taking locks and checking state.
2410 */
2411 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2412 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2413 if ( aIdx >= UINT32_C(0x20)
2414 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2415 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2416 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2417
2418 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2419 HRESULT rc = i_checkStateDependency(MutableStateDep);
2420 if (FAILED(rc)) return rc;
2421
2422 /*
2423 * Impose a maximum number of leaves.
2424 */
2425 if (mHWData->mCpuIdLeafList.size() > 256)
2426 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2427
2428 /*
2429 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2430 */
2431 i_setModified(IsModified_MachineData);
2432 mHWData.backup();
2433
2434 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2435 {
2436 settings::CpuIdLeaf &rLeaf= *it;
2437 if ( rLeaf.idx == aIdx
2438 && ( aSubIdx == UINT32_MAX
2439 || rLeaf.idxSub == aSubIdx) )
2440 it = mHWData->mCpuIdLeafList.erase(it);
2441 else
2442 ++it;
2443 }
2444
2445 settings::CpuIdLeaf NewLeaf;
2446 NewLeaf.idx = aIdx;
2447 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2448 NewLeaf.uEax = aValEax;
2449 NewLeaf.uEbx = aValEbx;
2450 NewLeaf.uEcx = aValEcx;
2451 NewLeaf.uEdx = aValEdx;
2452 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2453 return S_OK;
2454}
2455
2456HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2457{
2458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2459
2460 HRESULT rc = i_checkStateDependency(MutableStateDep);
2461 if (FAILED(rc)) return rc;
2462
2463 /*
2464 * Do the removal.
2465 */
2466 bool fModified = mHWData.isBackedUp();
2467 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2468 {
2469 settings::CpuIdLeaf &rLeaf= *it;
2470 if ( rLeaf.idx == aIdx
2471 && ( aSubIdx == UINT32_MAX
2472 || rLeaf.idxSub == aSubIdx) )
2473 {
2474 if (!fModified)
2475 {
2476 fModified = true;
2477 i_setModified(IsModified_MachineData);
2478 mHWData.backup();
2479 // Start from the beginning, since mHWData.backup() creates
2480 // a new list, causing iterator mixup. This makes sure that
2481 // the settings are not unnecessarily marked as modified,
2482 // at the price of extra list walking.
2483 it = mHWData->mCpuIdLeafList.begin();
2484 }
2485 else
2486 it = mHWData->mCpuIdLeafList.erase(it);
2487 }
2488 else
2489 ++it;
2490 }
2491
2492 return S_OK;
2493}
2494
2495HRESULT Machine::removeAllCPUIDLeaves()
2496{
2497 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2498
2499 HRESULT rc = i_checkStateDependency(MutableStateDep);
2500 if (FAILED(rc)) return rc;
2501
2502 if (mHWData->mCpuIdLeafList.size() > 0)
2503 {
2504 i_setModified(IsModified_MachineData);
2505 mHWData.backup();
2506
2507 mHWData->mCpuIdLeafList.clear();
2508 }
2509
2510 return S_OK;
2511}
2512HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2513{
2514 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2515
2516 switch(aProperty)
2517 {
2518 case HWVirtExPropertyType_Enabled:
2519 *aValue = mHWData->mHWVirtExEnabled;
2520 break;
2521
2522 case HWVirtExPropertyType_VPID:
2523 *aValue = mHWData->mHWVirtExVPIDEnabled;
2524 break;
2525
2526 case HWVirtExPropertyType_NestedPaging:
2527 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2528 break;
2529
2530 case HWVirtExPropertyType_UnrestrictedExecution:
2531 *aValue = mHWData->mHWVirtExUXEnabled;
2532 break;
2533
2534 case HWVirtExPropertyType_LargePages:
2535 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2536 break;
2537
2538 case HWVirtExPropertyType_Force:
2539 *aValue = mHWData->mHWVirtExForceEnabled;
2540 break;
2541
2542 case HWVirtExPropertyType_UseNativeApi:
2543 *aValue = mHWData->mHWVirtExUseNativeApi;
2544 break;
2545
2546 case HWVirtExPropertyType_VirtVmsaveVmload:
2547 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2548 break;
2549
2550 default:
2551 return E_INVALIDARG;
2552 }
2553 return S_OK;
2554}
2555
2556HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2557{
2558 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2559
2560 HRESULT rc = i_checkStateDependency(MutableStateDep);
2561 if (FAILED(rc)) return rc;
2562
2563 switch (aProperty)
2564 {
2565 case HWVirtExPropertyType_Enabled:
2566 i_setModified(IsModified_MachineData);
2567 mHWData.backup();
2568 mHWData->mHWVirtExEnabled = !!aValue;
2569 break;
2570
2571 case HWVirtExPropertyType_VPID:
2572 i_setModified(IsModified_MachineData);
2573 mHWData.backup();
2574 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2575 break;
2576
2577 case HWVirtExPropertyType_NestedPaging:
2578 i_setModified(IsModified_MachineData);
2579 mHWData.backup();
2580 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2581 break;
2582
2583 case HWVirtExPropertyType_UnrestrictedExecution:
2584 i_setModified(IsModified_MachineData);
2585 mHWData.backup();
2586 mHWData->mHWVirtExUXEnabled = !!aValue;
2587 break;
2588
2589 case HWVirtExPropertyType_LargePages:
2590 i_setModified(IsModified_MachineData);
2591 mHWData.backup();
2592 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2593 break;
2594
2595 case HWVirtExPropertyType_Force:
2596 i_setModified(IsModified_MachineData);
2597 mHWData.backup();
2598 mHWData->mHWVirtExForceEnabled = !!aValue;
2599 break;
2600
2601 case HWVirtExPropertyType_UseNativeApi:
2602 i_setModified(IsModified_MachineData);
2603 mHWData.backup();
2604 mHWData->mHWVirtExUseNativeApi = !!aValue;
2605 break;
2606
2607 case HWVirtExPropertyType_VirtVmsaveVmload:
2608 i_setModified(IsModified_MachineData);
2609 mHWData.backup();
2610 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2611 break;
2612
2613 default:
2614 return E_INVALIDARG;
2615 }
2616
2617 return S_OK;
2618}
2619
2620HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2621{
2622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2623
2624 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2625
2626 return S_OK;
2627}
2628
2629HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2630{
2631 /** @todo (r=dmik):
2632 * 1. Allow to change the name of the snapshot folder containing snapshots
2633 * 2. Rename the folder on disk instead of just changing the property
2634 * value (to be smart and not to leave garbage). Note that it cannot be
2635 * done here because the change may be rolled back. Thus, the right
2636 * place is #saveSettings().
2637 */
2638
2639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2640
2641 HRESULT rc = i_checkStateDependency(MutableStateDep);
2642 if (FAILED(rc)) return rc;
2643
2644 if (!mData->mCurrentSnapshot.isNull())
2645 return setError(E_FAIL,
2646 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2647
2648 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2649
2650 if (strSnapshotFolder.isEmpty())
2651 strSnapshotFolder = "Snapshots";
2652 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2653 if (RT_FAILURE(vrc))
2654 return setErrorBoth(E_FAIL, vrc,
2655 tr("Invalid snapshot folder '%s' (%Rrc)"),
2656 strSnapshotFolder.c_str(), vrc);
2657
2658 i_setModified(IsModified_MachineData);
2659 mUserData.backup();
2660
2661 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2662
2663 return S_OK;
2664}
2665
2666HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2667{
2668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2669
2670 aMediumAttachments.resize(mMediumAttachments->size());
2671 size_t i = 0;
2672 for (MediumAttachmentList::const_iterator
2673 it = mMediumAttachments->begin();
2674 it != mMediumAttachments->end();
2675 ++it, ++i)
2676 aMediumAttachments[i] = *it;
2677
2678 return S_OK;
2679}
2680
2681HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2682{
2683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2684
2685 Assert(!!mVRDEServer);
2686
2687 aVRDEServer = mVRDEServer;
2688
2689 return S_OK;
2690}
2691
2692HRESULT Machine::getAudioSettings(ComPtr<IAudioSettings> &aAudioSettings)
2693{
2694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2695
2696 aAudioSettings = mAudioSettings;
2697
2698 return S_OK;
2699}
2700
2701HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2702{
2703#ifdef VBOX_WITH_VUSB
2704 clearError();
2705 MultiResult rc(S_OK);
2706
2707# ifdef VBOX_WITH_USB
2708 rc = mParent->i_host()->i_checkUSBProxyService();
2709 if (FAILED(rc)) return rc;
2710# endif
2711
2712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2713
2714 aUSBControllers.resize(mUSBControllers->size());
2715 size_t i = 0;
2716 for (USBControllerList::const_iterator
2717 it = mUSBControllers->begin();
2718 it != mUSBControllers->end();
2719 ++it, ++i)
2720 aUSBControllers[i] = *it;
2721
2722 return S_OK;
2723#else
2724 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2725 * extended error info to indicate that USB is simply not available
2726 * (w/o treating it as a failure), for example, as in OSE */
2727 NOREF(aUSBControllers);
2728 ReturnComNotImplemented();
2729#endif /* VBOX_WITH_VUSB */
2730}
2731
2732HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2733{
2734#ifdef VBOX_WITH_VUSB
2735 clearError();
2736 MultiResult rc(S_OK);
2737
2738# ifdef VBOX_WITH_USB
2739 rc = mParent->i_host()->i_checkUSBProxyService();
2740 if (FAILED(rc)) return rc;
2741# endif
2742
2743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2744
2745 aUSBDeviceFilters = mUSBDeviceFilters;
2746 return rc;
2747#else
2748 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2749 * extended error info to indicate that USB is simply not available
2750 * (w/o treating it as a failure), for example, as in OSE */
2751 NOREF(aUSBDeviceFilters);
2752 ReturnComNotImplemented();
2753#endif /* VBOX_WITH_VUSB */
2754}
2755
2756HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2757{
2758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2759
2760 aSettingsFilePath = mData->m_strConfigFileFull;
2761
2762 return S_OK;
2763}
2764
2765HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2766{
2767 RT_NOREF(aSettingsFilePath);
2768 ReturnComNotImplemented();
2769}
2770
2771HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2772{
2773 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2774
2775 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2776 if (FAILED(rc)) return rc;
2777
2778 if (!mData->pMachineConfigFile->fileExists())
2779 // this is a new machine, and no config file exists yet:
2780 *aSettingsModified = TRUE;
2781 else
2782 *aSettingsModified = (mData->flModifications != 0);
2783
2784 return S_OK;
2785}
2786
2787HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2788{
2789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2790
2791 *aSessionState = mData->mSession.mState;
2792
2793 return S_OK;
2794}
2795
2796HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2797{
2798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2799
2800 aSessionName = mData->mSession.mName;
2801
2802 return S_OK;
2803}
2804
2805HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2806{
2807 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2808
2809 *aSessionPID = mData->mSession.mPID;
2810
2811 return S_OK;
2812}
2813
2814HRESULT Machine::getState(MachineState_T *aState)
2815{
2816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2817
2818 *aState = mData->mMachineState;
2819 Assert(mData->mMachineState != MachineState_Null);
2820
2821 return S_OK;
2822}
2823
2824HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2825{
2826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2827
2828 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2829
2830 return S_OK;
2831}
2832
2833HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2834{
2835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2836
2837 aStateFilePath = mSSData->strStateFilePath;
2838
2839 return S_OK;
2840}
2841
2842HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2843{
2844 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2845
2846 i_getLogFolder(aLogFolder);
2847
2848 return S_OK;
2849}
2850
2851HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2852{
2853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2854
2855 aCurrentSnapshot = mData->mCurrentSnapshot;
2856
2857 return S_OK;
2858}
2859
2860HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2861{
2862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2863
2864 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2865 ? 0
2866 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2867
2868 return S_OK;
2869}
2870
2871HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2872{
2873 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2874
2875 /* Note: for machines with no snapshots, we always return FALSE
2876 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2877 * reasons :) */
2878
2879 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2880 ? FALSE
2881 : mData->mCurrentStateModified;
2882
2883 return S_OK;
2884}
2885
2886HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2887{
2888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2889
2890 aSharedFolders.resize(mHWData->mSharedFolders.size());
2891 size_t i = 0;
2892 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2893 it = mHWData->mSharedFolders.begin();
2894 it != mHWData->mSharedFolders.end();
2895 ++it, ++i)
2896 aSharedFolders[i] = *it;
2897
2898 return S_OK;
2899}
2900
2901HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2902{
2903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2904
2905 *aClipboardMode = mHWData->mClipboardMode;
2906
2907 return S_OK;
2908}
2909
2910HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2911{
2912 HRESULT rc = S_OK;
2913
2914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2915
2916 rc = i_checkStateDependency(MutableOrRunningStateDep);
2917 if (FAILED(rc)) return rc;
2918
2919 alock.release();
2920 rc = i_onClipboardModeChange(aClipboardMode);
2921 alock.acquire();
2922 if (FAILED(rc)) return rc;
2923
2924 i_setModified(IsModified_MachineData);
2925 mHWData.backup();
2926 mHWData->mClipboardMode = aClipboardMode;
2927
2928 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2929 if (Global::IsOnline(mData->mMachineState))
2930 i_saveSettings(NULL, alock);
2931
2932 return S_OK;
2933}
2934
2935HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2936{
2937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2938
2939 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2940
2941 return S_OK;
2942}
2943
2944HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2945{
2946 HRESULT rc = S_OK;
2947
2948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2949
2950 rc = i_checkStateDependency(MutableOrRunningStateDep);
2951 if (FAILED(rc)) return rc;
2952
2953 alock.release();
2954 rc = i_onClipboardFileTransferModeChange(aEnabled);
2955 alock.acquire();
2956 if (FAILED(rc)) return rc;
2957
2958 i_setModified(IsModified_MachineData);
2959 mHWData.backup();
2960 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2961
2962 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2963 if (Global::IsOnline(mData->mMachineState))
2964 i_saveSettings(NULL, alock);
2965
2966 return S_OK;
2967}
2968
2969HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2970{
2971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2972
2973 *aDnDMode = mHWData->mDnDMode;
2974
2975 return S_OK;
2976}
2977
2978HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2979{
2980 HRESULT rc = S_OK;
2981
2982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2983
2984 rc = i_checkStateDependency(MutableOrRunningStateDep);
2985 if (FAILED(rc)) return rc;
2986
2987 alock.release();
2988 rc = i_onDnDModeChange(aDnDMode);
2989
2990 alock.acquire();
2991 if (FAILED(rc)) return rc;
2992
2993 i_setModified(IsModified_MachineData);
2994 mHWData.backup();
2995 mHWData->mDnDMode = aDnDMode;
2996
2997 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2998 if (Global::IsOnline(mData->mMachineState))
2999 i_saveSettings(NULL, alock);
3000
3001 return S_OK;
3002}
3003
3004HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
3005{
3006 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3007
3008 aStorageControllers.resize(mStorageControllers->size());
3009 size_t i = 0;
3010 for (StorageControllerList::const_iterator
3011 it = mStorageControllers->begin();
3012 it != mStorageControllers->end();
3013 ++it, ++i)
3014 aStorageControllers[i] = *it;
3015
3016 return S_OK;
3017}
3018
3019HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
3020{
3021 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3022
3023 *aEnabled = mUserData->s.fTeleporterEnabled;
3024
3025 return S_OK;
3026}
3027
3028HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
3029{
3030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3031
3032 /* Only allow it to be set to true when PoweredOff or Aborted.
3033 (Clearing it is always permitted.) */
3034 if ( aTeleporterEnabled
3035 && mData->mRegistered
3036 && ( !i_isSessionMachine()
3037 || ( mData->mMachineState != MachineState_PoweredOff
3038 && mData->mMachineState != MachineState_Teleported
3039 && mData->mMachineState != MachineState_Aborted
3040 )
3041 )
3042 )
3043 return setError(VBOX_E_INVALID_VM_STATE,
3044 tr("The machine is not powered off (state is %s)"),
3045 Global::stringifyMachineState(mData->mMachineState));
3046
3047 i_setModified(IsModified_MachineData);
3048 mUserData.backup();
3049 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
3050
3051 return S_OK;
3052}
3053
3054HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
3055{
3056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3057
3058 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
3059
3060 return S_OK;
3061}
3062
3063HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
3064{
3065 if (aTeleporterPort >= _64K)
3066 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
3067
3068 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3069
3070 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3071 if (FAILED(rc)) return rc;
3072
3073 i_setModified(IsModified_MachineData);
3074 mUserData.backup();
3075 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3076
3077 return S_OK;
3078}
3079
3080HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3081{
3082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3083
3084 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3085
3086 return S_OK;
3087}
3088
3089HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3090{
3091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3092
3093 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3094 if (FAILED(rc)) return rc;
3095
3096 i_setModified(IsModified_MachineData);
3097 mUserData.backup();
3098 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3099
3100 return S_OK;
3101}
3102
3103HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3104{
3105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3106 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3107
3108 return S_OK;
3109}
3110
3111HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3112{
3113 /*
3114 * Hash the password first.
3115 */
3116 com::Utf8Str aT = aTeleporterPassword;
3117
3118 if (!aT.isEmpty())
3119 {
3120 if (VBoxIsPasswordHashed(&aT))
3121 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3122 VBoxHashPassword(&aT);
3123 }
3124
3125 /*
3126 * Do the update.
3127 */
3128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3129 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3130 if (SUCCEEDED(hrc))
3131 {
3132 i_setModified(IsModified_MachineData);
3133 mUserData.backup();
3134 mUserData->s.strTeleporterPassword = aT;
3135 }
3136
3137 return hrc;
3138}
3139
3140HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3141{
3142 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3143
3144 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3145
3146 return S_OK;
3147}
3148
3149HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3150{
3151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3152
3153 /* Only allow it to be set to true when PoweredOff or Aborted.
3154 (Clearing it is always permitted.) */
3155 if ( aRTCUseUTC
3156 && mData->mRegistered
3157 && ( !i_isSessionMachine()
3158 || ( mData->mMachineState != MachineState_PoweredOff
3159 && mData->mMachineState != MachineState_Teleported
3160 && mData->mMachineState != MachineState_Aborted
3161 )
3162 )
3163 )
3164 return setError(VBOX_E_INVALID_VM_STATE,
3165 tr("The machine is not powered off (state is %s)"),
3166 Global::stringifyMachineState(mData->mMachineState));
3167
3168 i_setModified(IsModified_MachineData);
3169 mUserData.backup();
3170 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3171
3172 return S_OK;
3173}
3174
3175HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3176{
3177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3178
3179 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3180
3181 return S_OK;
3182}
3183
3184HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3185{
3186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3187
3188 HRESULT rc = i_checkStateDependency(MutableStateDep);
3189 if (FAILED(rc)) return rc;
3190
3191 i_setModified(IsModified_MachineData);
3192 mHWData.backup();
3193 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3194
3195 return S_OK;
3196}
3197
3198HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3199{
3200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3201
3202 *aIOCacheSize = mHWData->mIOCacheSize;
3203
3204 return S_OK;
3205}
3206
3207HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3208{
3209 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3210
3211 HRESULT rc = i_checkStateDependency(MutableStateDep);
3212 if (FAILED(rc)) return rc;
3213
3214 i_setModified(IsModified_MachineData);
3215 mHWData.backup();
3216 mHWData->mIOCacheSize = aIOCacheSize;
3217
3218 return S_OK;
3219}
3220
3221HRESULT Machine::getStateKeyId(com::Utf8Str &aKeyId)
3222{
3223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3224
3225#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3226 aKeyId = mSSData->strStateKeyId;
3227#else
3228 aKeyId = com::Utf8Str::Empty;
3229#endif
3230
3231 return S_OK;
3232}
3233
3234HRESULT Machine::getStateKeyStore(com::Utf8Str &aKeyStore)
3235{
3236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3237
3238#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3239 aKeyStore = mSSData->strStateKeyStore;
3240#else
3241 aKeyStore = com::Utf8Str::Empty;
3242#endif
3243
3244 return S_OK;
3245}
3246
3247HRESULT Machine::getLogKeyId(com::Utf8Str &aKeyId)
3248{
3249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3250
3251#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3252 aKeyId = mData->mstrLogKeyId;
3253#else
3254 aKeyId = com::Utf8Str::Empty;
3255#endif
3256
3257 return S_OK;
3258}
3259
3260HRESULT Machine::getLogKeyStore(com::Utf8Str &aKeyStore)
3261{
3262 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3263
3264#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3265 aKeyStore = mData->mstrLogKeyStore;
3266#else
3267 aKeyStore = com::Utf8Str::Empty;
3268#endif
3269
3270 return S_OK;
3271}
3272
3273HRESULT Machine::getGuestDebugControl(ComPtr<IGuestDebugControl> &aGuestDebugControl)
3274{
3275 mGuestDebugControl.queryInterfaceTo(aGuestDebugControl.asOutParam());
3276
3277 return S_OK;
3278}
3279
3280
3281/**
3282 * @note Locks objects!
3283 */
3284HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3285 LockType_T aLockType)
3286{
3287 /* check the session state */
3288 SessionState_T state;
3289 HRESULT rc = aSession->COMGETTER(State)(&state);
3290 if (FAILED(rc)) return rc;
3291
3292 if (state != SessionState_Unlocked)
3293 return setError(VBOX_E_INVALID_OBJECT_STATE,
3294 tr("The given session is busy"));
3295
3296 // get the client's IInternalSessionControl interface
3297 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3298 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
3299 E_INVALIDARG);
3300
3301 // session name (only used in some code paths)
3302 Utf8Str strSessionName;
3303
3304 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3305
3306 if (!mData->mRegistered)
3307 return setError(E_UNEXPECTED,
3308 tr("The machine '%s' is not registered"),
3309 mUserData->s.strName.c_str());
3310
3311 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3312
3313 SessionState_T oldState = mData->mSession.mState;
3314 /* Hack: in case the session is closing and there is a progress object
3315 * which allows waiting for the session to be closed, take the opportunity
3316 * and do a limited wait (max. 1 second). This helps a lot when the system
3317 * is busy and thus session closing can take a little while. */
3318 if ( mData->mSession.mState == SessionState_Unlocking
3319 && mData->mSession.mProgress)
3320 {
3321 alock.release();
3322 mData->mSession.mProgress->WaitForCompletion(1000);
3323 alock.acquire();
3324 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3325 }
3326
3327 // try again now
3328 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3329 // (i.e. session machine exists)
3330 && (aLockType == LockType_Shared) // caller wants a shared link to the
3331 // existing session that holds the write lock:
3332 )
3333 {
3334 // OK, share the session... we are now dealing with three processes:
3335 // 1) VBoxSVC (where this code runs);
3336 // 2) process C: the caller's client process (who wants a shared session);
3337 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3338
3339 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3340 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3341 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3342 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3343 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3344
3345 /*
3346 * Release the lock before calling the client process. It's safe here
3347 * since the only thing to do after we get the lock again is to add
3348 * the remote control to the list (which doesn't directly influence
3349 * anything).
3350 */
3351 alock.release();
3352
3353 // get the console of the session holding the write lock (this is a remote call)
3354 ComPtr<IConsole> pConsoleW;
3355 if (mData->mSession.mLockType == LockType_VM)
3356 {
3357 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3358 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3359 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3360 if (FAILED(rc))
3361 // the failure may occur w/o any error info (from RPC), so provide one
3362 return setError(VBOX_E_VM_ERROR,
3363 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3364 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3365 }
3366
3367 // share the session machine and W's console with the caller's session
3368 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3369 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3370 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3371
3372 if (FAILED(rc))
3373 // the failure may occur w/o any error info (from RPC), so provide one
3374 return setError(VBOX_E_VM_ERROR,
3375 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3376 alock.acquire();
3377
3378 // need to revalidate the state after acquiring the lock again
3379 if (mData->mSession.mState != SessionState_Locked)
3380 {
3381 pSessionControl->Uninitialize();
3382 return setError(VBOX_E_INVALID_SESSION_STATE,
3383 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3384 mUserData->s.strName.c_str());
3385 }
3386
3387 // add the caller's session to the list
3388 mData->mSession.mRemoteControls.push_back(pSessionControl);
3389 }
3390 else if ( mData->mSession.mState == SessionState_Locked
3391 || mData->mSession.mState == SessionState_Unlocking
3392 )
3393 {
3394 // sharing not permitted, or machine still unlocking:
3395 return setError(VBOX_E_INVALID_OBJECT_STATE,
3396 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3397 mUserData->s.strName.c_str());
3398 }
3399 else
3400 {
3401 // machine is not locked: then write-lock the machine (create the session machine)
3402
3403 // must not be busy
3404 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3405
3406 // get the caller's session PID
3407 RTPROCESS pid = NIL_RTPROCESS;
3408 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3409 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3410 Assert(pid != NIL_RTPROCESS);
3411
3412 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3413
3414 if (fLaunchingVMProcess)
3415 {
3416 if (mData->mSession.mPID == NIL_RTPROCESS)
3417 {
3418 // two or more clients racing for a lock, the one which set the
3419 // session state to Spawning will win, the others will get an
3420 // error as we can't decide here if waiting a little would help
3421 // (only for shared locks this would avoid an error)
3422 return setError(VBOX_E_INVALID_OBJECT_STATE,
3423 tr("The machine '%s' already has a lock request pending"),
3424 mUserData->s.strName.c_str());
3425 }
3426
3427 // this machine is awaiting for a spawning session to be opened:
3428 // then the calling process must be the one that got started by
3429 // LaunchVMProcess()
3430
3431 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3432 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3433
3434#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3435 /* Hardened windows builds spawns three processes when a VM is
3436 launched, the 3rd one is the one that will end up here. */
3437 RTPROCESS pidParent;
3438 int vrc = RTProcQueryParent(pid, &pidParent);
3439 if (RT_SUCCESS(vrc))
3440 vrc = RTProcQueryParent(pidParent, &pidParent);
3441 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3442 || vrc == VERR_ACCESS_DENIED)
3443 {
3444 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3445 mData->mSession.mPID = pid;
3446 }
3447#endif
3448
3449 if (mData->mSession.mPID != pid)
3450 return setError(E_ACCESSDENIED,
3451 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3452 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3453 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3454 }
3455
3456 // create the mutable SessionMachine from the current machine
3457 ComObjPtr<SessionMachine> sessionMachine;
3458 sessionMachine.createObject();
3459 rc = sessionMachine->init(this);
3460 AssertComRC(rc);
3461
3462 /* NOTE: doing return from this function after this point but
3463 * before the end is forbidden since it may call SessionMachine::uninit()
3464 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3465 * lock while still holding the Machine lock in alock so that a deadlock
3466 * is possible due to the wrong lock order. */
3467
3468 if (SUCCEEDED(rc))
3469 {
3470 /*
3471 * Set the session state to Spawning to protect against subsequent
3472 * attempts to open a session and to unregister the machine after
3473 * we release the lock.
3474 */
3475 SessionState_T origState = mData->mSession.mState;
3476 mData->mSession.mState = SessionState_Spawning;
3477
3478#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3479 /* Get the client token ID to be passed to the client process */
3480 Utf8Str strTokenId;
3481 sessionMachine->i_getTokenId(strTokenId);
3482 Assert(!strTokenId.isEmpty());
3483#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3484 /* Get the client token to be passed to the client process */
3485 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3486 /* The token is now "owned" by pToken, fix refcount */
3487 if (!pToken.isNull())
3488 pToken->Release();
3489#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3490
3491 /*
3492 * Release the lock before calling the client process -- it will call
3493 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3494 * because the state is Spawning, so that LaunchVMProcess() and
3495 * LockMachine() calls will fail. This method, called before we
3496 * acquire the lock again, will fail because of the wrong PID.
3497 *
3498 * Note that mData->mSession.mRemoteControls accessed outside
3499 * the lock may not be modified when state is Spawning, so it's safe.
3500 */
3501 alock.release();
3502
3503 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3504#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3505 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3506#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3507 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3508 /* Now the token is owned by the client process. */
3509 pToken.setNull();
3510#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3511 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3512
3513 /* The failure may occur w/o any error info (from RPC), so provide one */
3514 if (FAILED(rc))
3515 setError(VBOX_E_VM_ERROR,
3516 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3517
3518 // get session name, either to remember or to compare against
3519 // the already known session name.
3520 {
3521 Bstr bstrSessionName;
3522 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3523 if (SUCCEEDED(rc2))
3524 strSessionName = bstrSessionName;
3525 }
3526
3527 if ( SUCCEEDED(rc)
3528 && fLaunchingVMProcess
3529 )
3530 {
3531 /* complete the remote session initialization */
3532
3533 /* get the console from the direct session */
3534 ComPtr<IConsole> console;
3535 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3536 ComAssertComRC(rc);
3537
3538 if (SUCCEEDED(rc) && !console)
3539 {
3540 ComAssert(!!console);
3541 rc = E_FAIL;
3542 }
3543
3544 /* assign machine & console to the remote session */
3545 if (SUCCEEDED(rc))
3546 {
3547 /*
3548 * after LaunchVMProcess(), the first and the only
3549 * entry in remoteControls is that remote session
3550 */
3551 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3552 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3553 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3554
3555 /* The failure may occur w/o any error info (from RPC), so provide one */
3556 if (FAILED(rc))
3557 setError(VBOX_E_VM_ERROR,
3558 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3559 }
3560
3561 if (FAILED(rc))
3562 pSessionControl->Uninitialize();
3563 }
3564
3565 /* acquire the lock again */
3566 alock.acquire();
3567
3568 /* Restore the session state */
3569 mData->mSession.mState = origState;
3570 }
3571
3572 // finalize spawning anyway (this is why we don't return on errors above)
3573 if (fLaunchingVMProcess)
3574 {
3575 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3576 /* Note that the progress object is finalized later */
3577 /** @todo Consider checking mData->mSession.mProgress for cancellation
3578 * around here. */
3579
3580 /* We don't reset mSession.mPID here because it is necessary for
3581 * SessionMachine::uninit() to reap the child process later. */
3582
3583 if (FAILED(rc))
3584 {
3585 /* Close the remote session, remove the remote control from the list
3586 * and reset session state to Closed (@note keep the code in sync
3587 * with the relevant part in checkForSpawnFailure()). */
3588
3589 Assert(mData->mSession.mRemoteControls.size() == 1);
3590 if (mData->mSession.mRemoteControls.size() == 1)
3591 {
3592 ErrorInfoKeeper eik;
3593 mData->mSession.mRemoteControls.front()->Uninitialize();
3594 }
3595
3596 mData->mSession.mRemoteControls.clear();
3597 mData->mSession.mState = SessionState_Unlocked;
3598 }
3599 }
3600 else
3601 {
3602 /* memorize PID of the directly opened session */
3603 if (SUCCEEDED(rc))
3604 mData->mSession.mPID = pid;
3605 }
3606
3607 if (SUCCEEDED(rc))
3608 {
3609 mData->mSession.mLockType = aLockType;
3610 /* memorize the direct session control and cache IUnknown for it */
3611 mData->mSession.mDirectControl = pSessionControl;
3612 mData->mSession.mState = SessionState_Locked;
3613 if (!fLaunchingVMProcess)
3614 mData->mSession.mName = strSessionName;
3615 /* associate the SessionMachine with this Machine */
3616 mData->mSession.mMachine = sessionMachine;
3617
3618 /* request an IUnknown pointer early from the remote party for later
3619 * identity checks (it will be internally cached within mDirectControl
3620 * at least on XPCOM) */
3621 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3622 NOREF(unk);
3623
3624#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3625 if (aLockType == LockType_VM)
3626 {
3627 /* get the console from the direct session */
3628 ComPtr<IConsole> console;
3629 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3630 ComAssertComRC(rc);
3631 /* send passswords to console */
3632 for (SecretKeyStore::SecretKeyMap::iterator it = mData->mpKeyStore->begin();
3633 it != mData->mpKeyStore->end();
3634 ++it)
3635 {
3636 SecretKey *pKey = it->second;
3637 pKey->retain();
3638 console->AddEncryptionPassword(Bstr(it->first).raw(),
3639 Bstr((const char*)pKey->getKeyBuffer()).raw(),
3640 TRUE);
3641 pKey->release();
3642 }
3643
3644 }
3645#endif
3646 }
3647
3648 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3649 * would break the lock order */
3650 alock.release();
3651
3652 /* uninitialize the created session machine on failure */
3653 if (FAILED(rc))
3654 sessionMachine->uninit();
3655 }
3656
3657 if (SUCCEEDED(rc))
3658 {
3659 /*
3660 * tell the client watcher thread to update the set of
3661 * machines that have open sessions
3662 */
3663 mParent->i_updateClientWatcher();
3664
3665 if (oldState != SessionState_Locked)
3666 /* fire an event */
3667 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3668 }
3669
3670 return rc;
3671}
3672
3673/**
3674 * @note Locks objects!
3675 */
3676HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3677 const com::Utf8Str &aName,
3678 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3679 ComPtr<IProgress> &aProgress)
3680{
3681 Utf8Str strFrontend(aName);
3682 /* "emergencystop" doesn't need the session, so skip the checks/interface
3683 * retrieval. This code doesn't quite fit in here, but introducing a
3684 * special API method would be even more effort, and would require explicit
3685 * support by every API client. It's better to hide the feature a bit. */
3686 if (strFrontend != "emergencystop")
3687 CheckComArgNotNull(aSession);
3688
3689 HRESULT rc = S_OK;
3690 if (strFrontend.isEmpty())
3691 {
3692 Bstr bstrFrontend;
3693 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3694 if (FAILED(rc))
3695 return rc;
3696 strFrontend = bstrFrontend;
3697 if (strFrontend.isEmpty())
3698 {
3699 ComPtr<ISystemProperties> systemProperties;
3700 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3701 if (FAILED(rc))
3702 return rc;
3703 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3704 if (FAILED(rc))
3705 return rc;
3706 strFrontend = bstrFrontend;
3707 }
3708 /* paranoia - emergencystop is not a valid default */
3709 if (strFrontend == "emergencystop")
3710 strFrontend = Utf8Str::Empty;
3711 }
3712 /* default frontend: Qt GUI */
3713 if (strFrontend.isEmpty())
3714 strFrontend = "GUI/Qt";
3715
3716 if (strFrontend != "emergencystop")
3717 {
3718 /* check the session state */
3719 SessionState_T state;
3720 rc = aSession->COMGETTER(State)(&state);
3721 if (FAILED(rc))
3722 return rc;
3723
3724 if (state != SessionState_Unlocked)
3725 return setError(VBOX_E_INVALID_OBJECT_STATE,
3726 tr("The given session is busy"));
3727
3728 /* get the IInternalSessionControl interface */
3729 ComPtr<IInternalSessionControl> control(aSession);
3730 ComAssertMsgRet(!control.isNull(),
3731 ("No IInternalSessionControl interface"),
3732 E_INVALIDARG);
3733
3734 /* get the teleporter enable state for the progress object init. */
3735 BOOL fTeleporterEnabled;
3736 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3737 if (FAILED(rc))
3738 return rc;
3739
3740 /* create a progress object */
3741 ComObjPtr<ProgressProxy> progress;
3742 progress.createObject();
3743 rc = progress->init(mParent,
3744 static_cast<IMachine*>(this),
3745 Bstr(tr("Starting VM")).raw(),
3746 TRUE /* aCancelable */,
3747 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3748 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3749 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3750 2 /* uFirstOperationWeight */,
3751 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3752
3753 if (SUCCEEDED(rc))
3754 {
3755 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3756 if (SUCCEEDED(rc))
3757 {
3758 aProgress = progress;
3759
3760 /* signal the client watcher thread */
3761 mParent->i_updateClientWatcher();
3762
3763 /* fire an event */
3764 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3765 }
3766 }
3767 }
3768 else
3769 {
3770 /* no progress object - either instant success or failure */
3771 aProgress = NULL;
3772
3773 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3774
3775 if (mData->mSession.mState != SessionState_Locked)
3776 return setError(VBOX_E_INVALID_OBJECT_STATE,
3777 tr("The machine '%s' is not locked by a session"),
3778 mUserData->s.strName.c_str());
3779
3780 /* must have a VM process associated - do not kill normal API clients
3781 * with an open session */
3782 if (!Global::IsOnline(mData->mMachineState))
3783 return setError(VBOX_E_INVALID_OBJECT_STATE,
3784 tr("The machine '%s' does not have a VM process"),
3785 mUserData->s.strName.c_str());
3786
3787 /* forcibly terminate the VM process */
3788 if (mData->mSession.mPID != NIL_RTPROCESS)
3789 RTProcTerminate(mData->mSession.mPID);
3790
3791 /* signal the client watcher thread, as most likely the client has
3792 * been terminated */
3793 mParent->i_updateClientWatcher();
3794 }
3795
3796 return rc;
3797}
3798
3799HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3800{
3801 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3802 return setError(E_INVALIDARG,
3803 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3804 aPosition, SchemaDefs::MaxBootPosition);
3805
3806 if (aDevice == DeviceType_USB)
3807 return setError(E_NOTIMPL,
3808 tr("Booting from USB device is currently not supported"));
3809
3810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3811
3812 HRESULT rc = i_checkStateDependency(MutableStateDep);
3813 if (FAILED(rc)) return rc;
3814
3815 i_setModified(IsModified_MachineData);
3816 mHWData.backup();
3817 mHWData->mBootOrder[aPosition - 1] = aDevice;
3818
3819 return S_OK;
3820}
3821
3822HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3823{
3824 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3825 return setError(E_INVALIDARG,
3826 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3827 aPosition, SchemaDefs::MaxBootPosition);
3828
3829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3830
3831 *aDevice = mHWData->mBootOrder[aPosition - 1];
3832
3833 return S_OK;
3834}
3835
3836HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3837 LONG aControllerPort,
3838 LONG aDevice,
3839 DeviceType_T aType,
3840 const ComPtr<IMedium> &aMedium)
3841{
3842 IMedium *aM = aMedium;
3843 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3844 aName.c_str(), aControllerPort, aDevice, aType, aM));
3845
3846 // request the host lock first, since might be calling Host methods for getting host drives;
3847 // next, protect the media tree all the while we're in here, as well as our member variables
3848 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3849 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3850
3851 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3852 if (FAILED(rc)) return rc;
3853
3854 /// @todo NEWMEDIA implicit machine registration
3855 if (!mData->mRegistered)
3856 return setError(VBOX_E_INVALID_OBJECT_STATE,
3857 tr("Cannot attach storage devices to an unregistered machine"));
3858
3859 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3860
3861 /* Check for an existing controller. */
3862 ComObjPtr<StorageController> ctl;
3863 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3864 if (FAILED(rc)) return rc;
3865
3866 StorageControllerType_T ctrlType;
3867 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3868 if (FAILED(rc))
3869 return setError(E_FAIL,
3870 tr("Could not get type of controller '%s'"),
3871 aName.c_str());
3872
3873 bool fSilent = false;
3874 Utf8Str strReconfig;
3875
3876 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3877 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3878 if ( mData->mMachineState == MachineState_Paused
3879 && strReconfig == "1")
3880 fSilent = true;
3881
3882 /* Check that the controller can do hot-plugging if we attach the device while the VM is running. */
3883 bool fHotplug = false;
3884 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3885 fHotplug = true;
3886
3887 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3888 return setError(VBOX_E_INVALID_VM_STATE,
3889 tr("Controller '%s' does not support hot-plugging"),
3890 aName.c_str());
3891
3892 /* Attaching a USB device when a VM is powered off should default to being marked as hot-pluggable */
3893 if (!fHotplug && !Global::IsOnlineOrTransient(mData->mMachineState) && ctrlType == StorageControllerType_USB)
3894 fHotplug = true;
3895
3896 // check that the port and device are not out of range
3897 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3898 if (FAILED(rc)) return rc;
3899
3900 /* check if the device slot is already busy */
3901 MediumAttachment *pAttachTemp;
3902 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3903 aName,
3904 aControllerPort,
3905 aDevice)))
3906 {
3907 Medium *pMedium = pAttachTemp->i_getMedium();
3908 if (pMedium)
3909 {
3910 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3911 return setError(VBOX_E_OBJECT_IN_USE,
3912 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3913 pMedium->i_getLocationFull().c_str(),
3914 aControllerPort,
3915 aDevice,
3916 aName.c_str());
3917 }
3918 else
3919 return setError(VBOX_E_OBJECT_IN_USE,
3920 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3921 aControllerPort, aDevice, aName.c_str());
3922 }
3923
3924 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3925 if (aMedium && medium.isNull())
3926 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3927
3928 AutoCaller mediumCaller(medium);
3929 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3930
3931 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3932
3933 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3934 && !medium.isNull()
3935 && ( medium->i_getType() != MediumType_Readonly
3936 || medium->i_getDeviceType() != DeviceType_DVD)
3937 )
3938 return setError(VBOX_E_OBJECT_IN_USE,
3939 tr("Medium '%s' is already attached to this virtual machine"),
3940 medium->i_getLocationFull().c_str());
3941
3942 if (!medium.isNull())
3943 {
3944 MediumType_T mtype = medium->i_getType();
3945 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3946 // For DVDs it's not written to the config file, so needs no global config
3947 // version bump. For floppies it's a new attribute "type", which is ignored
3948 // by older VirtualBox version, so needs no global config version bump either.
3949 // For hard disks this type is not accepted.
3950 if (mtype == MediumType_MultiAttach)
3951 {
3952 // This type is new with VirtualBox 4.0 and therefore requires settings
3953 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3954 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3955 // two reasons: The medium type is a property of the media registry tree, which
3956 // can reside in the global config file (for pre-4.0 media); we would therefore
3957 // possibly need to bump the global config version. We don't want to do that though
3958 // because that might make downgrading to pre-4.0 impossible.
3959 // As a result, we can only use these two new types if the medium is NOT in the
3960 // global registry:
3961 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3962 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3963 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3964 )
3965 return setError(VBOX_E_INVALID_OBJECT_STATE,
3966 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3967 "to machines that were created with VirtualBox 4.0 or later"),
3968 medium->i_getLocationFull().c_str());
3969 }
3970 }
3971
3972 bool fIndirect = false;
3973 if (!medium.isNull())
3974 fIndirect = medium->i_isReadOnly();
3975 bool associate = true;
3976
3977 do
3978 {
3979 if ( aType == DeviceType_HardDisk
3980 && mMediumAttachments.isBackedUp())
3981 {
3982 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3983
3984 /* check if the medium was attached to the VM before we started
3985 * changing attachments in which case the attachment just needs to
3986 * be restored */
3987 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3988 {
3989 AssertReturn(!fIndirect, E_FAIL);
3990
3991 /* see if it's the same bus/channel/device */
3992 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3993 {
3994 /* the simplest case: restore the whole attachment
3995 * and return, nothing else to do */
3996 mMediumAttachments->push_back(pAttachTemp);
3997
3998 /* Reattach the medium to the VM. */
3999 if (fHotplug || fSilent)
4000 {
4001 mediumLock.release();
4002 treeLock.release();
4003 alock.release();
4004
4005 MediumLockList *pMediumLockList(new MediumLockList());
4006
4007 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4008 medium /* pToLockWrite */,
4009 false /* fMediumLockWriteAll */,
4010 NULL,
4011 *pMediumLockList);
4012 alock.acquire();
4013 if (FAILED(rc))
4014 delete pMediumLockList;
4015 else
4016 {
4017 mData->mSession.mLockedMedia.Unlock();
4018 alock.release();
4019 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4020 mData->mSession.mLockedMedia.Lock();
4021 alock.acquire();
4022 }
4023 alock.release();
4024
4025 if (SUCCEEDED(rc))
4026 {
4027 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4028 /* Remove lock list in case of error. */
4029 if (FAILED(rc))
4030 {
4031 mData->mSession.mLockedMedia.Unlock();
4032 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4033 mData->mSession.mLockedMedia.Lock();
4034 }
4035 }
4036 }
4037
4038 return S_OK;
4039 }
4040
4041 /* bus/channel/device differ; we need a new attachment object,
4042 * but don't try to associate it again */
4043 associate = false;
4044 break;
4045 }
4046 }
4047
4048 /* go further only if the attachment is to be indirect */
4049 if (!fIndirect)
4050 break;
4051
4052 /* perform the so called smart attachment logic for indirect
4053 * attachments. Note that smart attachment is only applicable to base
4054 * hard disks. */
4055
4056 if (medium->i_getParent().isNull())
4057 {
4058 /* first, investigate the backup copy of the current hard disk
4059 * attachments to make it possible to re-attach existing diffs to
4060 * another device slot w/o losing their contents */
4061 if (mMediumAttachments.isBackedUp())
4062 {
4063 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4064
4065 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4066 uint32_t foundLevel = 0;
4067
4068 for (MediumAttachmentList::const_iterator
4069 it = oldAtts.begin();
4070 it != oldAtts.end();
4071 ++it)
4072 {
4073 uint32_t level = 0;
4074 MediumAttachment *pAttach = *it;
4075 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4076 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4077 if (pMedium.isNull())
4078 continue;
4079
4080 if (pMedium->i_getBase(&level) == medium)
4081 {
4082 /* skip the hard disk if its currently attached (we
4083 * cannot attach the same hard disk twice) */
4084 if (i_findAttachment(*mMediumAttachments.data(),
4085 pMedium))
4086 continue;
4087
4088 /* matched device, channel and bus (i.e. attached to the
4089 * same place) will win and immediately stop the search;
4090 * otherwise the attachment that has the youngest
4091 * descendant of medium will be used
4092 */
4093 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4094 {
4095 /* the simplest case: restore the whole attachment
4096 * and return, nothing else to do */
4097 mMediumAttachments->push_back(*it);
4098
4099 /* Reattach the medium to the VM. */
4100 if (fHotplug || fSilent)
4101 {
4102 mediumLock.release();
4103 treeLock.release();
4104 alock.release();
4105
4106 MediumLockList *pMediumLockList(new MediumLockList());
4107
4108 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4109 medium /* pToLockWrite */,
4110 false /* fMediumLockWriteAll */,
4111 NULL,
4112 *pMediumLockList);
4113 alock.acquire();
4114 if (FAILED(rc))
4115 delete pMediumLockList;
4116 else
4117 {
4118 mData->mSession.mLockedMedia.Unlock();
4119 alock.release();
4120 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4121 mData->mSession.mLockedMedia.Lock();
4122 alock.acquire();
4123 }
4124 alock.release();
4125
4126 if (SUCCEEDED(rc))
4127 {
4128 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4129 /* Remove lock list in case of error. */
4130 if (FAILED(rc))
4131 {
4132 mData->mSession.mLockedMedia.Unlock();
4133 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4134 mData->mSession.mLockedMedia.Lock();
4135 }
4136 }
4137 }
4138
4139 return S_OK;
4140 }
4141 else if ( foundIt == oldAtts.end()
4142 || level > foundLevel /* prefer younger */
4143 )
4144 {
4145 foundIt = it;
4146 foundLevel = level;
4147 }
4148 }
4149 }
4150
4151 if (foundIt != oldAtts.end())
4152 {
4153 /* use the previously attached hard disk */
4154 medium = (*foundIt)->i_getMedium();
4155 mediumCaller.attach(medium);
4156 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4157 mediumLock.attach(medium);
4158 /* not implicit, doesn't require association with this VM */
4159 fIndirect = false;
4160 associate = false;
4161 /* go right to the MediumAttachment creation */
4162 break;
4163 }
4164 }
4165
4166 /* must give up the medium lock and medium tree lock as below we
4167 * go over snapshots, which needs a lock with higher lock order. */
4168 mediumLock.release();
4169 treeLock.release();
4170
4171 /* then, search through snapshots for the best diff in the given
4172 * hard disk's chain to base the new diff on */
4173
4174 ComObjPtr<Medium> base;
4175 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4176 while (snap)
4177 {
4178 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4179
4180 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4181
4182 MediumAttachment *pAttachFound = NULL;
4183 uint32_t foundLevel = 0;
4184
4185 for (MediumAttachmentList::const_iterator
4186 it = snapAtts.begin();
4187 it != snapAtts.end();
4188 ++it)
4189 {
4190 MediumAttachment *pAttach = *it;
4191 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4192 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4193 if (pMedium.isNull())
4194 continue;
4195
4196 uint32_t level = 0;
4197 if (pMedium->i_getBase(&level) == medium)
4198 {
4199 /* matched device, channel and bus (i.e. attached to the
4200 * same place) will win and immediately stop the search;
4201 * otherwise the attachment that has the youngest
4202 * descendant of medium will be used
4203 */
4204 if ( pAttach->i_getDevice() == aDevice
4205 && pAttach->i_getPort() == aControllerPort
4206 && pAttach->i_getControllerName() == aName
4207 )
4208 {
4209 pAttachFound = pAttach;
4210 break;
4211 }
4212 else if ( !pAttachFound
4213 || level > foundLevel /* prefer younger */
4214 )
4215 {
4216 pAttachFound = pAttach;
4217 foundLevel = level;
4218 }
4219 }
4220 }
4221
4222 if (pAttachFound)
4223 {
4224 base = pAttachFound->i_getMedium();
4225 break;
4226 }
4227
4228 snap = snap->i_getParent();
4229 }
4230
4231 /* re-lock medium tree and the medium, as we need it below */
4232 treeLock.acquire();
4233 mediumLock.acquire();
4234
4235 /* found a suitable diff, use it as a base */
4236 if (!base.isNull())
4237 {
4238 medium = base;
4239 mediumCaller.attach(medium);
4240 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4241 mediumLock.attach(medium);
4242 }
4243 }
4244
4245 Utf8Str strFullSnapshotFolder;
4246 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4247
4248 ComObjPtr<Medium> diff;
4249 diff.createObject();
4250 // store this diff in the same registry as the parent
4251 Guid uuidRegistryParent;
4252 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4253 {
4254 // parent image has no registry: this can happen if we're attaching a new immutable
4255 // image that has not yet been attached (medium then points to the base and we're
4256 // creating the diff image for the immutable, and the parent is not yet registered);
4257 // put the parent in the machine registry then
4258 mediumLock.release();
4259 treeLock.release();
4260 alock.release();
4261 i_addMediumToRegistry(medium);
4262 alock.acquire();
4263 treeLock.acquire();
4264 mediumLock.acquire();
4265 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4266 }
4267 rc = diff->init(mParent,
4268 medium->i_getPreferredDiffFormat(),
4269 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4270 uuidRegistryParent,
4271 DeviceType_HardDisk);
4272 if (FAILED(rc)) return rc;
4273
4274 /* Apply the normal locking logic to the entire chain. */
4275 MediumLockList *pMediumLockList(new MediumLockList());
4276 mediumLock.release();
4277 treeLock.release();
4278 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4279 diff /* pToLockWrite */,
4280 false /* fMediumLockWriteAll */,
4281 medium,
4282 *pMediumLockList);
4283 treeLock.acquire();
4284 mediumLock.acquire();
4285 if (SUCCEEDED(rc))
4286 {
4287 mediumLock.release();
4288 treeLock.release();
4289 rc = pMediumLockList->Lock();
4290 treeLock.acquire();
4291 mediumLock.acquire();
4292 if (FAILED(rc))
4293 setError(rc,
4294 tr("Could not lock medium when creating diff '%s'"),
4295 diff->i_getLocationFull().c_str());
4296 else
4297 {
4298 /* will release the lock before the potentially lengthy
4299 * operation, so protect with the special state */
4300 MachineState_T oldState = mData->mMachineState;
4301 i_setMachineState(MachineState_SettingUp);
4302
4303 mediumLock.release();
4304 treeLock.release();
4305 alock.release();
4306
4307 rc = medium->i_createDiffStorage(diff,
4308 medium->i_getPreferredDiffVariant(),
4309 pMediumLockList,
4310 NULL /* aProgress */,
4311 true /* aWait */,
4312 false /* aNotify */);
4313
4314 alock.acquire();
4315 treeLock.acquire();
4316 mediumLock.acquire();
4317
4318 i_setMachineState(oldState);
4319 }
4320 }
4321
4322 /* Unlock the media and free the associated memory. */
4323 delete pMediumLockList;
4324
4325 if (FAILED(rc)) return rc;
4326
4327 /* use the created diff for the actual attachment */
4328 medium = diff;
4329 mediumCaller.attach(medium);
4330 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4331 mediumLock.attach(medium);
4332 }
4333 while (0);
4334
4335 ComObjPtr<MediumAttachment> attachment;
4336 attachment.createObject();
4337 rc = attachment->init(this,
4338 medium,
4339 aName,
4340 aControllerPort,
4341 aDevice,
4342 aType,
4343 fIndirect,
4344 false /* fPassthrough */,
4345 false /* fTempEject */,
4346 false /* fNonRotational */,
4347 false /* fDiscard */,
4348 fHotplug /* fHotPluggable */,
4349 Utf8Str::Empty);
4350 if (FAILED(rc)) return rc;
4351
4352 if (associate && !medium.isNull())
4353 {
4354 // as the last step, associate the medium to the VM
4355 rc = medium->i_addBackReference(mData->mUuid);
4356 // here we can fail because of Deleting, or being in process of creating a Diff
4357 if (FAILED(rc)) return rc;
4358
4359 mediumLock.release();
4360 treeLock.release();
4361 alock.release();
4362 i_addMediumToRegistry(medium);
4363 alock.acquire();
4364 treeLock.acquire();
4365 mediumLock.acquire();
4366 }
4367
4368 /* success: finally remember the attachment */
4369 i_setModified(IsModified_Storage);
4370 mMediumAttachments.backup();
4371 mMediumAttachments->push_back(attachment);
4372
4373 mediumLock.release();
4374 treeLock.release();
4375 alock.release();
4376
4377 if (fHotplug || fSilent)
4378 {
4379 if (!medium.isNull())
4380 {
4381 MediumLockList *pMediumLockList(new MediumLockList());
4382
4383 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4384 medium /* pToLockWrite */,
4385 false /* fMediumLockWriteAll */,
4386 NULL,
4387 *pMediumLockList);
4388 alock.acquire();
4389 if (FAILED(rc))
4390 delete pMediumLockList;
4391 else
4392 {
4393 mData->mSession.mLockedMedia.Unlock();
4394 alock.release();
4395 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4396 mData->mSession.mLockedMedia.Lock();
4397 alock.acquire();
4398 }
4399 alock.release();
4400 }
4401
4402 if (SUCCEEDED(rc))
4403 {
4404 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4405 /* Remove lock list in case of error. */
4406 if (FAILED(rc))
4407 {
4408 mData->mSession.mLockedMedia.Unlock();
4409 mData->mSession.mLockedMedia.Remove(attachment);
4410 mData->mSession.mLockedMedia.Lock();
4411 }
4412 }
4413 }
4414
4415 /* Save modified registries, but skip this machine as it's the caller's
4416 * job to save its settings like all other settings changes. */
4417 mParent->i_unmarkRegistryModified(i_getId());
4418 mParent->i_saveModifiedRegistries();
4419
4420 if (SUCCEEDED(rc))
4421 {
4422 if (fIndirect && medium != aM)
4423 mParent->i_onMediumConfigChanged(medium);
4424 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4425 }
4426
4427 return rc;
4428}
4429
4430HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4431 LONG aDevice)
4432{
4433 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4434 aName.c_str(), aControllerPort, aDevice));
4435
4436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4437
4438 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4439 if (FAILED(rc)) return rc;
4440
4441 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4442
4443 /* Check for an existing controller. */
4444 ComObjPtr<StorageController> ctl;
4445 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4446 if (FAILED(rc)) return rc;
4447
4448 StorageControllerType_T ctrlType;
4449 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4450 if (FAILED(rc))
4451 return setError(E_FAIL,
4452 tr("Could not get type of controller '%s'"),
4453 aName.c_str());
4454
4455 bool fSilent = false;
4456 Utf8Str strReconfig;
4457
4458 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4459 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4460 if ( mData->mMachineState == MachineState_Paused
4461 && strReconfig == "1")
4462 fSilent = true;
4463
4464 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4465 bool fHotplug = false;
4466 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4467 fHotplug = true;
4468
4469 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4470 return setError(VBOX_E_INVALID_VM_STATE,
4471 tr("Controller '%s' does not support hot-plugging"),
4472 aName.c_str());
4473
4474 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4475 aName,
4476 aControllerPort,
4477 aDevice);
4478 if (!pAttach)
4479 return setError(VBOX_E_OBJECT_NOT_FOUND,
4480 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4481 aDevice, aControllerPort, aName.c_str());
4482
4483 if (fHotplug && !pAttach->i_getHotPluggable())
4484 return setError(VBOX_E_NOT_SUPPORTED,
4485 tr("The device slot %d on port %d of controller '%s' does not support hot-plugging"),
4486 aDevice, aControllerPort, aName.c_str());
4487
4488 /*
4489 * The VM has to detach the device before we delete any implicit diffs.
4490 * If this fails we can roll back without loosing data.
4491 */
4492 if (fHotplug || fSilent)
4493 {
4494 alock.release();
4495 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4496 alock.acquire();
4497 }
4498 if (FAILED(rc)) return rc;
4499
4500 /* If we are here everything went well and we can delete the implicit now. */
4501 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4502
4503 alock.release();
4504
4505 /* Save modified registries, but skip this machine as it's the caller's
4506 * job to save its settings like all other settings changes. */
4507 mParent->i_unmarkRegistryModified(i_getId());
4508 mParent->i_saveModifiedRegistries();
4509
4510 if (SUCCEEDED(rc))
4511 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4512
4513 return rc;
4514}
4515
4516HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4517 LONG aDevice, BOOL aPassthrough)
4518{
4519 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4520 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4521
4522 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4523
4524 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4525 if (FAILED(rc)) return rc;
4526
4527 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4528
4529 /* Check for an existing controller. */
4530 ComObjPtr<StorageController> ctl;
4531 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4532 if (FAILED(rc)) return rc;
4533
4534 StorageControllerType_T ctrlType;
4535 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4536 if (FAILED(rc))
4537 return setError(E_FAIL,
4538 tr("Could not get type of controller '%s'"),
4539 aName.c_str());
4540
4541 bool fSilent = false;
4542 Utf8Str strReconfig;
4543
4544 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4545 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4546 if ( mData->mMachineState == MachineState_Paused
4547 && strReconfig == "1")
4548 fSilent = true;
4549
4550 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4551 bool fHotplug = false;
4552 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4553 fHotplug = true;
4554
4555 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4556 return setError(VBOX_E_INVALID_VM_STATE,
4557 tr("Controller '%s' does not support hot-plugging which is required to change the passthrough setting while the VM is running"),
4558 aName.c_str());
4559
4560 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4561 aName,
4562 aControllerPort,
4563 aDevice);
4564 if (!pAttach)
4565 return setError(VBOX_E_OBJECT_NOT_FOUND,
4566 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4567 aDevice, aControllerPort, aName.c_str());
4568
4569
4570 i_setModified(IsModified_Storage);
4571 mMediumAttachments.backup();
4572
4573 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4574
4575 if (pAttach->i_getType() != DeviceType_DVD)
4576 return setError(E_INVALIDARG,
4577 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4578 aDevice, aControllerPort, aName.c_str());
4579
4580 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4581
4582 pAttach->i_updatePassthrough(!!aPassthrough);
4583
4584 attLock.release();
4585 alock.release();
4586 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4587 if (SUCCEEDED(rc) && fValueChanged)
4588 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4589
4590 return rc;
4591}
4592
4593HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4594 LONG aDevice, BOOL aTemporaryEject)
4595{
4596
4597 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4598 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4599
4600 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4601
4602 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4603 if (FAILED(rc)) return rc;
4604
4605 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4606 aName,
4607 aControllerPort,
4608 aDevice);
4609 if (!pAttach)
4610 return setError(VBOX_E_OBJECT_NOT_FOUND,
4611 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4612 aDevice, aControllerPort, aName.c_str());
4613
4614
4615 i_setModified(IsModified_Storage);
4616 mMediumAttachments.backup();
4617
4618 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4619
4620 if (pAttach->i_getType() != DeviceType_DVD)
4621 return setError(E_INVALIDARG,
4622 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4623 aDevice, aControllerPort, aName.c_str());
4624 pAttach->i_updateTempEject(!!aTemporaryEject);
4625
4626 return S_OK;
4627}
4628
4629HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4630 LONG aDevice, BOOL aNonRotational)
4631{
4632
4633 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4634 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4635
4636 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4637
4638 HRESULT rc = i_checkStateDependency(MutableStateDep);
4639 if (FAILED(rc)) return rc;
4640
4641 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4642
4643 if (Global::IsOnlineOrTransient(mData->mMachineState))
4644 return setError(VBOX_E_INVALID_VM_STATE,
4645 tr("Invalid machine state: %s"),
4646 Global::stringifyMachineState(mData->mMachineState));
4647
4648 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4649 aName,
4650 aControllerPort,
4651 aDevice);
4652 if (!pAttach)
4653 return setError(VBOX_E_OBJECT_NOT_FOUND,
4654 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4655 aDevice, aControllerPort, aName.c_str());
4656
4657
4658 i_setModified(IsModified_Storage);
4659 mMediumAttachments.backup();
4660
4661 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4662
4663 if (pAttach->i_getType() != DeviceType_HardDisk)
4664 return setError(E_INVALIDARG,
4665 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"),
4666 aDevice, aControllerPort, aName.c_str());
4667 pAttach->i_updateNonRotational(!!aNonRotational);
4668
4669 return S_OK;
4670}
4671
4672HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4673 LONG aDevice, BOOL aDiscard)
4674{
4675
4676 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4677 aName.c_str(), aControllerPort, aDevice, aDiscard));
4678
4679 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4680
4681 HRESULT rc = i_checkStateDependency(MutableStateDep);
4682 if (FAILED(rc)) return rc;
4683
4684 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4685
4686 if (Global::IsOnlineOrTransient(mData->mMachineState))
4687 return setError(VBOX_E_INVALID_VM_STATE,
4688 tr("Invalid machine state: %s"),
4689 Global::stringifyMachineState(mData->mMachineState));
4690
4691 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4692 aName,
4693 aControllerPort,
4694 aDevice);
4695 if (!pAttach)
4696 return setError(VBOX_E_OBJECT_NOT_FOUND,
4697 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4698 aDevice, aControllerPort, aName.c_str());
4699
4700
4701 i_setModified(IsModified_Storage);
4702 mMediumAttachments.backup();
4703
4704 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4705
4706 if (pAttach->i_getType() != DeviceType_HardDisk)
4707 return setError(E_INVALIDARG,
4708 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"),
4709 aDevice, aControllerPort, aName.c_str());
4710 pAttach->i_updateDiscard(!!aDiscard);
4711
4712 return S_OK;
4713}
4714
4715HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4716 LONG aDevice, BOOL aHotPluggable)
4717{
4718 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4719 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4720
4721 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4722
4723 HRESULT rc = i_checkStateDependency(MutableStateDep);
4724 if (FAILED(rc)) return rc;
4725
4726 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4727
4728 if (Global::IsOnlineOrTransient(mData->mMachineState))
4729 return setError(VBOX_E_INVALID_VM_STATE,
4730 tr("Invalid machine state: %s"),
4731 Global::stringifyMachineState(mData->mMachineState));
4732
4733 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4734 aName,
4735 aControllerPort,
4736 aDevice);
4737 if (!pAttach)
4738 return setError(VBOX_E_OBJECT_NOT_FOUND,
4739 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4740 aDevice, aControllerPort, aName.c_str());
4741
4742 /* Check for an existing controller. */
4743 ComObjPtr<StorageController> ctl;
4744 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4745 if (FAILED(rc)) return rc;
4746
4747 StorageControllerType_T ctrlType;
4748 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4749 if (FAILED(rc))
4750 return setError(E_FAIL,
4751 tr("Could not get type of controller '%s'"),
4752 aName.c_str());
4753
4754 if (!i_isControllerHotplugCapable(ctrlType))
4755 return setError(VBOX_E_NOT_SUPPORTED,
4756 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4757 aName.c_str());
4758
4759 /* silently ignore attempts to modify the hot-plug status of USB devices */
4760 if (ctrlType == StorageControllerType_USB)
4761 return S_OK;
4762
4763 i_setModified(IsModified_Storage);
4764 mMediumAttachments.backup();
4765
4766 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4767
4768 if (pAttach->i_getType() == DeviceType_Floppy)
4769 return setError(E_INVALIDARG,
4770 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"),
4771 aDevice, aControllerPort, aName.c_str());
4772 pAttach->i_updateHotPluggable(!!aHotPluggable);
4773
4774 return S_OK;
4775}
4776
4777HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4778 LONG aDevice)
4779{
4780 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4781 aName.c_str(), aControllerPort, aDevice));
4782
4783 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4784}
4785
4786HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4787 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4788{
4789 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4790 aName.c_str(), aControllerPort, aDevice));
4791
4792 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4793
4794 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4795 if (FAILED(rc)) return rc;
4796
4797 if (Global::IsOnlineOrTransient(mData->mMachineState))
4798 return setError(VBOX_E_INVALID_VM_STATE,
4799 tr("Invalid machine state: %s"),
4800 Global::stringifyMachineState(mData->mMachineState));
4801
4802 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4803 aName,
4804 aControllerPort,
4805 aDevice);
4806 if (!pAttach)
4807 return setError(VBOX_E_OBJECT_NOT_FOUND,
4808 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4809 aDevice, aControllerPort, aName.c_str());
4810
4811
4812 i_setModified(IsModified_Storage);
4813 mMediumAttachments.backup();
4814
4815 IBandwidthGroup *iB = aBandwidthGroup;
4816 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4817 if (aBandwidthGroup && group.isNull())
4818 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4819
4820 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4821
4822 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4823 if (strBandwidthGroupOld.isNotEmpty())
4824 {
4825 /* Get the bandwidth group object and release it - this must not fail. */
4826 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4827 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4828 Assert(SUCCEEDED(rc));
4829
4830 pBandwidthGroupOld->i_release();
4831 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4832 }
4833
4834 if (!group.isNull())
4835 {
4836 group->i_reference();
4837 pAttach->i_updateBandwidthGroup(group->i_getName());
4838 }
4839
4840 return S_OK;
4841}
4842
4843HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4844 LONG aControllerPort,
4845 LONG aDevice,
4846 DeviceType_T aType)
4847{
4848 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4849 aName.c_str(), aControllerPort, aDevice, aType));
4850
4851 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4852}
4853
4854
4855HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4856 LONG aControllerPort,
4857 LONG aDevice,
4858 BOOL aForce)
4859{
4860 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4861 aName.c_str(), aControllerPort, aForce));
4862
4863 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4864}
4865
4866HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4867 LONG aControllerPort,
4868 LONG aDevice,
4869 const ComPtr<IMedium> &aMedium,
4870 BOOL aForce)
4871{
4872 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4873 aName.c_str(), aControllerPort, aDevice, aForce));
4874
4875 // request the host lock first, since might be calling Host methods for getting host drives;
4876 // next, protect the media tree all the while we're in here, as well as our member variables
4877 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4878 this->lockHandle(),
4879 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4880
4881 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4882 if (FAILED(hrc)) return hrc;
4883
4884 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4885 aName,
4886 aControllerPort,
4887 aDevice);
4888 if (pAttach.isNull())
4889 return setError(VBOX_E_OBJECT_NOT_FOUND,
4890 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4891 aDevice, aControllerPort, aName.c_str());
4892
4893 /* Remember previously mounted medium. The medium before taking the
4894 * backup is not necessarily the same thing. */
4895 ComObjPtr<Medium> oldmedium;
4896 oldmedium = pAttach->i_getMedium();
4897
4898 IMedium *iM = aMedium;
4899 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4900 if (aMedium && pMedium.isNull())
4901 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4902
4903 /* Check if potential medium is already mounted */
4904 if (pMedium == oldmedium)
4905 return S_OK;
4906
4907 AutoCaller mediumCaller(pMedium);
4908 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4909
4910 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4911 if (pMedium)
4912 {
4913 DeviceType_T mediumType = pAttach->i_getType();
4914 switch (mediumType)
4915 {
4916 case DeviceType_DVD:
4917 case DeviceType_Floppy:
4918 break;
4919
4920 default:
4921 return setError(VBOX_E_INVALID_OBJECT_STATE,
4922 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4923 aControllerPort,
4924 aDevice,
4925 aName.c_str());
4926 }
4927 }
4928
4929 i_setModified(IsModified_Storage);
4930 mMediumAttachments.backup();
4931
4932 {
4933 // The backup operation makes the pAttach reference point to the
4934 // old settings. Re-get the correct reference.
4935 pAttach = i_findAttachment(*mMediumAttachments.data(),
4936 aName,
4937 aControllerPort,
4938 aDevice);
4939 if (!oldmedium.isNull())
4940 oldmedium->i_removeBackReference(mData->mUuid);
4941 if (!pMedium.isNull())
4942 {
4943 pMedium->i_addBackReference(mData->mUuid);
4944
4945 mediumLock.release();
4946 multiLock.release();
4947 i_addMediumToRegistry(pMedium);
4948 multiLock.acquire();
4949 mediumLock.acquire();
4950 }
4951
4952 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4953 pAttach->i_updateMedium(pMedium);
4954 }
4955
4956 i_setModified(IsModified_Storage);
4957
4958 mediumLock.release();
4959 multiLock.release();
4960 HRESULT rc = i_onMediumChange(pAttach, aForce);
4961 multiLock.acquire();
4962 mediumLock.acquire();
4963
4964 /* On error roll back this change only. */
4965 if (FAILED(rc))
4966 {
4967 if (!pMedium.isNull())
4968 pMedium->i_removeBackReference(mData->mUuid);
4969 pAttach = i_findAttachment(*mMediumAttachments.data(),
4970 aName,
4971 aControllerPort,
4972 aDevice);
4973 /* If the attachment is gone in the meantime, bail out. */
4974 if (pAttach.isNull())
4975 return rc;
4976 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4977 if (!oldmedium.isNull())
4978 oldmedium->i_addBackReference(mData->mUuid);
4979 pAttach->i_updateMedium(oldmedium);
4980 }
4981
4982 mediumLock.release();
4983 multiLock.release();
4984
4985 /* Save modified registries, but skip this machine as it's the caller's
4986 * job to save its settings like all other settings changes. */
4987 mParent->i_unmarkRegistryModified(i_getId());
4988 mParent->i_saveModifiedRegistries();
4989
4990 return rc;
4991}
4992HRESULT Machine::getMedium(const com::Utf8Str &aName,
4993 LONG aControllerPort,
4994 LONG aDevice,
4995 ComPtr<IMedium> &aMedium)
4996{
4997 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4998 aName.c_str(), aControllerPort, aDevice));
4999
5000 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5001
5002 aMedium = NULL;
5003
5004 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5005 aName,
5006 aControllerPort,
5007 aDevice);
5008 if (pAttach.isNull())
5009 return setError(VBOX_E_OBJECT_NOT_FOUND,
5010 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5011 aDevice, aControllerPort, aName.c_str());
5012
5013 aMedium = pAttach->i_getMedium();
5014
5015 return S_OK;
5016}
5017
5018HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
5019{
5020 if (aSlot < RT_ELEMENTS(mSerialPorts))
5021 {
5022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5023 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5024 return S_OK;
5025 }
5026 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
5027}
5028
5029HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
5030{
5031 if (aSlot < RT_ELEMENTS(mParallelPorts))
5032 {
5033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5034 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5035 return S_OK;
5036 }
5037 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
5038}
5039
5040
5041HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
5042{
5043 /* Do not assert if slot is out of range, just return the advertised
5044 status. testdriver/vbox.py triggers this in logVmInfo. */
5045 if (aSlot >= mNetworkAdapters.size())
5046 return setError(E_INVALIDARG,
5047 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
5048 aSlot, mNetworkAdapters.size());
5049
5050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5051
5052 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
5053
5054 return S_OK;
5055}
5056
5057HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
5058{
5059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5060
5061 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
5062 size_t i = 0;
5063 for (settings::StringsMap::const_iterator
5064 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5065 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5066 ++it, ++i)
5067 aKeys[i] = it->first;
5068
5069 return S_OK;
5070}
5071
5072 /**
5073 * @note Locks this object for reading.
5074 */
5075HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
5076 com::Utf8Str &aValue)
5077{
5078 /* start with nothing found */
5079 aValue = "";
5080
5081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5082
5083 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5084 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5085 // found:
5086 aValue = it->second; // source is a Utf8Str
5087
5088 /* return the result to caller (may be empty) */
5089 return S_OK;
5090}
5091
5092 /**
5093 * @note Locks mParent for writing + this object for writing.
5094 */
5095HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5096{
5097 /* Because control characters in aKey have caused problems in the settings
5098 * they are rejected unless the key should be deleted. */
5099 if (!aValue.isEmpty())
5100 {
5101 for (size_t i = 0; i < aKey.length(); ++i)
5102 {
5103 char ch = aKey[i];
5104 if (RTLocCIsCntrl(ch))
5105 return E_INVALIDARG;
5106 }
5107 }
5108
5109 Utf8Str strOldValue; // empty
5110
5111 // locking note: we only hold the read lock briefly to look up the old value,
5112 // then release it and call the onExtraCanChange callbacks. There is a small
5113 // chance of a race insofar as the callback might be called twice if two callers
5114 // change the same key at the same time, but that's a much better solution
5115 // than the deadlock we had here before. The actual changing of the extradata
5116 // is then performed under the write lock and race-free.
5117
5118 // look up the old value first; if nothing has changed then we need not do anything
5119 {
5120 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5121
5122 // For snapshots don't even think about allowing changes, extradata
5123 // is global for a machine, so there is nothing snapshot specific.
5124 if (i_isSnapshotMachine())
5125 return setError(VBOX_E_INVALID_VM_STATE,
5126 tr("Cannot set extradata for a snapshot"));
5127
5128 // check if the right IMachine instance is used
5129 if (mData->mRegistered && !i_isSessionMachine())
5130 return setError(VBOX_E_INVALID_VM_STATE,
5131 tr("Cannot set extradata for an immutable machine"));
5132
5133 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5134 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5135 strOldValue = it->second;
5136 }
5137
5138 bool fChanged;
5139 if ((fChanged = (strOldValue != aValue)))
5140 {
5141 // ask for permission from all listeners outside the locks;
5142 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5143 // lock to copy the list of callbacks to invoke
5144 Bstr bstrError;
5145 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
5146 {
5147 const char *sep = bstrError.isEmpty() ? "" : ": ";
5148 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
5149 return setError(E_ACCESSDENIED,
5150 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5151 aKey.c_str(),
5152 aValue.c_str(),
5153 sep,
5154 bstrError.raw());
5155 }
5156
5157 // data is changing and change not vetoed: then write it out under the lock
5158 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5159
5160 if (aValue.isEmpty())
5161 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5162 else
5163 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5164 // creates a new key if needed
5165
5166 bool fNeedsGlobalSaveSettings = false;
5167 // This saving of settings is tricky: there is no "old state" for the
5168 // extradata items at all (unlike all other settings), so the old/new
5169 // settings comparison would give a wrong result!
5170 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
5171
5172 if (fNeedsGlobalSaveSettings)
5173 {
5174 // save the global settings; for that we should hold only the VirtualBox lock
5175 alock.release();
5176 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5177 mParent->i_saveSettings();
5178 }
5179 }
5180
5181 // fire notification outside the lock
5182 if (fChanged)
5183 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
5184
5185 return S_OK;
5186}
5187
5188HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5189{
5190 aProgress = NULL;
5191 NOREF(aSettingsFilePath);
5192 ReturnComNotImplemented();
5193}
5194
5195HRESULT Machine::saveSettings()
5196{
5197 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5198
5199 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5200 if (FAILED(rc)) return rc;
5201
5202 /* the settings file path may never be null */
5203 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5204
5205 /* save all VM data excluding snapshots */
5206 bool fNeedsGlobalSaveSettings = false;
5207 rc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
5208 mlock.release();
5209
5210 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5211 {
5212 // save the global settings; for that we should hold only the VirtualBox lock
5213 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5214 rc = mParent->i_saveSettings();
5215 }
5216
5217 return rc;
5218}
5219
5220
5221HRESULT Machine::discardSettings()
5222{
5223 /*
5224 * We need to take the machine list lock here as well as the machine one
5225 * or we'll get into trouble should any media stuff require rolling back.
5226 *
5227 * Details:
5228 *
5229 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5230 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5231 * 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]
5232 * 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
5233 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5234 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5235 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5236 * 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
5237 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5238 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5239 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5240 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5241 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5242 * 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]
5243 * 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] (*)
5244 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5245 * 0:005> k
5246 * # Child-SP RetAddr Call Site
5247 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5248 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5249 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5250 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5251 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5252 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5253 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5254 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5255 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5256 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5257 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5258 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5259 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5260 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5261 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5262 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5263 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5264 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5265 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5266 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5267 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5268 *
5269 */
5270 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5271 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5272
5273 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5274 if (FAILED(rc)) return rc;
5275
5276 /*
5277 * during this rollback, the session will be notified if data has
5278 * been actually changed
5279 */
5280 i_rollback(true /* aNotify */);
5281
5282 return S_OK;
5283}
5284
5285/** @note Locks objects! */
5286HRESULT Machine::unregister(AutoCaller &autoCaller,
5287 CleanupMode_T aCleanupMode,
5288 std::vector<ComPtr<IMedium> > &aMedia)
5289{
5290 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5291
5292 Guid id(i_getId());
5293
5294 if (mData->mSession.mState != SessionState_Unlocked)
5295 return setError(VBOX_E_INVALID_OBJECT_STATE,
5296 tr("Cannot unregister the machine '%s' while it is locked"),
5297 mUserData->s.strName.c_str());
5298
5299 // wait for state dependents to drop to zero
5300 i_ensureNoStateDependencies(alock);
5301
5302 if (!mData->mAccessible)
5303 {
5304 // inaccessible machines can only be unregistered; uninitialize ourselves
5305 // here because currently there may be no unregistered that are inaccessible
5306 // (this state combination is not supported). Note releasing the caller and
5307 // leaving the lock before calling uninit()
5308 alock.release();
5309 autoCaller.release();
5310
5311 uninit();
5312
5313 mParent->i_unregisterMachine(this, CleanupMode_UnregisterOnly, id);
5314 // calls VirtualBox::i_saveSettings()
5315
5316 return S_OK;
5317 }
5318
5319 HRESULT rc = S_OK;
5320 mData->llFilesToDelete.clear();
5321
5322 if (!mSSData->strStateFilePath.isEmpty())
5323 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5324
5325 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
5326 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
5327 mData->llFilesToDelete.push_back(strNVRAMFile);
5328
5329 // This list collects the medium objects from all medium attachments
5330 // which we will detach from the machine and its snapshots, in a specific
5331 // order which allows for closing all media without getting "media in use"
5332 // errors, simply by going through the list from the front to the back:
5333 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5334 // and must be closed before the parent media from the snapshots, or closing the parents
5335 // will fail because they still have children);
5336 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5337 // the root ("first") snapshot of the machine.
5338 MediaList llMedia;
5339
5340 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5341 && mMediumAttachments->size()
5342 )
5343 {
5344 // we have media attachments: detach them all and add the Medium objects to our list
5345 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5346 }
5347
5348 if (mData->mFirstSnapshot)
5349 {
5350 // add the media from the medium attachments of the snapshots to
5351 // llMedia as well, after the "main" machine media;
5352 // Snapshot::uninitAll() calls Machine::detachAllMedia() for each
5353 // snapshot machine, depth first.
5354
5355 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5356 MachineState_T oldState = mData->mMachineState;
5357 mData->mMachineState = MachineState_DeletingSnapshot;
5358
5359 // make a copy of the first snapshot reference so the refcount does not
5360 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
5361 // (would hang due to the AutoCaller voodoo)
5362 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5363
5364 // GO!
5365 pFirstSnapshot->i_uninitAll(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5366
5367 mData->mMachineState = oldState;
5368 }
5369
5370 if (FAILED(rc))
5371 {
5372 i_rollbackMedia();
5373 return rc;
5374 }
5375
5376 // commit all the media changes made above
5377 i_commitMedia();
5378
5379 mData->mRegistered = false;
5380
5381 // machine lock no longer needed
5382 alock.release();
5383
5384 /* Make sure that the settings of the current VM are not saved, because
5385 * they are rather crippled at this point to meet the cleanup expectations
5386 * and there's no point destroying the VM config on disk just because. */
5387 mParent->i_unmarkRegistryModified(id);
5388
5389 // return media to caller
5390 aMedia.resize(llMedia.size());
5391 size_t i = 0;
5392 for (MediaList::const_iterator
5393 it = llMedia.begin();
5394 it != llMedia.end();
5395 ++it, ++i)
5396 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5397
5398 mParent->i_unregisterMachine(this, aCleanupMode, id);
5399 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5400
5401 return S_OK;
5402}
5403
5404/**
5405 * Task record for deleting a machine config.
5406 */
5407class Machine::DeleteConfigTask
5408 : public Machine::Task
5409{
5410public:
5411 DeleteConfigTask(Machine *m,
5412 Progress *p,
5413 const Utf8Str &t,
5414 const RTCList<ComPtr<IMedium> > &llMediums,
5415 const StringsList &llFilesToDelete)
5416 : Task(m, p, t),
5417 m_llMediums(llMediums),
5418 m_llFilesToDelete(llFilesToDelete)
5419 {}
5420
5421private:
5422 void handler()
5423 {
5424 try
5425 {
5426 m_pMachine->i_deleteConfigHandler(*this);
5427 }
5428 catch (...)
5429 {
5430 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5431 }
5432 }
5433
5434 RTCList<ComPtr<IMedium> > m_llMediums;
5435 StringsList m_llFilesToDelete;
5436
5437 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5438};
5439
5440/**
5441 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5442 * SessionMachine::taskHandler().
5443 *
5444 * @note Locks this object for writing.
5445 *
5446 * @param task
5447 * @return
5448 */
5449void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5450{
5451 LogFlowThisFuncEnter();
5452
5453 AutoCaller autoCaller(this);
5454 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5455 if (FAILED(autoCaller.rc()))
5456 {
5457 /* we might have been uninitialized because the session was accidentally
5458 * closed by the client, so don't assert */
5459 HRESULT rc = setError(E_FAIL,
5460 tr("The session has been accidentally closed"));
5461 task.m_pProgress->i_notifyComplete(rc);
5462 LogFlowThisFuncLeave();
5463 return;
5464 }
5465
5466 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5467
5468 HRESULT rc = S_OK;
5469
5470 try
5471 {
5472 ULONG uLogHistoryCount = 3;
5473 ComPtr<ISystemProperties> systemProperties;
5474 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5475 if (FAILED(rc)) throw rc;
5476
5477 if (!systemProperties.isNull())
5478 {
5479 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5480 if (FAILED(rc)) throw rc;
5481 }
5482
5483 MachineState_T oldState = mData->mMachineState;
5484 i_setMachineState(MachineState_SettingUp);
5485 alock.release();
5486 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5487 {
5488 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5489 {
5490 AutoCaller mac(pMedium);
5491 if (FAILED(mac.rc())) throw mac.rc();
5492 Utf8Str strLocation = pMedium->i_getLocationFull();
5493 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5494 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5495 if (FAILED(rc)) throw rc;
5496 }
5497 if (pMedium->i_isMediumFormatFile())
5498 {
5499 ComPtr<IProgress> pProgress2;
5500 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5501 if (FAILED(rc)) throw rc;
5502 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5503 if (FAILED(rc)) throw rc;
5504 }
5505
5506 /* Close the medium, deliberately without checking the return
5507 * code, and without leaving any trace in the error info, as
5508 * a failure here is a very minor issue, which shouldn't happen
5509 * as above we even managed to delete the medium. */
5510 {
5511 ErrorInfoKeeper eik;
5512 pMedium->Close();
5513 }
5514 }
5515 i_setMachineState(oldState);
5516 alock.acquire();
5517
5518 // delete the files pushed on the task list by Machine::Delete()
5519 // (this includes saved states of the machine and snapshots and
5520 // medium storage files from the IMedium list passed in, and the
5521 // machine XML file)
5522 for (StringsList::const_iterator
5523 it = task.m_llFilesToDelete.begin();
5524 it != task.m_llFilesToDelete.end();
5525 ++it)
5526 {
5527 const Utf8Str &strFile = *it;
5528 LogFunc(("Deleting file %s\n", strFile.c_str()));
5529 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5530 if (FAILED(rc)) throw rc;
5531 i_deleteFile(strFile);
5532 }
5533
5534 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5535 if (FAILED(rc)) throw rc;
5536
5537 /* delete the settings only when the file actually exists */
5538 if (mData->pMachineConfigFile->fileExists())
5539 {
5540 /* Delete any backup or uncommitted XML files. Ignore failures.
5541 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5542 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5543 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5544 i_deleteFile(otherXml, true /* fIgnoreFailures */);
5545 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5546 i_deleteFile(otherXml, true /* fIgnoreFailures */);
5547
5548 /* delete the Logs folder, nothing important should be left
5549 * there (we don't check for errors because the user might have
5550 * some private files there that we don't want to delete) */
5551 Utf8Str logFolder;
5552 getLogFolder(logFolder);
5553 Assert(logFolder.length());
5554 if (RTDirExists(logFolder.c_str()))
5555 {
5556 /* Delete all VBox.log[.N] files from the Logs folder
5557 * (this must be in sync with the rotation logic in
5558 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5559 * files that may have been created by the GUI. */
5560 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5561 i_deleteFile(log, true /* fIgnoreFailures */);
5562 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5563 i_deleteFile(log, true /* fIgnoreFailures */);
5564 for (ULONG i = uLogHistoryCount; i > 0; i--)
5565 {
5566 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5567 i_deleteFile(log, true /* fIgnoreFailures */);
5568 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5569 i_deleteFile(log, true /* fIgnoreFailures */);
5570 }
5571 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5572 i_deleteFile(log, true /* fIgnoreFailures */);
5573#if defined(RT_OS_WINDOWS)
5574 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5575 i_deleteFile(log, true /* fIgnoreFailures */);
5576 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5577 i_deleteFile(log, true /* fIgnoreFailures */);
5578#endif
5579
5580 RTDirRemove(logFolder.c_str());
5581 }
5582
5583 /* delete the Snapshots folder, nothing important should be left
5584 * there (we don't check for errors because the user might have
5585 * some private files there that we don't want to delete) */
5586 Utf8Str strFullSnapshotFolder;
5587 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5588 Assert(!strFullSnapshotFolder.isEmpty());
5589 if (RTDirExists(strFullSnapshotFolder.c_str()))
5590 RTDirRemove(strFullSnapshotFolder.c_str());
5591
5592 // delete the directory that contains the settings file, but only
5593 // if it matches the VM name
5594 Utf8Str settingsDir;
5595 if (i_isInOwnDir(&settingsDir))
5596 RTDirRemove(settingsDir.c_str());
5597 }
5598
5599 alock.release();
5600
5601 mParent->i_saveModifiedRegistries();
5602 }
5603 catch (HRESULT aRC) { rc = aRC; }
5604
5605 task.m_pProgress->i_notifyComplete(rc);
5606
5607 LogFlowThisFuncLeave();
5608}
5609
5610HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5611{
5612 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5613
5614 HRESULT rc = i_checkStateDependency(MutableStateDep);
5615 if (FAILED(rc)) return rc;
5616
5617 if (mData->mRegistered)
5618 return setError(VBOX_E_INVALID_VM_STATE,
5619 tr("Cannot delete settings of a registered machine"));
5620
5621 // collect files to delete
5622 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5623 // machine config file
5624 if (mData->pMachineConfigFile->fileExists())
5625 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5626 // backup of machine config file
5627 Utf8Str strTmp(mData->m_strConfigFileFull);
5628 strTmp.append("-prev");
5629 if (RTFileExists(strTmp.c_str()))
5630 llFilesToDelete.push_back(strTmp);
5631
5632 RTCList<ComPtr<IMedium> > llMediums;
5633 for (size_t i = 0; i < aMedia.size(); ++i)
5634 {
5635 IMedium *pIMedium(aMedia[i]);
5636 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5637 if (pMedium.isNull())
5638 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
5639 SafeArray<BSTR> ids;
5640 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5641 if (FAILED(rc)) return rc;
5642 /* At this point the medium should not have any back references
5643 * anymore. If it has it is attached to another VM and *must* not
5644 * deleted. */
5645 if (ids.size() < 1)
5646 llMediums.append(pMedium);
5647 }
5648
5649 ComObjPtr<Progress> pProgress;
5650 pProgress.createObject();
5651 rc = pProgress->init(i_getVirtualBox(),
5652 static_cast<IMachine*>(this) /* aInitiator */,
5653 tr("Deleting files"),
5654 true /* fCancellable */,
5655 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5656 tr("Collecting file inventory"));
5657 if (FAILED(rc))
5658 return rc;
5659
5660 /* create and start the task on a separate thread (note that it will not
5661 * start working until we release alock) */
5662 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5663 rc = pTask->createThread();
5664 pTask = NULL;
5665 if (FAILED(rc))
5666 return rc;
5667
5668 pProgress.queryInterfaceTo(aProgress.asOutParam());
5669
5670 LogFlowFuncLeave();
5671
5672 return S_OK;
5673}
5674
5675HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5676{
5677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5678
5679 ComObjPtr<Snapshot> pSnapshot;
5680 HRESULT rc;
5681
5682 if (aNameOrId.isEmpty())
5683 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5684 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5685 else
5686 {
5687 Guid uuid(aNameOrId);
5688 if (uuid.isValid())
5689 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5690 else
5691 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5692 }
5693 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5694
5695 return rc;
5696}
5697
5698HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5699 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5700{
5701 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5702
5703 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5704 if (FAILED(rc)) return rc;
5705
5706 ComObjPtr<SharedFolder> sharedFolder;
5707 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5708 if (SUCCEEDED(rc))
5709 return setError(VBOX_E_OBJECT_IN_USE,
5710 tr("Shared folder named '%s' already exists"),
5711 aName.c_str());
5712
5713 sharedFolder.createObject();
5714 rc = sharedFolder->init(i_getMachine(),
5715 aName,
5716 aHostPath,
5717 !!aWritable,
5718 !!aAutomount,
5719 aAutoMountPoint,
5720 true /* fFailOnError */);
5721 if (FAILED(rc)) return rc;
5722
5723 i_setModified(IsModified_SharedFolders);
5724 mHWData.backup();
5725 mHWData->mSharedFolders.push_back(sharedFolder);
5726
5727 /* inform the direct session if any */
5728 alock.release();
5729 i_onSharedFolderChange();
5730
5731 return S_OK;
5732}
5733
5734HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5735{
5736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5737
5738 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5739 if (FAILED(rc)) return rc;
5740
5741 ComObjPtr<SharedFolder> sharedFolder;
5742 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5743 if (FAILED(rc)) return rc;
5744
5745 i_setModified(IsModified_SharedFolders);
5746 mHWData.backup();
5747 mHWData->mSharedFolders.remove(sharedFolder);
5748
5749 /* inform the direct session if any */
5750 alock.release();
5751 i_onSharedFolderChange();
5752
5753 return S_OK;
5754}
5755
5756HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5757{
5758 /* start with No */
5759 *aCanShow = FALSE;
5760
5761 ComPtr<IInternalSessionControl> directControl;
5762 {
5763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5764
5765 if (mData->mSession.mState != SessionState_Locked)
5766 return setError(VBOX_E_INVALID_VM_STATE,
5767 tr("Machine is not locked for session (session state: %s)"),
5768 Global::stringifySessionState(mData->mSession.mState));
5769
5770 if (mData->mSession.mLockType == LockType_VM)
5771 directControl = mData->mSession.mDirectControl;
5772 }
5773
5774 /* ignore calls made after #OnSessionEnd() is called */
5775 if (!directControl)
5776 return S_OK;
5777
5778 LONG64 dummy;
5779 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5780}
5781
5782HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5783{
5784 ComPtr<IInternalSessionControl> directControl;
5785 {
5786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5787
5788 if (mData->mSession.mState != SessionState_Locked)
5789 return setError(E_FAIL,
5790 tr("Machine is not locked for session (session state: %s)"),
5791 Global::stringifySessionState(mData->mSession.mState));
5792
5793 if (mData->mSession.mLockType == LockType_VM)
5794 directControl = mData->mSession.mDirectControl;
5795 }
5796
5797 /* ignore calls made after #OnSessionEnd() is called */
5798 if (!directControl)
5799 return S_OK;
5800
5801 BOOL dummy;
5802 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5803}
5804
5805#ifdef VBOX_WITH_GUEST_PROPS
5806/**
5807 * Look up a guest property in VBoxSVC's internal structures.
5808 */
5809HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5810 com::Utf8Str &aValue,
5811 LONG64 *aTimestamp,
5812 com::Utf8Str &aFlags) const
5813{
5814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5815
5816 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5817 if (it != mHWData->mGuestProperties.end())
5818 {
5819 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5820 aValue = it->second.strValue;
5821 *aTimestamp = it->second.mTimestamp;
5822 GuestPropWriteFlags(it->second.mFlags, szFlags);
5823 aFlags = Utf8Str(szFlags);
5824 }
5825
5826 return S_OK;
5827}
5828
5829/**
5830 * Query the VM that a guest property belongs to for the property.
5831 * @returns E_ACCESSDENIED if the VM process is not available or not
5832 * currently handling queries and the lookup should then be done in
5833 * VBoxSVC.
5834 */
5835HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5836 com::Utf8Str &aValue,
5837 LONG64 *aTimestamp,
5838 com::Utf8Str &aFlags) const
5839{
5840 HRESULT rc = S_OK;
5841 Bstr bstrValue;
5842 Bstr bstrFlags;
5843
5844 ComPtr<IInternalSessionControl> directControl;
5845 {
5846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5847 if (mData->mSession.mLockType == LockType_VM)
5848 directControl = mData->mSession.mDirectControl;
5849 }
5850
5851 /* ignore calls made after #OnSessionEnd() is called */
5852 if (!directControl)
5853 rc = E_ACCESSDENIED;
5854 else
5855 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5856 0 /* accessMode */,
5857 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5858
5859 aValue = bstrValue;
5860 aFlags = bstrFlags;
5861
5862 return rc;
5863}
5864#endif // VBOX_WITH_GUEST_PROPS
5865
5866HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5867 com::Utf8Str &aValue,
5868 LONG64 *aTimestamp,
5869 com::Utf8Str &aFlags)
5870{
5871#ifndef VBOX_WITH_GUEST_PROPS
5872 ReturnComNotImplemented();
5873#else // VBOX_WITH_GUEST_PROPS
5874
5875 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5876
5877 if (rc == E_ACCESSDENIED)
5878 /* The VM is not running or the service is not (yet) accessible */
5879 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5880 return rc;
5881#endif // VBOX_WITH_GUEST_PROPS
5882}
5883
5884HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5885{
5886 LONG64 dummyTimestamp;
5887 com::Utf8Str dummyFlags;
5888 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5889 return rc;
5890
5891}
5892HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5893{
5894 com::Utf8Str dummyFlags;
5895 com::Utf8Str dummyValue;
5896 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5897 return rc;
5898}
5899
5900#ifdef VBOX_WITH_GUEST_PROPS
5901/**
5902 * Set a guest property in VBoxSVC's internal structures.
5903 */
5904HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5905 const com::Utf8Str &aFlags, bool fDelete)
5906{
5907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5908 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5909 if (FAILED(rc)) return rc;
5910
5911 try
5912 {
5913 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5914 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5915 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5916
5917 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
5918 return setError(E_INVALIDARG, tr("Properties with TRANSIENT or TRANSRESET flag cannot be set or modified if VM is not running"));
5919
5920 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5921 if (it == mHWData->mGuestProperties.end())
5922 {
5923 if (!fDelete)
5924 {
5925 i_setModified(IsModified_MachineData);
5926 mHWData.backupEx();
5927
5928 RTTIMESPEC time;
5929 HWData::GuestProperty prop;
5930 prop.strValue = aValue;
5931 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5932 prop.mFlags = fFlags;
5933 mHWData->mGuestProperties[aName] = prop;
5934 }
5935 }
5936 else
5937 {
5938 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5939 {
5940 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5941 }
5942 else
5943 {
5944 i_setModified(IsModified_MachineData);
5945 mHWData.backupEx();
5946
5947 /* The backupEx() operation invalidates our iterator,
5948 * so get a new one. */
5949 it = mHWData->mGuestProperties.find(aName);
5950 Assert(it != mHWData->mGuestProperties.end());
5951
5952 if (!fDelete)
5953 {
5954 RTTIMESPEC time;
5955 it->second.strValue = aValue;
5956 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5957 it->second.mFlags = fFlags;
5958 }
5959 else
5960 mHWData->mGuestProperties.erase(it);
5961 }
5962 }
5963
5964 if (SUCCEEDED(rc))
5965 {
5966 alock.release();
5967
5968 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fDelete);
5969 }
5970 }
5971 catch (std::bad_alloc &)
5972 {
5973 rc = E_OUTOFMEMORY;
5974 }
5975
5976 return rc;
5977}
5978
5979/**
5980 * Set a property on the VM that that property belongs to.
5981 * @returns E_ACCESSDENIED if the VM process is not available or not
5982 * currently handling queries and the setting should then be done in
5983 * VBoxSVC.
5984 */
5985HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5986 const com::Utf8Str &aFlags, bool fDelete)
5987{
5988 HRESULT rc;
5989
5990 try
5991 {
5992 ComPtr<IInternalSessionControl> directControl;
5993 {
5994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5995 if (mData->mSession.mLockType == LockType_VM)
5996 directControl = mData->mSession.mDirectControl;
5997 }
5998
5999 Bstr dummy1; /* will not be changed (setter) */
6000 Bstr dummy2; /* will not be changed (setter) */
6001 LONG64 dummy64;
6002 if (!directControl)
6003 rc = E_ACCESSDENIED;
6004 else
6005 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6006 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
6007 fDelete ? 2 : 1 /* accessMode */,
6008 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
6009 }
6010 catch (std::bad_alloc &)
6011 {
6012 rc = E_OUTOFMEMORY;
6013 }
6014
6015 return rc;
6016}
6017#endif // VBOX_WITH_GUEST_PROPS
6018
6019HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
6020 const com::Utf8Str &aFlags)
6021{
6022#ifndef VBOX_WITH_GUEST_PROPS
6023 ReturnComNotImplemented();
6024#else // VBOX_WITH_GUEST_PROPS
6025
6026 int vrc = GuestPropValidateName(aProperty.c_str(), aProperty.length() + 1 /* '\0' */);
6027 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
6028
6029 vrc = GuestPropValidateValue(aValue.c_str(), aValue.length() + 1 /* '\0' */);
6030 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
6031
6032 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
6033 if (rc == E_ACCESSDENIED)
6034 /* The VM is not running or the service is not (yet) accessible */
6035 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
6036 return rc;
6037#endif // VBOX_WITH_GUEST_PROPS
6038}
6039
6040HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
6041{
6042 return setGuestProperty(aProperty, aValue, "");
6043}
6044
6045HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
6046{
6047#ifndef VBOX_WITH_GUEST_PROPS
6048 ReturnComNotImplemented();
6049#else // VBOX_WITH_GUEST_PROPS
6050 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
6051 if (rc == E_ACCESSDENIED)
6052 /* The VM is not running or the service is not (yet) accessible */
6053 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
6054 return rc;
6055#endif // VBOX_WITH_GUEST_PROPS
6056}
6057
6058#ifdef VBOX_WITH_GUEST_PROPS
6059/**
6060 * Enumerate the guest properties in VBoxSVC's internal structures.
6061 */
6062HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
6063 std::vector<com::Utf8Str> &aNames,
6064 std::vector<com::Utf8Str> &aValues,
6065 std::vector<LONG64> &aTimestamps,
6066 std::vector<com::Utf8Str> &aFlags)
6067{
6068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6069 Utf8Str strPatterns(aPatterns);
6070
6071 /*
6072 * Look for matching patterns and build up a list.
6073 */
6074 HWData::GuestPropertyMap propMap;
6075 for (HWData::GuestPropertyMap::const_iterator
6076 it = mHWData->mGuestProperties.begin();
6077 it != mHWData->mGuestProperties.end();
6078 ++it)
6079 {
6080 if ( strPatterns.isEmpty()
6081 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6082 RTSTR_MAX,
6083 it->first.c_str(),
6084 RTSTR_MAX,
6085 NULL)
6086 )
6087 propMap.insert(*it);
6088 }
6089
6090 alock.release();
6091
6092 /*
6093 * And build up the arrays for returning the property information.
6094 */
6095 size_t cEntries = propMap.size();
6096
6097 aNames.resize(cEntries);
6098 aValues.resize(cEntries);
6099 aTimestamps.resize(cEntries);
6100 aFlags.resize(cEntries);
6101
6102 size_t i = 0;
6103 for (HWData::GuestPropertyMap::const_iterator
6104 it = propMap.begin();
6105 it != propMap.end();
6106 ++it, ++i)
6107 {
6108 aNames[i] = it->first;
6109 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
6110 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
6111
6112 aValues[i] = it->second.strValue;
6113 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
6114 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
6115
6116 aTimestamps[i] = it->second.mTimestamp;
6117
6118 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6119 GuestPropWriteFlags(it->second.mFlags, szFlags);
6120 aFlags[i] = szFlags;
6121 }
6122
6123 return S_OK;
6124}
6125
6126/**
6127 * Enumerate the properties managed by a VM.
6128 * @returns E_ACCESSDENIED if the VM process is not available or not
6129 * currently handling queries and the setting should then be done in
6130 * VBoxSVC.
6131 */
6132HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6133 std::vector<com::Utf8Str> &aNames,
6134 std::vector<com::Utf8Str> &aValues,
6135 std::vector<LONG64> &aTimestamps,
6136 std::vector<com::Utf8Str> &aFlags)
6137{
6138 HRESULT rc;
6139 ComPtr<IInternalSessionControl> directControl;
6140 {
6141 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6142 if (mData->mSession.mLockType == LockType_VM)
6143 directControl = mData->mSession.mDirectControl;
6144 }
6145
6146 com::SafeArray<BSTR> bNames;
6147 com::SafeArray<BSTR> bValues;
6148 com::SafeArray<LONG64> bTimestamps;
6149 com::SafeArray<BSTR> bFlags;
6150
6151 if (!directControl)
6152 rc = E_ACCESSDENIED;
6153 else
6154 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6155 ComSafeArrayAsOutParam(bNames),
6156 ComSafeArrayAsOutParam(bValues),
6157 ComSafeArrayAsOutParam(bTimestamps),
6158 ComSafeArrayAsOutParam(bFlags));
6159 size_t i;
6160 aNames.resize(bNames.size());
6161 for (i = 0; i < bNames.size(); ++i)
6162 aNames[i] = Utf8Str(bNames[i]);
6163 aValues.resize(bValues.size());
6164 for (i = 0; i < bValues.size(); ++i)
6165 aValues[i] = Utf8Str(bValues[i]);
6166 aTimestamps.resize(bTimestamps.size());
6167 for (i = 0; i < bTimestamps.size(); ++i)
6168 aTimestamps[i] = bTimestamps[i];
6169 aFlags.resize(bFlags.size());
6170 for (i = 0; i < bFlags.size(); ++i)
6171 aFlags[i] = Utf8Str(bFlags[i]);
6172
6173 return rc;
6174}
6175#endif // VBOX_WITH_GUEST_PROPS
6176HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6177 std::vector<com::Utf8Str> &aNames,
6178 std::vector<com::Utf8Str> &aValues,
6179 std::vector<LONG64> &aTimestamps,
6180 std::vector<com::Utf8Str> &aFlags)
6181{
6182#ifndef VBOX_WITH_GUEST_PROPS
6183 ReturnComNotImplemented();
6184#else // VBOX_WITH_GUEST_PROPS
6185
6186 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6187
6188 if (rc == E_ACCESSDENIED)
6189 /* The VM is not running or the service is not (yet) accessible */
6190 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6191 return rc;
6192#endif // VBOX_WITH_GUEST_PROPS
6193}
6194
6195HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6196 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6197{
6198 MediumAttachmentList atts;
6199
6200 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6201 if (FAILED(rc)) return rc;
6202
6203 aMediumAttachments.resize(atts.size());
6204 size_t i = 0;
6205 for (MediumAttachmentList::const_iterator
6206 it = atts.begin();
6207 it != atts.end();
6208 ++it, ++i)
6209 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6210
6211 return S_OK;
6212}
6213
6214HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6215 LONG aControllerPort,
6216 LONG aDevice,
6217 ComPtr<IMediumAttachment> &aAttachment)
6218{
6219 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6220 aName.c_str(), aControllerPort, aDevice));
6221
6222 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6223
6224 aAttachment = NULL;
6225
6226 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6227 aName,
6228 aControllerPort,
6229 aDevice);
6230 if (pAttach.isNull())
6231 return setError(VBOX_E_OBJECT_NOT_FOUND,
6232 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6233 aDevice, aControllerPort, aName.c_str());
6234
6235 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6236
6237 return S_OK;
6238}
6239
6240
6241HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6242 StorageBus_T aConnectionType,
6243 ComPtr<IStorageController> &aController)
6244{
6245 if ( (aConnectionType <= StorageBus_Null)
6246 || (aConnectionType > StorageBus_VirtioSCSI))
6247 return setError(E_INVALIDARG,
6248 tr("Invalid connection type: %d"),
6249 aConnectionType);
6250
6251 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6252
6253 HRESULT rc = i_checkStateDependency(MutableStateDep);
6254 if (FAILED(rc)) return rc;
6255
6256 /* try to find one with the name first. */
6257 ComObjPtr<StorageController> ctrl;
6258
6259 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6260 if (SUCCEEDED(rc))
6261 return setError(VBOX_E_OBJECT_IN_USE,
6262 tr("Storage controller named '%s' already exists"),
6263 aName.c_str());
6264
6265 ctrl.createObject();
6266
6267 /* get a new instance number for the storage controller */
6268 ULONG ulInstance = 0;
6269 bool fBootable = true;
6270 for (StorageControllerList::const_iterator
6271 it = mStorageControllers->begin();
6272 it != mStorageControllers->end();
6273 ++it)
6274 {
6275 if ((*it)->i_getStorageBus() == aConnectionType)
6276 {
6277 ULONG ulCurInst = (*it)->i_getInstance();
6278
6279 if (ulCurInst >= ulInstance)
6280 ulInstance = ulCurInst + 1;
6281
6282 /* Only one controller of each type can be marked as bootable. */
6283 if ((*it)->i_getBootable())
6284 fBootable = false;
6285 }
6286 }
6287
6288 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6289 if (FAILED(rc)) return rc;
6290
6291 i_setModified(IsModified_Storage);
6292 mStorageControllers.backup();
6293 mStorageControllers->push_back(ctrl);
6294
6295 ctrl.queryInterfaceTo(aController.asOutParam());
6296
6297 /* inform the direct session if any */
6298 alock.release();
6299 i_onStorageControllerChange(i_getId(), aName);
6300
6301 return S_OK;
6302}
6303
6304HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6305 ComPtr<IStorageController> &aStorageController)
6306{
6307 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6308
6309 ComObjPtr<StorageController> ctrl;
6310
6311 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6312 if (SUCCEEDED(rc))
6313 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6314
6315 return rc;
6316}
6317
6318HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6319 ULONG aInstance,
6320 ComPtr<IStorageController> &aStorageController)
6321{
6322 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6323
6324 for (StorageControllerList::const_iterator
6325 it = mStorageControllers->begin();
6326 it != mStorageControllers->end();
6327 ++it)
6328 {
6329 if ( (*it)->i_getStorageBus() == aConnectionType
6330 && (*it)->i_getInstance() == aInstance)
6331 {
6332 (*it).queryInterfaceTo(aStorageController.asOutParam());
6333 return S_OK;
6334 }
6335 }
6336
6337 return setError(VBOX_E_OBJECT_NOT_FOUND,
6338 tr("Could not find a storage controller with instance number '%lu'"),
6339 aInstance);
6340}
6341
6342HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6343{
6344 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6345
6346 HRESULT rc = i_checkStateDependency(MutableStateDep);
6347 if (FAILED(rc)) return rc;
6348
6349 ComObjPtr<StorageController> ctrl;
6350
6351 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6352 if (SUCCEEDED(rc))
6353 {
6354 /* Ensure that only one controller of each type is marked as bootable. */
6355 if (aBootable == TRUE)
6356 {
6357 for (StorageControllerList::const_iterator
6358 it = mStorageControllers->begin();
6359 it != mStorageControllers->end();
6360 ++it)
6361 {
6362 ComObjPtr<StorageController> aCtrl = (*it);
6363
6364 if ( (aCtrl->i_getName() != aName)
6365 && aCtrl->i_getBootable() == TRUE
6366 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6367 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6368 {
6369 aCtrl->i_setBootable(FALSE);
6370 break;
6371 }
6372 }
6373 }
6374
6375 if (SUCCEEDED(rc))
6376 {
6377 ctrl->i_setBootable(aBootable);
6378 i_setModified(IsModified_Storage);
6379 }
6380 }
6381
6382 if (SUCCEEDED(rc))
6383 {
6384 /* inform the direct session if any */
6385 alock.release();
6386 i_onStorageControllerChange(i_getId(), aName);
6387 }
6388
6389 return rc;
6390}
6391
6392HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6393{
6394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6395
6396 HRESULT rc = i_checkStateDependency(MutableStateDep);
6397 if (FAILED(rc)) return rc;
6398
6399 ComObjPtr<StorageController> ctrl;
6400 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6401 if (FAILED(rc)) return rc;
6402
6403 MediumAttachmentList llDetachedAttachments;
6404 {
6405 /* find all attached devices to the appropriate storage controller and detach them all */
6406 // make a temporary list because detachDevice invalidates iterators into
6407 // mMediumAttachments
6408 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6409
6410 for (MediumAttachmentList::const_iterator
6411 it = llAttachments2.begin();
6412 it != llAttachments2.end();
6413 ++it)
6414 {
6415 MediumAttachment *pAttachTemp = *it;
6416
6417 AutoCaller localAutoCaller(pAttachTemp);
6418 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6419
6420 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6421
6422 if (pAttachTemp->i_getControllerName() == aName)
6423 {
6424 llDetachedAttachments.push_back(pAttachTemp);
6425 rc = i_detachDevice(pAttachTemp, alock, NULL);
6426 if (FAILED(rc)) return rc;
6427 }
6428 }
6429 }
6430
6431 /* send event about detached devices before removing parent controller */
6432 for (MediumAttachmentList::const_iterator
6433 it = llDetachedAttachments.begin();
6434 it != llDetachedAttachments.end();
6435 ++it)
6436 {
6437 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6438 }
6439
6440 /* We can remove it now. */
6441 i_setModified(IsModified_Storage);
6442 mStorageControllers.backup();
6443
6444 ctrl->i_unshare();
6445
6446 mStorageControllers->remove(ctrl);
6447
6448 /* inform the direct session if any */
6449 alock.release();
6450 i_onStorageControllerChange(i_getId(), aName);
6451
6452 return S_OK;
6453}
6454
6455HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6456 ComPtr<IUSBController> &aController)
6457{
6458 if ( (aType <= USBControllerType_Null)
6459 || (aType >= USBControllerType_Last))
6460 return setError(E_INVALIDARG,
6461 tr("Invalid USB controller type: %d"),
6462 aType);
6463
6464 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6465
6466 HRESULT rc = i_checkStateDependency(MutableStateDep);
6467 if (FAILED(rc)) return rc;
6468
6469 /* try to find one with the same type first. */
6470 ComObjPtr<USBController> ctrl;
6471
6472 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6473 if (SUCCEEDED(rc))
6474 return setError(VBOX_E_OBJECT_IN_USE,
6475 tr("USB controller named '%s' already exists"),
6476 aName.c_str());
6477
6478 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6479 ULONG maxInstances;
6480 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6481 if (FAILED(rc))
6482 return rc;
6483
6484 ULONG cInstances = i_getUSBControllerCountByType(aType);
6485 if (cInstances >= maxInstances)
6486 return setError(E_INVALIDARG,
6487 tr("Too many USB controllers of this type"));
6488
6489 ctrl.createObject();
6490
6491 rc = ctrl->init(this, aName, aType);
6492 if (FAILED(rc)) return rc;
6493
6494 i_setModified(IsModified_USB);
6495 mUSBControllers.backup();
6496 mUSBControllers->push_back(ctrl);
6497
6498 ctrl.queryInterfaceTo(aController.asOutParam());
6499
6500 /* inform the direct session if any */
6501 alock.release();
6502 i_onUSBControllerChange();
6503
6504 return S_OK;
6505}
6506
6507HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6508{
6509 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6510
6511 ComObjPtr<USBController> ctrl;
6512
6513 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6514 if (SUCCEEDED(rc))
6515 ctrl.queryInterfaceTo(aController.asOutParam());
6516
6517 return rc;
6518}
6519
6520HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6521 ULONG *aControllers)
6522{
6523 if ( (aType <= USBControllerType_Null)
6524 || (aType >= USBControllerType_Last))
6525 return setError(E_INVALIDARG,
6526 tr("Invalid USB controller type: %d"),
6527 aType);
6528
6529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6530
6531 ComObjPtr<USBController> ctrl;
6532
6533 *aControllers = i_getUSBControllerCountByType(aType);
6534
6535 return S_OK;
6536}
6537
6538HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6539{
6540
6541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6542
6543 HRESULT rc = i_checkStateDependency(MutableStateDep);
6544 if (FAILED(rc)) return rc;
6545
6546 ComObjPtr<USBController> ctrl;
6547 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6548 if (FAILED(rc)) return rc;
6549
6550 i_setModified(IsModified_USB);
6551 mUSBControllers.backup();
6552
6553 ctrl->i_unshare();
6554
6555 mUSBControllers->remove(ctrl);
6556
6557 /* inform the direct session if any */
6558 alock.release();
6559 i_onUSBControllerChange();
6560
6561 return S_OK;
6562}
6563
6564HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6565 ULONG *aOriginX,
6566 ULONG *aOriginY,
6567 ULONG *aWidth,
6568 ULONG *aHeight,
6569 BOOL *aEnabled)
6570{
6571 uint32_t u32OriginX= 0;
6572 uint32_t u32OriginY= 0;
6573 uint32_t u32Width = 0;
6574 uint32_t u32Height = 0;
6575 uint16_t u16Flags = 0;
6576
6577#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6578 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6579#else
6580 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6581#endif
6582 int vrc = readSavedGuestScreenInfo(SavedStateStream, mSSData->strStateFilePath, aScreenId,
6583 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6584 if (RT_FAILURE(vrc))
6585 {
6586#ifdef RT_OS_WINDOWS
6587 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6588 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6589 * So just assign fEnable to TRUE again.
6590 * The right fix would be to change GUI API wrappers to make sure that parameters
6591 * are changed only if API succeeds.
6592 */
6593 *aEnabled = TRUE;
6594#endif
6595 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6596 tr("Saved guest size is not available (%Rrc)"),
6597 vrc);
6598 }
6599
6600 *aOriginX = u32OriginX;
6601 *aOriginY = u32OriginY;
6602 *aWidth = u32Width;
6603 *aHeight = u32Height;
6604 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6605
6606 return S_OK;
6607}
6608
6609HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6610 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6611{
6612 if (aScreenId != 0)
6613 return E_NOTIMPL;
6614
6615 if ( aBitmapFormat != BitmapFormat_BGR0
6616 && aBitmapFormat != BitmapFormat_BGRA
6617 && aBitmapFormat != BitmapFormat_RGBA
6618 && aBitmapFormat != BitmapFormat_PNG)
6619 return setError(E_NOTIMPL,
6620 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6621
6622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6623
6624 uint8_t *pu8Data = NULL;
6625 uint32_t cbData = 0;
6626 uint32_t u32Width = 0;
6627 uint32_t u32Height = 0;
6628
6629#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6630 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6631#else
6632 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6633#endif
6634 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 0 /* u32Type */,
6635 &pu8Data, &cbData, &u32Width, &u32Height);
6636 if (RT_FAILURE(vrc))
6637 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6638 tr("Saved thumbnail data is not available (%Rrc)"),
6639 vrc);
6640
6641 HRESULT hr = S_OK;
6642
6643 *aWidth = u32Width;
6644 *aHeight = u32Height;
6645
6646 if (cbData > 0)
6647 {
6648 /* Convert pixels to the format expected by the API caller. */
6649 if (aBitmapFormat == BitmapFormat_BGR0)
6650 {
6651 /* [0] B, [1] G, [2] R, [3] 0. */
6652 aData.resize(cbData);
6653 memcpy(&aData.front(), pu8Data, cbData);
6654 }
6655 else if (aBitmapFormat == BitmapFormat_BGRA)
6656 {
6657 /* [0] B, [1] G, [2] R, [3] A. */
6658 aData.resize(cbData);
6659 for (uint32_t i = 0; i < cbData; i += 4)
6660 {
6661 aData[i] = pu8Data[i];
6662 aData[i + 1] = pu8Data[i + 1];
6663 aData[i + 2] = pu8Data[i + 2];
6664 aData[i + 3] = 0xff;
6665 }
6666 }
6667 else if (aBitmapFormat == BitmapFormat_RGBA)
6668 {
6669 /* [0] R, [1] G, [2] B, [3] A. */
6670 aData.resize(cbData);
6671 for (uint32_t i = 0; i < cbData; i += 4)
6672 {
6673 aData[i] = pu8Data[i + 2];
6674 aData[i + 1] = pu8Data[i + 1];
6675 aData[i + 2] = pu8Data[i];
6676 aData[i + 3] = 0xff;
6677 }
6678 }
6679 else if (aBitmapFormat == BitmapFormat_PNG)
6680 {
6681 uint8_t *pu8PNG = NULL;
6682 uint32_t cbPNG = 0;
6683 uint32_t cxPNG = 0;
6684 uint32_t cyPNG = 0;
6685
6686 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6687
6688 if (RT_SUCCESS(vrc))
6689 {
6690 aData.resize(cbPNG);
6691 if (cbPNG)
6692 memcpy(&aData.front(), pu8PNG, cbPNG);
6693 }
6694 else
6695 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6696 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6697 vrc);
6698
6699 RTMemFree(pu8PNG);
6700 }
6701 }
6702
6703 freeSavedDisplayScreenshot(pu8Data);
6704
6705 return hr;
6706}
6707
6708HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6709 ULONG *aWidth,
6710 ULONG *aHeight,
6711 std::vector<BitmapFormat_T> &aBitmapFormats)
6712{
6713 if (aScreenId != 0)
6714 return E_NOTIMPL;
6715
6716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6717
6718 uint8_t *pu8Data = NULL;
6719 uint32_t cbData = 0;
6720 uint32_t u32Width = 0;
6721 uint32_t u32Height = 0;
6722
6723#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6724 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6725#else
6726 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6727#endif
6728 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6729 &pu8Data, &cbData, &u32Width, &u32Height);
6730
6731 if (RT_FAILURE(vrc))
6732 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6733 tr("Saved screenshot data is not available (%Rrc)"),
6734 vrc);
6735
6736 *aWidth = u32Width;
6737 *aHeight = u32Height;
6738 aBitmapFormats.resize(1);
6739 aBitmapFormats[0] = BitmapFormat_PNG;
6740
6741 freeSavedDisplayScreenshot(pu8Data);
6742
6743 return S_OK;
6744}
6745
6746HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6747 BitmapFormat_T aBitmapFormat,
6748 ULONG *aWidth,
6749 ULONG *aHeight,
6750 std::vector<BYTE> &aData)
6751{
6752 if (aScreenId != 0)
6753 return E_NOTIMPL;
6754
6755 if (aBitmapFormat != BitmapFormat_PNG)
6756 return E_NOTIMPL;
6757
6758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6759
6760 uint8_t *pu8Data = NULL;
6761 uint32_t cbData = 0;
6762 uint32_t u32Width = 0;
6763 uint32_t u32Height = 0;
6764
6765#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6766 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6767#else
6768 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6769#endif
6770 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6771 &pu8Data, &cbData, &u32Width, &u32Height);
6772
6773 if (RT_FAILURE(vrc))
6774 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6775 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6776 vrc);
6777
6778 *aWidth = u32Width;
6779 *aHeight = u32Height;
6780
6781 aData.resize(cbData);
6782 if (cbData)
6783 memcpy(&aData.front(), pu8Data, cbData);
6784
6785 freeSavedDisplayScreenshot(pu8Data);
6786
6787 return S_OK;
6788}
6789
6790HRESULT Machine::hotPlugCPU(ULONG aCpu)
6791{
6792 HRESULT rc = S_OK;
6793 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6794
6795 if (!mHWData->mCPUHotPlugEnabled)
6796 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6797
6798 if (aCpu >= mHWData->mCPUCount)
6799 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6800
6801 if (mHWData->mCPUAttached[aCpu])
6802 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6803
6804 rc = i_checkStateDependency(MutableOrRunningStateDep);
6805 if (FAILED(rc)) return rc;
6806
6807 alock.release();
6808 rc = i_onCPUChange(aCpu, false);
6809 alock.acquire();
6810 if (FAILED(rc)) return rc;
6811
6812 i_setModified(IsModified_MachineData);
6813 mHWData.backup();
6814 mHWData->mCPUAttached[aCpu] = true;
6815
6816 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6817 if (Global::IsOnline(mData->mMachineState))
6818 i_saveSettings(NULL, alock);
6819
6820 return S_OK;
6821}
6822
6823HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6824{
6825 HRESULT rc = S_OK;
6826
6827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6828
6829 if (!mHWData->mCPUHotPlugEnabled)
6830 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6831
6832 if (aCpu >= SchemaDefs::MaxCPUCount)
6833 return setError(E_INVALIDARG,
6834 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6835 SchemaDefs::MaxCPUCount);
6836
6837 if (!mHWData->mCPUAttached[aCpu])
6838 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6839
6840 /* CPU 0 can't be detached */
6841 if (aCpu == 0)
6842 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6843
6844 rc = i_checkStateDependency(MutableOrRunningStateDep);
6845 if (FAILED(rc)) return rc;
6846
6847 alock.release();
6848 rc = i_onCPUChange(aCpu, true);
6849 alock.acquire();
6850 if (FAILED(rc)) return rc;
6851
6852 i_setModified(IsModified_MachineData);
6853 mHWData.backup();
6854 mHWData->mCPUAttached[aCpu] = false;
6855
6856 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6857 if (Global::IsOnline(mData->mMachineState))
6858 i_saveSettings(NULL, alock);
6859
6860 return S_OK;
6861}
6862
6863HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6864{
6865 *aAttached = false;
6866
6867 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6868
6869 /* If hotplug is enabled the CPU is always enabled. */
6870 if (!mHWData->mCPUHotPlugEnabled)
6871 {
6872 if (aCpu < mHWData->mCPUCount)
6873 *aAttached = true;
6874 }
6875 else
6876 {
6877 if (aCpu < SchemaDefs::MaxCPUCount)
6878 *aAttached = mHWData->mCPUAttached[aCpu];
6879 }
6880
6881 return S_OK;
6882}
6883
6884HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6885{
6886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6887
6888 Utf8Str log = i_getLogFilename(aIdx);
6889 if (!RTFileExists(log.c_str()))
6890 log.setNull();
6891 aFilename = log;
6892
6893 return S_OK;
6894}
6895
6896HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6897{
6898 if (aSize < 0)
6899 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6900
6901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6902
6903 HRESULT rc = S_OK;
6904 Utf8Str log = i_getLogFilename(aIdx);
6905
6906 /* do not unnecessarily hold the lock while doing something which does
6907 * not need the lock and potentially takes a long time. */
6908 alock.release();
6909
6910 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6911 * keeps the SOAP reply size under 1M for the webservice (we're using
6912 * base64 encoded strings for binary data for years now, avoiding the
6913 * expansion of each byte array element to approx. 25 bytes of XML. */
6914 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6915 aData.resize(cbData);
6916
6917 int vrc = VINF_SUCCESS;
6918 RTVFSIOSTREAM hVfsIosLog = NIL_RTVFSIOSTREAM;
6919
6920#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6921 if (mData->mstrLogKeyId.isNotEmpty() && mData->mstrLogKeyStore.isNotEmpty())
6922 {
6923 PCVBOXCRYPTOIF pCryptoIf = NULL;
6924 rc = i_getVirtualBox()->i_retainCryptoIf(&pCryptoIf);
6925 if (SUCCEEDED(rc))
6926 {
6927 alock.acquire();
6928
6929 SecretKey *pKey = NULL;
6930 vrc = mData->mpKeyStore->retainSecretKey(mData->mstrLogKeyId, &pKey);
6931 alock.release();
6932
6933 if (RT_SUCCESS(vrc))
6934 {
6935 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6936 if (RT_SUCCESS(vrc))
6937 {
6938 RTVFSIOSTREAM hVfsIosLogDec = NIL_RTVFSIOSTREAM;
6939 vrc = pCryptoIf->pfnCryptoIoStrmFromVfsIoStrmDecrypt(hVfsIosLog, mData->mstrLogKeyStore.c_str(),
6940 (const char *)pKey->getKeyBuffer(), &hVfsIosLogDec);
6941 if (RT_SUCCESS(vrc))
6942 {
6943 RTVfsIoStrmRelease(hVfsIosLog);
6944 hVfsIosLog = hVfsIosLogDec;
6945 }
6946 }
6947
6948 pKey->release();
6949 }
6950
6951 i_getVirtualBox()->i_releaseCryptoIf(pCryptoIf);
6952 }
6953 }
6954 else
6955 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6956#else
6957 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6958#endif
6959 if (RT_SUCCESS(vrc))
6960 {
6961 vrc = RTVfsIoStrmReadAt(hVfsIosLog, aOffset,
6962 cbData ? &aData.front() : NULL, cbData,
6963 true /*fBlocking*/, &cbData);
6964 if (RT_SUCCESS(vrc))
6965 aData.resize(cbData);
6966 else
6967 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6968 tr("Could not read log file '%s' (%Rrc)"),
6969 log.c_str(), vrc);
6970
6971 RTVfsIoStrmRelease(hVfsIosLog);
6972 }
6973 else
6974 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6975 tr("Could not open log file '%s' (%Rrc)"),
6976 log.c_str(), vrc);
6977
6978 if (FAILED(rc))
6979 aData.resize(0);
6980
6981 return rc;
6982}
6983
6984
6985/**
6986 * Currently this method doesn't attach device to the running VM,
6987 * just makes sure it's plugged on next VM start.
6988 */
6989HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6990{
6991 // lock scope
6992 {
6993 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6994
6995 HRESULT rc = i_checkStateDependency(MutableStateDep);
6996 if (FAILED(rc)) return rc;
6997
6998 ChipsetType_T aChipset = ChipsetType_PIIX3;
6999 COMGETTER(ChipsetType)(&aChipset);
7000
7001 if (aChipset != ChipsetType_ICH9)
7002 {
7003 return setError(E_INVALIDARG,
7004 tr("Host PCI attachment only supported with ICH9 chipset"));
7005 }
7006
7007 // check if device with this host PCI address already attached
7008 for (HWData::PCIDeviceAssignmentList::const_iterator
7009 it = mHWData->mPCIDeviceAssignments.begin();
7010 it != mHWData->mPCIDeviceAssignments.end();
7011 ++it)
7012 {
7013 LONG iHostAddress = -1;
7014 ComPtr<PCIDeviceAttachment> pAttach;
7015 pAttach = *it;
7016 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7017 if (iHostAddress == aHostAddress)
7018 return setError(E_INVALIDARG,
7019 tr("Device with host PCI address already attached to this VM"));
7020 }
7021
7022 ComObjPtr<PCIDeviceAttachment> pda;
7023 char name[32];
7024
7025 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
7026 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
7027 pda.createObject();
7028 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
7029 i_setModified(IsModified_MachineData);
7030 mHWData.backup();
7031 mHWData->mPCIDeviceAssignments.push_back(pda);
7032 }
7033
7034 return S_OK;
7035}
7036
7037/**
7038 * Currently this method doesn't detach device from the running VM,
7039 * just makes sure it's not plugged on next VM start.
7040 */
7041HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
7042{
7043 ComObjPtr<PCIDeviceAttachment> pAttach;
7044 bool fRemoved = false;
7045 HRESULT rc;
7046
7047 // lock scope
7048 {
7049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7050
7051 rc = i_checkStateDependency(MutableStateDep);
7052 if (FAILED(rc)) return rc;
7053
7054 for (HWData::PCIDeviceAssignmentList::const_iterator
7055 it = mHWData->mPCIDeviceAssignments.begin();
7056 it != mHWData->mPCIDeviceAssignments.end();
7057 ++it)
7058 {
7059 LONG iHostAddress = -1;
7060 pAttach = *it;
7061 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7062 if (iHostAddress != -1 && iHostAddress == aHostAddress)
7063 {
7064 i_setModified(IsModified_MachineData);
7065 mHWData.backup();
7066 mHWData->mPCIDeviceAssignments.remove(pAttach);
7067 fRemoved = true;
7068 break;
7069 }
7070 }
7071 }
7072
7073
7074 /* Fire event outside of the lock */
7075 if (fRemoved)
7076 {
7077 Assert(!pAttach.isNull());
7078 ComPtr<IEventSource> es;
7079 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7080 Assert(SUCCEEDED(rc));
7081 Bstr mid;
7082 rc = this->COMGETTER(Id)(mid.asOutParam());
7083 Assert(SUCCEEDED(rc));
7084 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7085 }
7086
7087 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7088 tr("No host PCI device %08x attached"),
7089 aHostAddress
7090 );
7091}
7092
7093HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
7094{
7095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7096
7097 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
7098 size_t i = 0;
7099 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
7100 it = mHWData->mPCIDeviceAssignments.begin();
7101 it != mHWData->mPCIDeviceAssignments.end();
7102 ++it, ++i)
7103 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
7104
7105 return S_OK;
7106}
7107
7108HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
7109{
7110 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
7111
7112 return S_OK;
7113}
7114
7115HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
7116{
7117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7118
7119 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
7120
7121 return S_OK;
7122}
7123
7124HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
7125{
7126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7127 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7128 if (SUCCEEDED(hrc))
7129 {
7130 hrc = mHWData.backupEx();
7131 if (SUCCEEDED(hrc))
7132 {
7133 i_setModified(IsModified_MachineData);
7134 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
7135 }
7136 }
7137 return hrc;
7138}
7139
7140HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
7141{
7142 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7143 aTracingConfig = mHWData->mDebugging.strTracingConfig;
7144 return S_OK;
7145}
7146
7147HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
7148{
7149 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7150 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7151 if (SUCCEEDED(hrc))
7152 {
7153 hrc = mHWData.backupEx();
7154 if (SUCCEEDED(hrc))
7155 {
7156 mHWData->mDebugging.strTracingConfig = aTracingConfig;
7157 if (SUCCEEDED(hrc))
7158 i_setModified(IsModified_MachineData);
7159 }
7160 }
7161 return hrc;
7162}
7163
7164HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
7165{
7166 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7167
7168 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
7169
7170 return S_OK;
7171}
7172
7173HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
7174{
7175 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7176 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7177 if (SUCCEEDED(hrc))
7178 {
7179 hrc = mHWData.backupEx();
7180 if (SUCCEEDED(hrc))
7181 {
7182 i_setModified(IsModified_MachineData);
7183 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
7184 }
7185 }
7186 return hrc;
7187}
7188
7189HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7190{
7191 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7192
7193 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7194
7195 return S_OK;
7196}
7197
7198HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7199{
7200 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7201
7202 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7203 if ( SUCCEEDED(hrc)
7204 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7205 {
7206 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7207 int vrc;
7208
7209 if (aAutostartEnabled)
7210 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7211 else
7212 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7213
7214 if (RT_SUCCESS(vrc))
7215 {
7216 hrc = mHWData.backupEx();
7217 if (SUCCEEDED(hrc))
7218 {
7219 i_setModified(IsModified_MachineData);
7220 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7221 }
7222 }
7223 else if (vrc == VERR_NOT_SUPPORTED)
7224 hrc = setError(VBOX_E_NOT_SUPPORTED,
7225 tr("The VM autostart feature is not supported on this platform"));
7226 else if (vrc == VERR_PATH_NOT_FOUND)
7227 hrc = setError(E_FAIL,
7228 tr("The path to the autostart database is not set"));
7229 else
7230 hrc = setError(E_UNEXPECTED,
7231 aAutostartEnabled ?
7232 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
7233 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
7234 mUserData->s.strName.c_str(), vrc);
7235 }
7236 return hrc;
7237}
7238
7239HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7240{
7241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7242
7243 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7244
7245 return S_OK;
7246}
7247
7248HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7249{
7250 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7251 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7252 if (SUCCEEDED(hrc))
7253 {
7254 hrc = mHWData.backupEx();
7255 if (SUCCEEDED(hrc))
7256 {
7257 i_setModified(IsModified_MachineData);
7258 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7259 }
7260 }
7261 return hrc;
7262}
7263
7264HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7265{
7266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7267
7268 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7269
7270 return S_OK;
7271}
7272
7273HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7274{
7275 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7276 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7277 if ( SUCCEEDED(hrc)
7278 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7279 {
7280 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7281 int vrc;
7282
7283 if (aAutostopType != AutostopType_Disabled)
7284 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7285 else
7286 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7287
7288 if (RT_SUCCESS(vrc))
7289 {
7290 hrc = mHWData.backupEx();
7291 if (SUCCEEDED(hrc))
7292 {
7293 i_setModified(IsModified_MachineData);
7294 mHWData->mAutostart.enmAutostopType = aAutostopType;
7295 }
7296 }
7297 else if (vrc == VERR_NOT_SUPPORTED)
7298 hrc = setError(VBOX_E_NOT_SUPPORTED,
7299 tr("The VM autostop feature is not supported on this platform"));
7300 else if (vrc == VERR_PATH_NOT_FOUND)
7301 hrc = setError(E_FAIL,
7302 tr("The path to the autostart database is not set"));
7303 else
7304 hrc = setError(E_UNEXPECTED,
7305 aAutostopType != AutostopType_Disabled ?
7306 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
7307 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
7308 mUserData->s.strName.c_str(), vrc);
7309 }
7310 return hrc;
7311}
7312
7313HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7314{
7315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7316
7317 aDefaultFrontend = mHWData->mDefaultFrontend;
7318
7319 return S_OK;
7320}
7321
7322HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7323{
7324 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7325 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7326 if (SUCCEEDED(hrc))
7327 {
7328 hrc = mHWData.backupEx();
7329 if (SUCCEEDED(hrc))
7330 {
7331 i_setModified(IsModified_MachineData);
7332 mHWData->mDefaultFrontend = aDefaultFrontend;
7333 }
7334 }
7335 return hrc;
7336}
7337
7338HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7339{
7340 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7341 size_t cbIcon = mUserData->s.ovIcon.size();
7342 aIcon.resize(cbIcon);
7343 if (cbIcon)
7344 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7345 return S_OK;
7346}
7347
7348HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7349{
7350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7351 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7352 if (SUCCEEDED(hrc))
7353 {
7354 i_setModified(IsModified_MachineData);
7355 mUserData.backup();
7356 size_t cbIcon = aIcon.size();
7357 mUserData->s.ovIcon.resize(cbIcon);
7358 if (cbIcon)
7359 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7360 }
7361 return hrc;
7362}
7363
7364HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7365{
7366#ifdef VBOX_WITH_USB
7367 *aUSBProxyAvailable = true;
7368#else
7369 *aUSBProxyAvailable = false;
7370#endif
7371 return S_OK;
7372}
7373
7374HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
7375{
7376 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7377
7378 *aVMProcessPriority = mUserData->s.enmVMPriority;
7379
7380 return S_OK;
7381}
7382
7383HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
7384{
7385 RT_NOREF(aVMProcessPriority);
7386 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7387 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7388 if (SUCCEEDED(hrc))
7389 {
7390 hrc = mUserData.backupEx();
7391 if (SUCCEEDED(hrc))
7392 {
7393 i_setModified(IsModified_MachineData);
7394 mUserData->s.enmVMPriority = aVMProcessPriority;
7395 }
7396 }
7397 alock.release();
7398 if (SUCCEEDED(hrc))
7399 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
7400 return hrc;
7401}
7402
7403HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7404 ComPtr<IProgress> &aProgress)
7405{
7406 ComObjPtr<Progress> pP;
7407 Progress *ppP = pP;
7408 IProgress *iP = static_cast<IProgress *>(ppP);
7409 IProgress **pProgress = &iP;
7410
7411 IMachine *pTarget = aTarget;
7412
7413 /* Convert the options. */
7414 RTCList<CloneOptions_T> optList;
7415 if (aOptions.size())
7416 for (size_t i = 0; i < aOptions.size(); ++i)
7417 optList.append(aOptions[i]);
7418
7419 if (optList.contains(CloneOptions_Link))
7420 {
7421 if (!i_isSnapshotMachine())
7422 return setError(E_INVALIDARG,
7423 tr("Linked clone can only be created from a snapshot"));
7424 if (aMode != CloneMode_MachineState)
7425 return setError(E_INVALIDARG,
7426 tr("Linked clone can only be created for a single machine state"));
7427 }
7428 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7429
7430 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7431
7432 HRESULT rc = pWorker->start(pProgress);
7433
7434 pP = static_cast<Progress *>(*pProgress);
7435 pP.queryInterfaceTo(aProgress.asOutParam());
7436
7437 return rc;
7438
7439}
7440
7441HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7442 const com::Utf8Str &aType,
7443 ComPtr<IProgress> &aProgress)
7444{
7445 LogFlowThisFuncEnter();
7446
7447 ComObjPtr<Progress> ptrProgress;
7448 HRESULT hrc = ptrProgress.createObject();
7449 if (SUCCEEDED(hrc))
7450 {
7451 com::Utf8Str strDefaultPath;
7452 if (aTargetPath.isEmpty())
7453 i_calculateFullPath(".", strDefaultPath);
7454
7455 /* Initialize our worker task */
7456 MachineMoveVM *pTask = NULL;
7457 try
7458 {
7459 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7460 }
7461 catch (std::bad_alloc &)
7462 {
7463 return E_OUTOFMEMORY;
7464 }
7465
7466 hrc = pTask->init();//no exceptions are thrown
7467
7468 if (SUCCEEDED(hrc))
7469 {
7470 hrc = pTask->createThread();
7471 pTask = NULL; /* Consumed by createThread(). */
7472 if (SUCCEEDED(hrc))
7473 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7474 else
7475 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7476 }
7477 else
7478 delete pTask;
7479 }
7480
7481 LogFlowThisFuncLeave();
7482 return hrc;
7483
7484}
7485
7486HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7487{
7488 NOREF(aProgress);
7489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7490
7491 // This check should always fail.
7492 HRESULT rc = i_checkStateDependency(MutableStateDep);
7493 if (FAILED(rc)) return rc;
7494
7495 AssertFailedReturn(E_NOTIMPL);
7496}
7497
7498HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7499{
7500 NOREF(aSavedStateFile);
7501 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7502
7503 // This check should always fail.
7504 HRESULT rc = i_checkStateDependency(MutableStateDep);
7505 if (FAILED(rc)) return rc;
7506
7507 AssertFailedReturn(E_NOTIMPL);
7508}
7509
7510HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7511{
7512 NOREF(aFRemoveFile);
7513 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7514
7515 // This check should always fail.
7516 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7517 if (FAILED(rc)) return rc;
7518
7519 AssertFailedReturn(E_NOTIMPL);
7520}
7521
7522// public methods for internal purposes
7523/////////////////////////////////////////////////////////////////////////////
7524
7525/**
7526 * Adds the given IsModified_* flag to the dirty flags of the machine.
7527 * This must be called either during i_loadSettings or under the machine write lock.
7528 * @param fl Flag
7529 * @param fAllowStateModification If state modifications are allowed.
7530 */
7531void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7532{
7533 mData->flModifications |= fl;
7534 if (fAllowStateModification && i_isStateModificationAllowed())
7535 mData->mCurrentStateModified = true;
7536}
7537
7538/**
7539 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7540 * care of the write locking.
7541 *
7542 * @param fModification The flag to add.
7543 * @param fAllowStateModification If state modifications are allowed.
7544 */
7545void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7546{
7547 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7548 i_setModified(fModification, fAllowStateModification);
7549}
7550
7551/**
7552 * Saves the registry entry of this machine to the given configuration node.
7553 *
7554 * @param data Machine registry data.
7555 *
7556 * @note locks this object for reading.
7557 */
7558HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7559{
7560 AutoLimitedCaller autoCaller(this);
7561 AssertComRCReturnRC(autoCaller.rc());
7562
7563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7564
7565 data.uuid = mData->mUuid;
7566 data.strSettingsFile = mData->m_strConfigFile;
7567
7568 return S_OK;
7569}
7570
7571/**
7572 * Calculates the absolute path of the given path taking the directory of the
7573 * machine settings file as the current directory.
7574 *
7575 * @param strPath Path to calculate the absolute path for.
7576 * @param aResult Where to put the result (used only on success, can be the
7577 * same Utf8Str instance as passed in @a aPath).
7578 * @return IPRT result.
7579 *
7580 * @note Locks this object for reading.
7581 */
7582int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7583{
7584 AutoCaller autoCaller(this);
7585 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7586
7587 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7588
7589 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7590
7591 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7592
7593 strSettingsDir.stripFilename();
7594 char szFolder[RTPATH_MAX];
7595 size_t cbFolder = sizeof(szFolder);
7596 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7597 if (RT_SUCCESS(vrc))
7598 aResult = szFolder;
7599
7600 return vrc;
7601}
7602
7603/**
7604 * Copies strSource to strTarget, making it relative to the machine folder
7605 * if it is a subdirectory thereof, or simply copying it otherwise.
7606 *
7607 * @param strSource Path to evaluate and copy.
7608 * @param strTarget Buffer to receive target path.
7609 *
7610 * @note Locks this object for reading.
7611 */
7612void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7613 Utf8Str &strTarget)
7614{
7615 AutoCaller autoCaller(this);
7616 AssertComRCReturn(autoCaller.rc(), (void)0);
7617
7618 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7619
7620 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7621 // use strTarget as a temporary buffer to hold the machine settings dir
7622 strTarget = mData->m_strConfigFileFull;
7623 strTarget.stripFilename();
7624 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7625 {
7626 // is relative: then append what's left
7627 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7628 // for empty paths (only possible for subdirs) use "." to avoid
7629 // triggering default settings for not present config attributes.
7630 if (strTarget.isEmpty())
7631 strTarget = ".";
7632 }
7633 else
7634 // is not relative: then overwrite
7635 strTarget = strSource;
7636}
7637
7638/**
7639 * Returns the full path to the machine's log folder in the
7640 * \a aLogFolder argument.
7641 */
7642void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7643{
7644 AutoCaller autoCaller(this);
7645 AssertComRCReturnVoid(autoCaller.rc());
7646
7647 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7648
7649 char szTmp[RTPATH_MAX];
7650 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7651 if (RT_SUCCESS(vrc))
7652 {
7653 if (szTmp[0] && !mUserData.isNull())
7654 {
7655 char szTmp2[RTPATH_MAX];
7656 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7657 if (RT_SUCCESS(vrc))
7658 aLogFolder.printf("%s%c%s",
7659 szTmp2,
7660 RTPATH_DELIMITER,
7661 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7662 }
7663 else
7664 vrc = VERR_PATH_IS_RELATIVE;
7665 }
7666
7667 if (RT_FAILURE(vrc))
7668 {
7669 // fallback if VBOX_USER_LOGHOME is not set or invalid
7670 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7671 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7672 aLogFolder.append(RTPATH_DELIMITER);
7673 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7674 }
7675}
7676
7677/**
7678 * Returns the full path to the machine's log file for an given index.
7679 */
7680Utf8Str Machine::i_getLogFilename(ULONG idx)
7681{
7682 Utf8Str logFolder;
7683 getLogFolder(logFolder);
7684 Assert(logFolder.length());
7685
7686 Utf8Str log;
7687 if (idx == 0)
7688 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7689#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7690 else if (idx == 1)
7691 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7692 else
7693 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7694#else
7695 else
7696 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7697#endif
7698 return log;
7699}
7700
7701/**
7702 * Returns the full path to the machine's hardened log file.
7703 */
7704Utf8Str Machine::i_getHardeningLogFilename(void)
7705{
7706 Utf8Str strFilename;
7707 getLogFolder(strFilename);
7708 Assert(strFilename.length());
7709 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7710 return strFilename;
7711}
7712
7713/**
7714 * Returns the default NVRAM filename based on the location of the VM config.
7715 * Note that this is a relative path.
7716 */
7717Utf8Str Machine::i_getDefaultNVRAMFilename()
7718{
7719 AutoCaller autoCaller(this);
7720 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7721
7722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7723
7724 if (i_isSnapshotMachine())
7725 return Utf8Str::Empty;
7726
7727 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7728 strNVRAMFilePath.stripPath();
7729 strNVRAMFilePath.stripSuffix();
7730 strNVRAMFilePath += ".nvram";
7731
7732 return strNVRAMFilePath;
7733}
7734
7735/**
7736 * Returns the NVRAM filename for a new snapshot. This intentionally works
7737 * similarly to the saved state file naming. Note that this is usually
7738 * a relative path, unless the snapshot folder is absolute.
7739 */
7740Utf8Str Machine::i_getSnapshotNVRAMFilename()
7741{
7742 AutoCaller autoCaller(this);
7743 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7744
7745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7746
7747 RTTIMESPEC ts;
7748 RTTimeNow(&ts);
7749 RTTIME time;
7750 RTTimeExplode(&time, &ts);
7751
7752 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7753 strNVRAMFilePath += RTPATH_DELIMITER;
7754 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7755 time.i32Year, time.u8Month, time.u8MonthDay,
7756 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7757
7758 return strNVRAMFilePath;
7759}
7760
7761/**
7762 * Returns the version of the settings file.
7763 */
7764SettingsVersion_T Machine::i_getSettingsVersion(void)
7765{
7766 AutoCaller autoCaller(this);
7767 AssertComRCReturn(autoCaller.rc(), SettingsVersion_Null);
7768
7769 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7770
7771 return mData->pMachineConfigFile->getSettingsVersion();
7772}
7773
7774/**
7775 * Composes a unique saved state filename based on the current system time. The filename is
7776 * granular to the second so this will work so long as no more than one snapshot is taken on
7777 * a machine per second.
7778 *
7779 * Before version 4.1, we used this formula for saved state files:
7780 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7781 * which no longer works because saved state files can now be shared between the saved state of the
7782 * "saved" machine and an online snapshot, and the following would cause problems:
7783 * 1) save machine
7784 * 2) create online snapshot from that machine state --> reusing saved state file
7785 * 3) save machine again --> filename would be reused, breaking the online snapshot
7786 *
7787 * So instead we now use a timestamp.
7788 *
7789 * @param strStateFilePath
7790 */
7791
7792void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7793{
7794 AutoCaller autoCaller(this);
7795 AssertComRCReturnVoid(autoCaller.rc());
7796
7797 {
7798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7799 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7800 }
7801
7802 RTTIMESPEC ts;
7803 RTTimeNow(&ts);
7804 RTTIME time;
7805 RTTimeExplode(&time, &ts);
7806
7807 strStateFilePath += RTPATH_DELIMITER;
7808 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7809 time.i32Year, time.u8Month, time.u8MonthDay,
7810 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7811}
7812
7813/**
7814 * Returns whether at least one USB controller is present for the VM.
7815 */
7816bool Machine::i_isUSBControllerPresent()
7817{
7818 AutoCaller autoCaller(this);
7819 AssertComRCReturn(autoCaller.rc(), false);
7820
7821 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7822
7823 return (mUSBControllers->size() > 0);
7824}
7825
7826
7827/**
7828 * @note Locks this object for writing, calls the client process
7829 * (inside the lock).
7830 */
7831HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7832 const Utf8Str &strFrontend,
7833 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7834 ProgressProxy *aProgress)
7835{
7836 LogFlowThisFuncEnter();
7837
7838 AssertReturn(aControl, E_FAIL);
7839 AssertReturn(aProgress, E_FAIL);
7840 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7841
7842 AutoCaller autoCaller(this);
7843 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7844
7845 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7846
7847 if (!mData->mRegistered)
7848 return setError(E_UNEXPECTED,
7849 tr("The machine '%s' is not registered"),
7850 mUserData->s.strName.c_str());
7851
7852 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7853
7854 /* The process started when launching a VM with separate UI/VM processes is always
7855 * the UI process, i.e. needs special handling as it won't claim the session. */
7856 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7857
7858 if (fSeparate)
7859 {
7860 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7861 return setError(VBOX_E_INVALID_OBJECT_STATE,
7862 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7863 mUserData->s.strName.c_str());
7864 }
7865 else
7866 {
7867 if ( mData->mSession.mState == SessionState_Locked
7868 || mData->mSession.mState == SessionState_Spawning
7869 || mData->mSession.mState == SessionState_Unlocking)
7870 return setError(VBOX_E_INVALID_OBJECT_STATE,
7871 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7872 mUserData->s.strName.c_str());
7873
7874 /* may not be busy */
7875 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7876 }
7877
7878 /* Hardening logging */
7879#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7880 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7881 {
7882 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7883 int vrc2;
7884 /* ignore rc */ i_deleteFile(strHardeningLogFile, false /* fIgnoreFailures */, tr("hardening log file"), &vrc2);
7885 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7886 {
7887 Utf8Str strStartupLogDir = strHardeningLogFile;
7888 strStartupLogDir.stripFilename();
7889 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7890 file without stripping the file. */
7891 }
7892 strSupHardeningLogArg.append(strHardeningLogFile);
7893
7894 /* Remove legacy log filename to avoid confusion. */
7895 Utf8Str strOldStartupLogFile;
7896 getLogFolder(strOldStartupLogFile);
7897 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7898 i_deleteFile(strOldStartupLogFile, true /* fIgnoreFailures */);
7899 }
7900#else
7901 Utf8Str strSupHardeningLogArg;
7902#endif
7903
7904 Utf8Str strAppOverride;
7905#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7906 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7907#endif
7908
7909 bool fUseVBoxSDS = false;
7910 Utf8Str strCanonicalName;
7911 if (false)
7912 { }
7913#ifdef VBOX_WITH_QTGUI
7914 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7915 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7916 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7917 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7918 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7919 {
7920 strCanonicalName = "GUI/Qt";
7921 fUseVBoxSDS = true;
7922 }
7923#endif
7924#ifdef VBOX_WITH_VBOXSDL
7925 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7926 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7927 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7928 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7929 {
7930 strCanonicalName = "GUI/SDL";
7931 fUseVBoxSDS = true;
7932 }
7933#endif
7934#ifdef VBOX_WITH_HEADLESS
7935 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7936 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7937 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7938 {
7939 strCanonicalName = "headless";
7940 }
7941#endif
7942 else
7943 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7944
7945 Utf8Str idStr = mData->mUuid.toString();
7946 Utf8Str const &strMachineName = mUserData->s.strName;
7947 RTPROCESS pid = NIL_RTPROCESS;
7948
7949#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7950 RT_NOREF(fUseVBoxSDS);
7951#else
7952 DWORD idCallerSession = ~(DWORD)0;
7953 if (fUseVBoxSDS)
7954 {
7955 /*
7956 * The VBoxSDS should be used for process launching the VM with
7957 * GUI only if the caller and the VBoxSDS are in different Windows
7958 * sessions and the caller in the interactive one.
7959 */
7960 fUseVBoxSDS = false;
7961
7962 /* Get windows session of the current process. The process token used
7963 due to several reasons:
7964 1. The token is absent for the current thread except someone set it
7965 for us.
7966 2. Needs to get the id of the session where the process is started.
7967 We only need to do this once, though. */
7968 static DWORD s_idCurrentSession = ~(DWORD)0;
7969 DWORD idCurrentSession = s_idCurrentSession;
7970 if (idCurrentSession == ~(DWORD)0)
7971 {
7972 HANDLE hCurrentProcessToken = NULL;
7973 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7974 {
7975 DWORD cbIgn = 0;
7976 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7977 s_idCurrentSession = idCurrentSession;
7978 else
7979 {
7980 idCurrentSession = ~(DWORD)0;
7981 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7982 }
7983 CloseHandle(hCurrentProcessToken);
7984 }
7985 else
7986 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7987 }
7988
7989 /* get the caller's session */
7990 HRESULT hrc = CoImpersonateClient();
7991 if (SUCCEEDED(hrc))
7992 {
7993 HANDLE hCallerThreadToken;
7994 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7995 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7996 &hCallerThreadToken))
7997 {
7998 SetLastError(NO_ERROR);
7999 DWORD cbIgn = 0;
8000 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
8001 {
8002 /* Only need to use SDS if the session ID differs: */
8003 if (idCurrentSession != idCallerSession)
8004 {
8005 fUseVBoxSDS = false;
8006
8007 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
8008 DWORD cbTokenGroups = 0;
8009 PTOKEN_GROUPS pTokenGroups = NULL;
8010 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
8011 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
8012 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
8013 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
8014 {
8015 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
8016 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
8017 PSID pInteractiveSid = NULL;
8018 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
8019 {
8020 /* Iterate over the groups looking for the interactive SID: */
8021 fUseVBoxSDS = false;
8022 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
8023 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
8024 {
8025 fUseVBoxSDS = true;
8026 break;
8027 }
8028 FreeSid(pInteractiveSid);
8029 }
8030 }
8031 else
8032 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
8033 RTMemTmpFree(pTokenGroups);
8034 }
8035 }
8036 else
8037 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
8038 CloseHandle(hCallerThreadToken);
8039 }
8040 else
8041 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
8042 CoRevertToSelf();
8043 }
8044 else
8045 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
8046 }
8047 if (fUseVBoxSDS)
8048 {
8049 /* connect to VBoxSDS */
8050 ComPtr<IVirtualBoxSDS> pVBoxSDS;
8051 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
8052 if (FAILED(rc))
8053 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
8054 strMachineName.c_str());
8055
8056 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
8057 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
8058 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
8059 service to access the files. */
8060 rc = CoSetProxyBlanket(pVBoxSDS,
8061 RPC_C_AUTHN_DEFAULT,
8062 RPC_C_AUTHZ_DEFAULT,
8063 COLE_DEFAULT_PRINCIPAL,
8064 RPC_C_AUTHN_LEVEL_DEFAULT,
8065 RPC_C_IMP_LEVEL_IMPERSONATE,
8066 NULL,
8067 EOAC_DEFAULT);
8068 if (FAILED(rc))
8069 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
8070
8071 size_t const cEnvVars = aEnvironmentChanges.size();
8072 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
8073 for (size_t i = 0; i < cEnvVars; i++)
8074 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
8075
8076 ULONG uPid = 0;
8077 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
8078 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
8079 idCallerSession, &uPid);
8080 if (FAILED(rc))
8081 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
8082 pid = (RTPROCESS)uPid;
8083 }
8084 else
8085#endif /* VBOX_WITH_SDS && RT_OS_WINDOWS */
8086 {
8087 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
8088 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
8089 if (RT_FAILURE(vrc))
8090 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
8091 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
8092 }
8093
8094 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
8095 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
8096
8097 if (!fSeparate)
8098 {
8099 /*
8100 * Note that we don't release the lock here before calling the client,
8101 * because it doesn't need to call us back if called with a NULL argument.
8102 * Releasing the lock here is dangerous because we didn't prepare the
8103 * launch data yet, but the client we've just started may happen to be
8104 * too fast and call LockMachine() that will fail (because of PID, etc.),
8105 * so that the Machine will never get out of the Spawning session state.
8106 */
8107
8108 /* inform the session that it will be a remote one */
8109 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8110#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8111 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8112#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8113 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8114#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8115 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8116
8117 if (FAILED(rc))
8118 {
8119 /* restore the session state */
8120 mData->mSession.mState = SessionState_Unlocked;
8121 alock.release();
8122 mParent->i_addProcessToReap(pid);
8123 /* The failure may occur w/o any error info (from RPC), so provide one */
8124 return setError(VBOX_E_VM_ERROR,
8125 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8126 }
8127
8128 /* attach launch data to the machine */
8129 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8130 mData->mSession.mRemoteControls.push_back(aControl);
8131 mData->mSession.mProgress = aProgress;
8132 mData->mSession.mPID = pid;
8133 mData->mSession.mState = SessionState_Spawning;
8134 Assert(strCanonicalName.isNotEmpty());
8135 mData->mSession.mName = strCanonicalName;
8136 }
8137 else
8138 {
8139 /* For separate UI process we declare the launch as completed instantly, as the
8140 * actual headless VM start may or may not come. No point in remembering anything
8141 * yet, as what matters for us is when the headless VM gets started. */
8142 aProgress->i_notifyComplete(S_OK);
8143 }
8144
8145 alock.release();
8146 mParent->i_addProcessToReap(pid);
8147
8148 LogFlowThisFuncLeave();
8149 return S_OK;
8150}
8151
8152/**
8153 * Returns @c true if the given session machine instance has an open direct
8154 * session (and optionally also for direct sessions which are closing) and
8155 * returns the session control machine instance if so.
8156 *
8157 * Note that when the method returns @c false, the arguments remain unchanged.
8158 *
8159 * @param aMachine Session machine object.
8160 * @param aControl Direct session control object (optional).
8161 * @param aRequireVM If true then only allow VM sessions.
8162 * @param aAllowClosing If true then additionally a session which is currently
8163 * being closed will also be allowed.
8164 *
8165 * @note locks this object for reading.
8166 */
8167bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8168 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8169 bool aRequireVM /*= false*/,
8170 bool aAllowClosing /*= false*/)
8171{
8172 AutoLimitedCaller autoCaller(this);
8173 AssertComRCReturn(autoCaller.rc(), false);
8174
8175 /* just return false for inaccessible machines */
8176 if (getObjectState().getState() != ObjectState::Ready)
8177 return false;
8178
8179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8180
8181 if ( ( mData->mSession.mState == SessionState_Locked
8182 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
8183 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8184 )
8185 {
8186 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8187
8188 aMachine = mData->mSession.mMachine;
8189
8190 if (aControl != NULL)
8191 *aControl = mData->mSession.mDirectControl;
8192
8193 return true;
8194 }
8195
8196 return false;
8197}
8198
8199/**
8200 * Returns @c true if the given machine has an spawning direct session.
8201 *
8202 * @note locks this object for reading.
8203 */
8204bool Machine::i_isSessionSpawning()
8205{
8206 AutoLimitedCaller autoCaller(this);
8207 AssertComRCReturn(autoCaller.rc(), false);
8208
8209 /* just return false for inaccessible machines */
8210 if (getObjectState().getState() != ObjectState::Ready)
8211 return false;
8212
8213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8214
8215 if (mData->mSession.mState == SessionState_Spawning)
8216 return true;
8217
8218 return false;
8219}
8220
8221/**
8222 * Called from the client watcher thread to check for unexpected client process
8223 * death during Session_Spawning state (e.g. before it successfully opened a
8224 * direct session).
8225 *
8226 * On Win32 and on OS/2, this method is called only when we've got the
8227 * direct client's process termination notification, so it always returns @c
8228 * true.
8229 *
8230 * On other platforms, this method returns @c true if the client process is
8231 * terminated and @c false if it's still alive.
8232 *
8233 * @note Locks this object for writing.
8234 */
8235bool Machine::i_checkForSpawnFailure()
8236{
8237 AutoCaller autoCaller(this);
8238 if (!autoCaller.isOk())
8239 {
8240 /* nothing to do */
8241 LogFlowThisFunc(("Already uninitialized!\n"));
8242 return true;
8243 }
8244
8245 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8246
8247 if (mData->mSession.mState != SessionState_Spawning)
8248 {
8249 /* nothing to do */
8250 LogFlowThisFunc(("Not spawning any more!\n"));
8251 return true;
8252 }
8253
8254 HRESULT rc = S_OK;
8255
8256 /* PID not yet initialized, skip check. */
8257 if (mData->mSession.mPID == NIL_RTPROCESS)
8258 return false;
8259
8260 RTPROCSTATUS status;
8261 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8262
8263 if (vrc != VERR_PROCESS_RUNNING)
8264 {
8265 Utf8Str strExtraInfo;
8266
8267#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8268 /* If the startup logfile exists and is of non-zero length, tell the
8269 user to look there for more details to encourage them to attach it
8270 when reporting startup issues. */
8271 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8272 uint64_t cbStartupLogFile = 0;
8273 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
8274 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8275 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
8276#endif
8277
8278 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8279 rc = setError(E_FAIL,
8280 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8281 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8282 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8283 rc = setError(E_FAIL,
8284 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8285 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8286 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8287 rc = setError(E_FAIL,
8288 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8289 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8290 else
8291 rc = setErrorBoth(E_FAIL, vrc,
8292 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8293 i_getName().c_str(), vrc, strExtraInfo.c_str());
8294 }
8295
8296 if (FAILED(rc))
8297 {
8298 /* Close the remote session, remove the remote control from the list
8299 * and reset session state to Closed (@note keep the code in sync with
8300 * the relevant part in LockMachine()). */
8301
8302 Assert(mData->mSession.mRemoteControls.size() == 1);
8303 if (mData->mSession.mRemoteControls.size() == 1)
8304 {
8305 ErrorInfoKeeper eik;
8306 mData->mSession.mRemoteControls.front()->Uninitialize();
8307 }
8308
8309 mData->mSession.mRemoteControls.clear();
8310 mData->mSession.mState = SessionState_Unlocked;
8311
8312 /* finalize the progress after setting the state */
8313 if (!mData->mSession.mProgress.isNull())
8314 {
8315 mData->mSession.mProgress->notifyComplete(rc);
8316 mData->mSession.mProgress.setNull();
8317 }
8318
8319 mData->mSession.mPID = NIL_RTPROCESS;
8320
8321 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
8322 return true;
8323 }
8324
8325 return false;
8326}
8327
8328/**
8329 * Checks whether the machine can be registered. If so, commits and saves
8330 * all settings.
8331 *
8332 * @note Must be called from mParent's write lock. Locks this object and
8333 * children for writing.
8334 */
8335HRESULT Machine::i_prepareRegister()
8336{
8337 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8338
8339 AutoLimitedCaller autoCaller(this);
8340 AssertComRCReturnRC(autoCaller.rc());
8341
8342 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8343
8344 /* wait for state dependents to drop to zero */
8345 i_ensureNoStateDependencies(alock);
8346
8347 if (!mData->mAccessible)
8348 return setError(VBOX_E_INVALID_OBJECT_STATE,
8349 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8350 mUserData->s.strName.c_str(),
8351 mData->mUuid.toString().c_str());
8352
8353 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8354
8355 if (mData->mRegistered)
8356 return setError(VBOX_E_INVALID_OBJECT_STATE,
8357 tr("The machine '%s' with UUID {%s} is already registered"),
8358 mUserData->s.strName.c_str(),
8359 mData->mUuid.toString().c_str());
8360
8361 HRESULT rc = S_OK;
8362
8363 // Ensure the settings are saved. If we are going to be registered and
8364 // no config file exists yet, create it by calling i_saveSettings() too.
8365 if ( (mData->flModifications)
8366 || (!mData->pMachineConfigFile->fileExists())
8367 )
8368 {
8369 rc = i_saveSettings(NULL, alock);
8370 // no need to check whether VirtualBox.xml needs saving too since
8371 // we can't have a machine XML file rename pending
8372 if (FAILED(rc)) return rc;
8373 }
8374
8375 /* more config checking goes here */
8376
8377 if (SUCCEEDED(rc))
8378 {
8379 /* we may have had implicit modifications we want to fix on success */
8380 i_commit();
8381
8382 mData->mRegistered = true;
8383 }
8384 else
8385 {
8386 /* we may have had implicit modifications we want to cancel on failure*/
8387 i_rollback(false /* aNotify */);
8388 }
8389
8390 return rc;
8391}
8392
8393/**
8394 * Increases the number of objects dependent on the machine state or on the
8395 * registered state. Guarantees that these two states will not change at least
8396 * until #i_releaseStateDependency() is called.
8397 *
8398 * Depending on the @a aDepType value, additional state checks may be made.
8399 * These checks will set extended error info on failure. See
8400 * #i_checkStateDependency() for more info.
8401 *
8402 * If this method returns a failure, the dependency is not added and the caller
8403 * is not allowed to rely on any particular machine state or registration state
8404 * value and may return the failed result code to the upper level.
8405 *
8406 * @param aDepType Dependency type to add.
8407 * @param aState Current machine state (NULL if not interested).
8408 * @param aRegistered Current registered state (NULL if not interested).
8409 *
8410 * @note Locks this object for writing.
8411 */
8412HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8413 MachineState_T *aState /* = NULL */,
8414 BOOL *aRegistered /* = NULL */)
8415{
8416 AutoCaller autoCaller(this);
8417 AssertComRCReturnRC(autoCaller.rc());
8418
8419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8420
8421 HRESULT rc = i_checkStateDependency(aDepType);
8422 if (FAILED(rc)) return rc;
8423
8424 {
8425 if (mData->mMachineStateChangePending != 0)
8426 {
8427 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8428 * drop to zero so don't add more. It may make sense to wait a bit
8429 * and retry before reporting an error (since the pending state
8430 * transition should be really quick) but let's just assert for
8431 * now to see if it ever happens on practice. */
8432
8433 AssertFailed();
8434
8435 return setError(E_ACCESSDENIED,
8436 tr("Machine state change is in progress. Please retry the operation later."));
8437 }
8438
8439 ++mData->mMachineStateDeps;
8440 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8441 }
8442
8443 if (aState)
8444 *aState = mData->mMachineState;
8445 if (aRegistered)
8446 *aRegistered = mData->mRegistered;
8447
8448 return S_OK;
8449}
8450
8451/**
8452 * Decreases the number of objects dependent on the machine state.
8453 * Must always complete the #i_addStateDependency() call after the state
8454 * dependency is no more necessary.
8455 */
8456void Machine::i_releaseStateDependency()
8457{
8458 AutoCaller autoCaller(this);
8459 AssertComRCReturnVoid(autoCaller.rc());
8460
8461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8462
8463 /* releaseStateDependency() w/o addStateDependency()? */
8464 AssertReturnVoid(mData->mMachineStateDeps != 0);
8465 -- mData->mMachineStateDeps;
8466
8467 if (mData->mMachineStateDeps == 0)
8468 {
8469 /* inform i_ensureNoStateDependencies() that there are no more deps */
8470 if (mData->mMachineStateChangePending != 0)
8471 {
8472 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8473 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8474 }
8475 }
8476}
8477
8478Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8479{
8480 /* start with nothing found */
8481 Utf8Str strResult("");
8482
8483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8484
8485 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8486 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8487 // found:
8488 strResult = it->second; // source is a Utf8Str
8489
8490 return strResult;
8491}
8492
8493// protected methods
8494/////////////////////////////////////////////////////////////////////////////
8495
8496/**
8497 * Performs machine state checks based on the @a aDepType value. If a check
8498 * fails, this method will set extended error info, otherwise it will return
8499 * S_OK. It is supposed, that on failure, the caller will immediately return
8500 * the return value of this method to the upper level.
8501 *
8502 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8503 *
8504 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8505 * current state of this machine object allows to change settings of the
8506 * machine (i.e. the machine is not registered, or registered but not running
8507 * and not saved). It is useful to call this method from Machine setters
8508 * before performing any change.
8509 *
8510 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8511 * as for MutableStateDep except that if the machine is saved, S_OK is also
8512 * returned. This is useful in setters which allow changing machine
8513 * properties when it is in the saved state.
8514 *
8515 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8516 * if the current state of this machine object allows to change runtime
8517 * changeable settings of the machine (i.e. the machine is not registered, or
8518 * registered but either running or not running and not saved). It is useful
8519 * to call this method from Machine setters before performing any changes to
8520 * runtime changeable settings.
8521 *
8522 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8523 * the same as for MutableOrRunningStateDep except that if the machine is
8524 * saved, S_OK is also returned. This is useful in setters which allow
8525 * changing runtime and saved state changeable machine properties.
8526 *
8527 * @param aDepType Dependency type to check.
8528 *
8529 * @note Non Machine based classes should use #i_addStateDependency() and
8530 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8531 * template.
8532 *
8533 * @note This method must be called from under this object's read or write
8534 * lock.
8535 */
8536HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8537{
8538 switch (aDepType)
8539 {
8540 case AnyStateDep:
8541 {
8542 break;
8543 }
8544 case MutableStateDep:
8545 {
8546 if ( mData->mRegistered
8547 && ( !i_isSessionMachine()
8548 || ( mData->mMachineState != MachineState_Aborted
8549 && mData->mMachineState != MachineState_Teleported
8550 && mData->mMachineState != MachineState_PoweredOff
8551 )
8552 )
8553 )
8554 return setError(VBOX_E_INVALID_VM_STATE,
8555 tr("The machine is not mutable (state is %s)"),
8556 Global::stringifyMachineState(mData->mMachineState));
8557 break;
8558 }
8559 case MutableOrSavedStateDep:
8560 {
8561 if ( mData->mRegistered
8562 && ( !i_isSessionMachine()
8563 || ( mData->mMachineState != MachineState_Aborted
8564 && mData->mMachineState != MachineState_Teleported
8565 && mData->mMachineState != MachineState_Saved
8566 && mData->mMachineState != MachineState_AbortedSaved
8567 && mData->mMachineState != MachineState_PoweredOff
8568 )
8569 )
8570 )
8571 return setError(VBOX_E_INVALID_VM_STATE,
8572 tr("The machine is not mutable or saved (state is %s)"),
8573 Global::stringifyMachineState(mData->mMachineState));
8574 break;
8575 }
8576 case MutableOrRunningStateDep:
8577 {
8578 if ( mData->mRegistered
8579 && ( !i_isSessionMachine()
8580 || ( mData->mMachineState != MachineState_Aborted
8581 && mData->mMachineState != MachineState_Teleported
8582 && mData->mMachineState != MachineState_PoweredOff
8583 && !Global::IsOnline(mData->mMachineState)
8584 )
8585 )
8586 )
8587 return setError(VBOX_E_INVALID_VM_STATE,
8588 tr("The machine is not mutable or running (state is %s)"),
8589 Global::stringifyMachineState(mData->mMachineState));
8590 break;
8591 }
8592 case MutableOrSavedOrRunningStateDep:
8593 {
8594 if ( mData->mRegistered
8595 && ( !i_isSessionMachine()
8596 || ( mData->mMachineState != MachineState_Aborted
8597 && mData->mMachineState != MachineState_Teleported
8598 && mData->mMachineState != MachineState_Saved
8599 && mData->mMachineState != MachineState_AbortedSaved
8600 && mData->mMachineState != MachineState_PoweredOff
8601 && !Global::IsOnline(mData->mMachineState)
8602 )
8603 )
8604 )
8605 return setError(VBOX_E_INVALID_VM_STATE,
8606 tr("The machine is not mutable, saved or running (state is %s)"),
8607 Global::stringifyMachineState(mData->mMachineState));
8608 break;
8609 }
8610 }
8611
8612 return S_OK;
8613}
8614
8615/**
8616 * Helper to initialize all associated child objects and allocate data
8617 * structures.
8618 *
8619 * This method must be called as a part of the object's initialization procedure
8620 * (usually done in the #init() method).
8621 *
8622 * @note Must be called only from #init() or from #i_registeredInit().
8623 */
8624HRESULT Machine::initDataAndChildObjects()
8625{
8626 AutoCaller autoCaller(this);
8627 AssertComRCReturnRC(autoCaller.rc());
8628 AssertReturn( getObjectState().getState() == ObjectState::InInit
8629 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8630
8631 AssertReturn(!mData->mAccessible, E_FAIL);
8632
8633 /* allocate data structures */
8634 mSSData.allocate();
8635 mUserData.allocate();
8636 mHWData.allocate();
8637 mMediumAttachments.allocate();
8638 mStorageControllers.allocate();
8639 mUSBControllers.allocate();
8640
8641 /* initialize mOSTypeId */
8642 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8643
8644/** @todo r=bird: init() methods never fails, right? Why don't we make them
8645 * return void then! */
8646
8647 /* create associated BIOS settings object */
8648 unconst(mBIOSSettings).createObject();
8649 mBIOSSettings->init(this);
8650
8651 /* create associated recording settings object */
8652 unconst(mRecordingSettings).createObject();
8653 mRecordingSettings->init(this);
8654
8655 /* create associated trusted platform module object */
8656 unconst(mTrustedPlatformModule).createObject();
8657 mTrustedPlatformModule->init(this);
8658
8659 /* create associated NVRAM store object */
8660 unconst(mNvramStore).createObject();
8661 mNvramStore->init(this);
8662
8663 /* create the graphics adapter object (always present) */
8664 unconst(mGraphicsAdapter).createObject();
8665 mGraphicsAdapter->init(this);
8666
8667 /* create an associated VRDE object (default is disabled) */
8668 unconst(mVRDEServer).createObject();
8669 mVRDEServer->init(this);
8670
8671 /* create associated serial port objects */
8672 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8673 {
8674 unconst(mSerialPorts[slot]).createObject();
8675 mSerialPorts[slot]->init(this, slot);
8676 }
8677
8678 /* create associated parallel port objects */
8679 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8680 {
8681 unconst(mParallelPorts[slot]).createObject();
8682 mParallelPorts[slot]->init(this, slot);
8683 }
8684
8685 /* create the audio settings object */
8686 unconst(mAudioSettings).createObject();
8687 mAudioSettings->init(this);
8688
8689 /* create the USB device filters object (always present) */
8690 unconst(mUSBDeviceFilters).createObject();
8691 mUSBDeviceFilters->init(this);
8692
8693 /* create associated network adapter objects */
8694 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8695 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8696 {
8697 unconst(mNetworkAdapters[slot]).createObject();
8698 mNetworkAdapters[slot]->init(this, slot);
8699 }
8700
8701 /* create the bandwidth control */
8702 unconst(mBandwidthControl).createObject();
8703 mBandwidthControl->init(this);
8704
8705 /* create the guest debug control object */
8706 unconst(mGuestDebugControl).createObject();
8707 mGuestDebugControl->init(this);
8708
8709 return S_OK;
8710}
8711
8712/**
8713 * Helper to uninitialize all associated child objects and to free all data
8714 * structures.
8715 *
8716 * This method must be called as a part of the object's uninitialization
8717 * procedure (usually done in the #uninit() method).
8718 *
8719 * @note Must be called only from #uninit() or from #i_registeredInit().
8720 */
8721void Machine::uninitDataAndChildObjects()
8722{
8723 AutoCaller autoCaller(this);
8724 AssertComRCReturnVoid(autoCaller.rc());
8725 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8726 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8727 || getObjectState().getState() == ObjectState::InUninit
8728 || getObjectState().getState() == ObjectState::Limited);
8729
8730 /* tell all our other child objects we've been uninitialized */
8731 if (mGuestDebugControl)
8732 {
8733 mGuestDebugControl->uninit();
8734 unconst(mGuestDebugControl).setNull();
8735 }
8736
8737 if (mBandwidthControl)
8738 {
8739 mBandwidthControl->uninit();
8740 unconst(mBandwidthControl).setNull();
8741 }
8742
8743 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8744 {
8745 if (mNetworkAdapters[slot])
8746 {
8747 mNetworkAdapters[slot]->uninit();
8748 unconst(mNetworkAdapters[slot]).setNull();
8749 }
8750 }
8751
8752 if (mUSBDeviceFilters)
8753 {
8754 mUSBDeviceFilters->uninit();
8755 unconst(mUSBDeviceFilters).setNull();
8756 }
8757
8758 if (mAudioSettings)
8759 {
8760 mAudioSettings->uninit();
8761 unconst(mAudioSettings).setNull();
8762 }
8763
8764 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8765 {
8766 if (mParallelPorts[slot])
8767 {
8768 mParallelPorts[slot]->uninit();
8769 unconst(mParallelPorts[slot]).setNull();
8770 }
8771 }
8772
8773 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8774 {
8775 if (mSerialPorts[slot])
8776 {
8777 mSerialPorts[slot]->uninit();
8778 unconst(mSerialPorts[slot]).setNull();
8779 }
8780 }
8781
8782 if (mVRDEServer)
8783 {
8784 mVRDEServer->uninit();
8785 unconst(mVRDEServer).setNull();
8786 }
8787
8788 if (mGraphicsAdapter)
8789 {
8790 mGraphicsAdapter->uninit();
8791 unconst(mGraphicsAdapter).setNull();
8792 }
8793
8794 if (mBIOSSettings)
8795 {
8796 mBIOSSettings->uninit();
8797 unconst(mBIOSSettings).setNull();
8798 }
8799
8800 if (mRecordingSettings)
8801 {
8802 mRecordingSettings->uninit();
8803 unconst(mRecordingSettings).setNull();
8804 }
8805
8806 if (mTrustedPlatformModule)
8807 {
8808 mTrustedPlatformModule->uninit();
8809 unconst(mTrustedPlatformModule).setNull();
8810 }
8811
8812 if (mNvramStore)
8813 {
8814 mNvramStore->uninit();
8815 unconst(mNvramStore).setNull();
8816 }
8817
8818 /* Deassociate media (only when a real Machine or a SnapshotMachine
8819 * instance is uninitialized; SessionMachine instances refer to real
8820 * Machine media). This is necessary for a clean re-initialization of
8821 * the VM after successfully re-checking the accessibility state. Note
8822 * that in case of normal Machine or SnapshotMachine uninitialization (as
8823 * a result of unregistering or deleting the snapshot), outdated media
8824 * attachments will already be uninitialized and deleted, so this
8825 * code will not affect them. */
8826 if ( !mMediumAttachments.isNull()
8827 && !i_isSessionMachine()
8828 )
8829 {
8830 for (MediumAttachmentList::const_iterator
8831 it = mMediumAttachments->begin();
8832 it != mMediumAttachments->end();
8833 ++it)
8834 {
8835 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8836 if (pMedium.isNull())
8837 continue;
8838 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8839 AssertComRC(rc);
8840 }
8841 }
8842
8843 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8844 {
8845 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children)
8846 if (mData->mFirstSnapshot)
8847 {
8848 // Snapshots tree is protected by machine write lock.
8849 // Otherwise we assert in Snapshot::uninit()
8850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8851 mData->mFirstSnapshot->uninit();
8852 mData->mFirstSnapshot.setNull();
8853 }
8854
8855 mData->mCurrentSnapshot.setNull();
8856 }
8857
8858 /* free data structures (the essential mData structure is not freed here
8859 * since it may be still in use) */
8860 mMediumAttachments.free();
8861 mStorageControllers.free();
8862 mUSBControllers.free();
8863 mHWData.free();
8864 mUserData.free();
8865 mSSData.free();
8866}
8867
8868/**
8869 * Returns a pointer to the Machine object for this machine that acts like a
8870 * parent for complex machine data objects such as shared folders, etc.
8871 *
8872 * For primary Machine objects and for SnapshotMachine objects, returns this
8873 * object's pointer itself. For SessionMachine objects, returns the peer
8874 * (primary) machine pointer.
8875 */
8876Machine *Machine::i_getMachine()
8877{
8878 if (i_isSessionMachine())
8879 return (Machine*)mPeer;
8880 return this;
8881}
8882
8883/**
8884 * Makes sure that there are no machine state dependents. If necessary, waits
8885 * for the number of dependents to drop to zero.
8886 *
8887 * Make sure this method is called from under this object's write lock to
8888 * guarantee that no new dependents may be added when this method returns
8889 * control to the caller.
8890 *
8891 * @note Receives a lock to this object for writing. The lock will be released
8892 * while waiting (if necessary).
8893 *
8894 * @warning To be used only in methods that change the machine state!
8895 */
8896void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8897{
8898 AssertReturnVoid(isWriteLockOnCurrentThread());
8899
8900 /* Wait for all state dependents if necessary */
8901 if (mData->mMachineStateDeps != 0)
8902 {
8903 /* lazy semaphore creation */
8904 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8905 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8906
8907 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8908 mData->mMachineStateDeps));
8909
8910 ++mData->mMachineStateChangePending;
8911
8912 /* reset the semaphore before waiting, the last dependent will signal
8913 * it */
8914 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8915
8916 alock.release();
8917
8918 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8919
8920 alock.acquire();
8921
8922 -- mData->mMachineStateChangePending;
8923 }
8924}
8925
8926/**
8927 * Changes the machine state and informs callbacks.
8928 *
8929 * This method is not intended to fail so it either returns S_OK or asserts (and
8930 * returns a failure).
8931 *
8932 * @note Locks this object for writing.
8933 */
8934HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8935{
8936 LogFlowThisFuncEnter();
8937 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8938 Assert(aMachineState != MachineState_Null);
8939
8940 AutoCaller autoCaller(this);
8941 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8942
8943 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8944
8945 /* wait for state dependents to drop to zero */
8946 i_ensureNoStateDependencies(alock);
8947
8948 MachineState_T const enmOldState = mData->mMachineState;
8949 if (enmOldState != aMachineState)
8950 {
8951 mData->mMachineState = aMachineState;
8952 RTTimeNow(&mData->mLastStateChange);
8953
8954#ifdef VBOX_WITH_DTRACE_R3_MAIN
8955 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8956#endif
8957 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8958 }
8959
8960 LogFlowThisFuncLeave();
8961 return S_OK;
8962}
8963
8964/**
8965 * Searches for a shared folder with the given logical name
8966 * in the collection of shared folders.
8967 *
8968 * @param aName logical name of the shared folder
8969 * @param aSharedFolder where to return the found object
8970 * @param aSetError whether to set the error info if the folder is
8971 * not found
8972 * @return
8973 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8974 *
8975 * @note
8976 * must be called from under the object's lock!
8977 */
8978HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8979 ComObjPtr<SharedFolder> &aSharedFolder,
8980 bool aSetError /* = false */)
8981{
8982 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8983 for (HWData::SharedFolderList::const_iterator
8984 it = mHWData->mSharedFolders.begin();
8985 it != mHWData->mSharedFolders.end();
8986 ++it)
8987 {
8988 SharedFolder *pSF = *it;
8989 AutoCaller autoCaller(pSF);
8990 if (pSF->i_getName() == aName)
8991 {
8992 aSharedFolder = pSF;
8993 rc = S_OK;
8994 break;
8995 }
8996 }
8997
8998 if (aSetError && FAILED(rc))
8999 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
9000
9001 return rc;
9002}
9003
9004/**
9005 * Initializes all machine instance data from the given settings structures
9006 * from XML. The exception is the machine UUID which needs special handling
9007 * depending on the caller's use case, so the caller needs to set that herself.
9008 *
9009 * This gets called in several contexts during machine initialization:
9010 *
9011 * -- When machine XML exists on disk already and needs to be loaded into memory,
9012 * for example, from #i_registeredInit() to load all registered machines on
9013 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
9014 * attached to the machine should be part of some media registry already.
9015 *
9016 * -- During OVF import, when a machine config has been constructed from an
9017 * OVF file. In this case, puuidRegistry is set to the machine UUID to
9018 * ensure that the media listed as attachments in the config (which have
9019 * been imported from the OVF) receive the correct registry ID.
9020 *
9021 * -- During VM cloning.
9022 *
9023 * @param config Machine settings from XML.
9024 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
9025 * for each attached medium in the config.
9026 * @return
9027 */
9028HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
9029 const Guid *puuidRegistry)
9030{
9031 // copy name, description, OS type, teleporter, UTC etc.
9032 mUserData->s = config.machineUserData;
9033
9034 // look up the object by Id to check it is valid
9035 ComObjPtr<GuestOSType> pGuestOSType;
9036 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9037 if (!pGuestOSType.isNull())
9038 mUserData->s.strOsType = pGuestOSType->i_id();
9039
9040#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9041 // stateFile encryption (optional)
9042 mSSData->strStateKeyId = config.strStateKeyId;
9043 mSSData->strStateKeyStore = config.strStateKeyStore;
9044 mData->mstrLogKeyId = config.strLogKeyId;
9045 mData->mstrLogKeyStore = config.strLogKeyStore;
9046#endif
9047
9048 // stateFile (optional)
9049 if (config.strStateFile.isEmpty())
9050 mSSData->strStateFilePath.setNull();
9051 else
9052 {
9053 Utf8Str stateFilePathFull(config.strStateFile);
9054 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
9055 if (RT_FAILURE(vrc))
9056 return setErrorBoth(E_FAIL, vrc,
9057 tr("Invalid saved state file path '%s' (%Rrc)"),
9058 config.strStateFile.c_str(),
9059 vrc);
9060 mSSData->strStateFilePath = stateFilePathFull;
9061 }
9062
9063 // snapshot folder needs special processing so set it again
9064 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
9065 if (FAILED(rc)) return rc;
9066
9067 /* Copy the extra data items (config may or may not be the same as
9068 * mData->pMachineConfigFile) if necessary. When loading the XML files
9069 * from disk they are the same, but not for OVF import. */
9070 if (mData->pMachineConfigFile != &config)
9071 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
9072
9073 /* currentStateModified (optional, default is true) */
9074 mData->mCurrentStateModified = config.fCurrentStateModified;
9075
9076 mData->mLastStateChange = config.timeLastStateChange;
9077
9078 /*
9079 * note: all mUserData members must be assigned prior this point because
9080 * we need to commit changes in order to let mUserData be shared by all
9081 * snapshot machine instances.
9082 */
9083 mUserData.commitCopy();
9084
9085 // machine registry, if present (must be loaded before snapshots)
9086 if (config.canHaveOwnMediaRegistry())
9087 {
9088 // determine machine folder
9089 Utf8Str strMachineFolder = i_getSettingsFileFull();
9090 strMachineFolder.stripFilename();
9091 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
9092 config.mediaRegistry,
9093 strMachineFolder);
9094 if (FAILED(rc)) return rc;
9095 }
9096
9097 /* Snapshot node (optional) */
9098 size_t cRootSnapshots;
9099 if ((cRootSnapshots = config.llFirstSnapshot.size()))
9100 {
9101 // there must be only one root snapshot
9102 Assert(cRootSnapshots == 1);
9103 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9104
9105 rc = i_loadSnapshot(snap,
9106 config.uuidCurrentSnapshot);
9107 if (FAILED(rc)) return rc;
9108 }
9109
9110 // hardware data
9111 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart,
9112 config.recordingSettings);
9113 if (FAILED(rc)) return rc;
9114
9115 /*
9116 * NOTE: the assignment below must be the last thing to do,
9117 * otherwise it will be not possible to change the settings
9118 * somewhere in the code above because all setters will be
9119 * blocked by i_checkStateDependency(MutableStateDep).
9120 */
9121
9122 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
9123 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
9124 {
9125 /* no need to use i_setMachineState() during init() */
9126 mData->mMachineState = MachineState_AbortedSaved;
9127 }
9128 else if (config.fAborted)
9129 {
9130 mSSData->strStateFilePath.setNull();
9131
9132 /* no need to use i_setMachineState() during init() */
9133 mData->mMachineState = MachineState_Aborted;
9134 }
9135 else if (!mSSData->strStateFilePath.isEmpty())
9136 {
9137 /* no need to use i_setMachineState() during init() */
9138 mData->mMachineState = MachineState_Saved;
9139 }
9140
9141 // after loading settings, we are no longer different from the XML on disk
9142 mData->flModifications = 0;
9143
9144 return S_OK;
9145}
9146
9147/**
9148 * Loads all snapshots starting from the given settings.
9149 *
9150 * @param data snapshot settings.
9151 * @param aCurSnapshotId Current snapshot ID from the settings file.
9152 */
9153HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
9154 const Guid &aCurSnapshotId)
9155{
9156 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
9157 AssertReturn(!i_isSessionMachine(), E_FAIL);
9158
9159 HRESULT rc = S_OK;
9160
9161 std::list<const settings::Snapshot *> llSettingsTodo;
9162 llSettingsTodo.push_back(&data);
9163 std::list<Snapshot *> llParentsTodo;
9164 llParentsTodo.push_back(NULL);
9165
9166 while (llSettingsTodo.size() > 0)
9167 {
9168 const settings::Snapshot *current = llSettingsTodo.front();
9169 llSettingsTodo.pop_front();
9170 Snapshot *pParent = llParentsTodo.front();
9171 llParentsTodo.pop_front();
9172
9173 Utf8Str strStateFile;
9174 if (!current->strStateFile.isEmpty())
9175 {
9176 /* optional */
9177 strStateFile = current->strStateFile;
9178 int vrc = i_calculateFullPath(strStateFile, strStateFile);
9179 if (RT_FAILURE(vrc))
9180 {
9181 setErrorBoth(E_FAIL, vrc,
9182 tr("Invalid saved state file path '%s' (%Rrc)"),
9183 strStateFile.c_str(), vrc);
9184 }
9185 }
9186
9187 /* create a snapshot machine object */
9188 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9189 pSnapshotMachine.createObject();
9190 rc = pSnapshotMachine->initFromSettings(this,
9191 current->hardware,
9192 &current->debugging,
9193 &current->autostart,
9194 current->recordingSettings,
9195 current->uuid.ref(),
9196 strStateFile);
9197 if (FAILED(rc)) break;
9198
9199 /* create a snapshot object */
9200 ComObjPtr<Snapshot> pSnapshot;
9201 pSnapshot.createObject();
9202 /* initialize the snapshot */
9203 rc = pSnapshot->init(mParent, // VirtualBox object
9204 current->uuid,
9205 current->strName,
9206 current->strDescription,
9207 current->timestamp,
9208 pSnapshotMachine,
9209 pParent);
9210 if (FAILED(rc)) break;
9211
9212 /* memorize the first snapshot if necessary */
9213 if (!mData->mFirstSnapshot)
9214 {
9215 Assert(pParent == NULL);
9216 mData->mFirstSnapshot = pSnapshot;
9217 }
9218
9219 /* memorize the current snapshot when appropriate */
9220 if ( !mData->mCurrentSnapshot
9221 && pSnapshot->i_getId() == aCurSnapshotId
9222 )
9223 mData->mCurrentSnapshot = pSnapshot;
9224
9225 /* create all children */
9226 std::list<settings::Snapshot>::const_iterator itBegin = current->llChildSnapshots.begin();
9227 std::list<settings::Snapshot>::const_iterator itEnd = current->llChildSnapshots.end();
9228 for (std::list<settings::Snapshot>::const_iterator it = itBegin; it != itEnd; ++it)
9229 {
9230 llSettingsTodo.push_back(&*it);
9231 llParentsTodo.push_back(pSnapshot);
9232 }
9233 }
9234
9235 return rc;
9236}
9237
9238/**
9239 * Loads settings into mHWData.
9240 *
9241 * @param puuidRegistry Registry ID.
9242 * @param puuidSnapshot Snapshot ID
9243 * @param data Reference to the hardware settings.
9244 * @param pDbg Pointer to the debugging settings.
9245 * @param pAutostart Pointer to the autostart settings
9246 * @param recording Reference to recording settings.
9247 */
9248HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
9249 const Guid *puuidSnapshot,
9250 const settings::Hardware &data,
9251 const settings::Debugging *pDbg,
9252 const settings::Autostart *pAutostart,
9253 const settings::RecordingSettings &recording)
9254{
9255 AssertReturn(!i_isSessionMachine(), E_FAIL);
9256
9257 HRESULT rc = S_OK;
9258
9259 try
9260 {
9261 ComObjPtr<GuestOSType> pGuestOSType;
9262 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9263
9264 /* The hardware version attribute (optional). */
9265 mHWData->mHWVersion = data.strVersion;
9266 mHWData->mHardwareUUID = data.uuid;
9267
9268 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9269 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9270 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9271 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9272 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9273 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9274 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
9275 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
9276 mHWData->mPAEEnabled = data.fPAE;
9277 mHWData->mLongMode = data.enmLongMode;
9278 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9279 mHWData->mAPIC = data.fAPIC;
9280 mHWData->mX2APIC = data.fX2APIC;
9281 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9282 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9283 mHWData->mSpecCtrl = data.fSpecCtrl;
9284 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
9285 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
9286 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
9287 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
9288 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
9289 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9290 mHWData->mCPUCount = data.cCPUs;
9291 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9292 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9293 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9294 mHWData->mCpuProfile = data.strCpuProfile;
9295
9296 // cpu
9297 if (mHWData->mCPUHotPlugEnabled)
9298 {
9299 for (settings::CpuList::const_iterator
9300 it = data.llCpus.begin();
9301 it != data.llCpus.end();
9302 ++it)
9303 {
9304 const settings::Cpu &cpu = *it;
9305
9306 mHWData->mCPUAttached[cpu.ulId] = true;
9307 }
9308 }
9309
9310 // cpuid leafs
9311 for (settings::CpuIdLeafsList::const_iterator
9312 it = data.llCpuIdLeafs.begin();
9313 it != data.llCpuIdLeafs.end();
9314 ++it)
9315 {
9316 const settings::CpuIdLeaf &rLeaf= *it;
9317 if ( rLeaf.idx < UINT32_C(0x20)
9318 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9319 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9320 mHWData->mCpuIdLeafList.push_back(rLeaf);
9321 /* else: just ignore */
9322 }
9323
9324 mHWData->mMemorySize = data.ulMemorySizeMB;
9325 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9326
9327 // boot order
9328 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9329 {
9330 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9331 if (it == data.mapBootOrder.end())
9332 mHWData->mBootOrder[i] = DeviceType_Null;
9333 else
9334 mHWData->mBootOrder[i] = it->second;
9335 }
9336
9337 mHWData->mFirmwareType = data.firmwareType;
9338 mHWData->mPointingHIDType = data.pointingHIDType;
9339 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9340 mHWData->mChipsetType = data.chipsetType;
9341 mHWData->mIommuType = data.iommuType;
9342 mHWData->mParavirtProvider = data.paravirtProvider;
9343 mHWData->mParavirtDebug = data.strParavirtDebug;
9344 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9345 mHWData->mHPETEnabled = data.fHPETEnabled;
9346
9347 /* GraphicsAdapter */
9348 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
9349 if (FAILED(rc)) return rc;
9350
9351 /* VRDEServer */
9352 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9353 if (FAILED(rc)) return rc;
9354
9355 /* BIOS */
9356 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9357 if (FAILED(rc)) return rc;
9358
9359 /* Recording */
9360 rc = mRecordingSettings->i_loadSettings(recording);
9361 if (FAILED(rc)) return rc;
9362
9363 /* Trusted Platform Module */
9364 rc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
9365 if (FAILED(rc)) return rc;
9366
9367 rc = mNvramStore->i_loadSettings(data.nvramSettings);
9368 if (FAILED(rc)) return rc;
9369
9370 // Bandwidth control (must come before network adapters)
9371 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9372 if (FAILED(rc)) return rc;
9373
9374 /* USB controllers */
9375 for (settings::USBControllerList::const_iterator
9376 it = data.usbSettings.llUSBControllers.begin();
9377 it != data.usbSettings.llUSBControllers.end();
9378 ++it)
9379 {
9380 const settings::USBController &settingsCtrl = *it;
9381 ComObjPtr<USBController> newCtrl;
9382
9383 newCtrl.createObject();
9384 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9385 mUSBControllers->push_back(newCtrl);
9386 }
9387
9388 /* USB device filters */
9389 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9390 if (FAILED(rc)) return rc;
9391
9392 // network adapters (establish array size first and apply defaults, to
9393 // ensure reading the same settings as we saved, since the list skips
9394 // adapters having defaults)
9395 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9396 size_t oldCount = mNetworkAdapters.size();
9397 if (newCount > oldCount)
9398 {
9399 mNetworkAdapters.resize(newCount);
9400 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9401 {
9402 unconst(mNetworkAdapters[slot]).createObject();
9403 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9404 }
9405 }
9406 else if (newCount < oldCount)
9407 mNetworkAdapters.resize(newCount);
9408 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9409 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9410 for (settings::NetworkAdaptersList::const_iterator
9411 it = data.llNetworkAdapters.begin();
9412 it != data.llNetworkAdapters.end();
9413 ++it)
9414 {
9415 const settings::NetworkAdapter &nic = *it;
9416
9417 /* slot uniqueness is guaranteed by XML Schema */
9418 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9419 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9420 if (FAILED(rc)) return rc;
9421 }
9422
9423 // serial ports (establish defaults first, to ensure reading the same
9424 // settings as we saved, since the list skips ports having defaults)
9425 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9426 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9427 for (settings::SerialPortsList::const_iterator
9428 it = data.llSerialPorts.begin();
9429 it != data.llSerialPorts.end();
9430 ++it)
9431 {
9432 const settings::SerialPort &s = *it;
9433
9434 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9435 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9436 if (FAILED(rc)) return rc;
9437 }
9438
9439 // parallel ports (establish defaults first, to ensure reading the same
9440 // settings as we saved, since the list skips ports having defaults)
9441 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9442 mParallelPorts[i]->i_applyDefaults();
9443 for (settings::ParallelPortsList::const_iterator
9444 it = data.llParallelPorts.begin();
9445 it != data.llParallelPorts.end();
9446 ++it)
9447 {
9448 const settings::ParallelPort &p = *it;
9449
9450 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9451 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9452 if (FAILED(rc)) return rc;
9453 }
9454
9455 /* Audio settings */
9456 rc = mAudioSettings->i_loadSettings(data.audioAdapter);
9457 if (FAILED(rc)) return rc;
9458
9459 /* storage controllers */
9460 rc = i_loadStorageControllers(data.storage,
9461 puuidRegistry,
9462 puuidSnapshot);
9463 if (FAILED(rc)) return rc;
9464
9465 /* Shared folders */
9466 for (settings::SharedFoldersList::const_iterator
9467 it = data.llSharedFolders.begin();
9468 it != data.llSharedFolders.end();
9469 ++it)
9470 {
9471 const settings::SharedFolder &sf = *it;
9472
9473 ComObjPtr<SharedFolder> sharedFolder;
9474 /* Check for double entries. Not allowed! */
9475 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9476 if (SUCCEEDED(rc))
9477 return setError(VBOX_E_OBJECT_IN_USE,
9478 tr("Shared folder named '%s' already exists"),
9479 sf.strName.c_str());
9480
9481 /* Create the new shared folder. Don't break on error. This will be
9482 * reported when the machine starts. */
9483 sharedFolder.createObject();
9484 rc = sharedFolder->init(i_getMachine(),
9485 sf.strName,
9486 sf.strHostPath,
9487 RT_BOOL(sf.fWritable),
9488 RT_BOOL(sf.fAutoMount),
9489 sf.strAutoMountPoint,
9490 false /* fFailOnError */);
9491 if (FAILED(rc)) return rc;
9492 mHWData->mSharedFolders.push_back(sharedFolder);
9493 }
9494
9495 // Clipboard
9496 mHWData->mClipboardMode = data.clipboardMode;
9497 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9498
9499 // drag'n'drop
9500 mHWData->mDnDMode = data.dndMode;
9501
9502 // guest settings
9503 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9504
9505 // IO settings
9506 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9507 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9508
9509 // Host PCI devices
9510 for (settings::HostPCIDeviceAttachmentList::const_iterator
9511 it = data.pciAttachments.begin();
9512 it != data.pciAttachments.end();
9513 ++it)
9514 {
9515 const settings::HostPCIDeviceAttachment &hpda = *it;
9516 ComObjPtr<PCIDeviceAttachment> pda;
9517
9518 pda.createObject();
9519 pda->i_loadSettings(this, hpda);
9520 mHWData->mPCIDeviceAssignments.push_back(pda);
9521 }
9522
9523 /*
9524 * (The following isn't really real hardware, but it lives in HWData
9525 * for reasons of convenience.)
9526 */
9527
9528#ifdef VBOX_WITH_GUEST_PROPS
9529 /* Guest properties (optional) */
9530
9531 /* Only load transient guest properties for configs which have saved
9532 * state, because there shouldn't be any for powered off VMs. The same
9533 * logic applies for snapshots, as offline snapshots shouldn't have
9534 * any such properties. They confuse the code in various places.
9535 * Note: can't rely on the machine state, as it isn't set yet. */
9536 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9537 /* apologies for the hacky unconst() usage, but this needs hacking
9538 * actually inconsistent settings into consistency, otherwise there
9539 * will be some corner cases where the inconsistency survives
9540 * surprisingly long without getting fixed, especially for snapshots
9541 * as there are no config changes. */
9542 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9543 for (settings::GuestPropertiesList::iterator
9544 it = llGuestProperties.begin();
9545 it != llGuestProperties.end();
9546 /*nothing*/)
9547 {
9548 const settings::GuestProperty &prop = *it;
9549 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9550 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9551 if ( fSkipTransientGuestProperties
9552 && ( fFlags & GUEST_PROP_F_TRANSIENT
9553 || fFlags & GUEST_PROP_F_TRANSRESET))
9554 {
9555 it = llGuestProperties.erase(it);
9556 continue;
9557 }
9558 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9559 mHWData->mGuestProperties[prop.strName] = property;
9560 ++it;
9561 }
9562#endif /* VBOX_WITH_GUEST_PROPS defined */
9563
9564 rc = i_loadDebugging(pDbg);
9565 if (FAILED(rc))
9566 return rc;
9567
9568 mHWData->mAutostart = *pAutostart;
9569
9570 /* default frontend */
9571 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9572 }
9573 catch (std::bad_alloc &)
9574 {
9575 return E_OUTOFMEMORY;
9576 }
9577
9578 AssertComRC(rc);
9579 return rc;
9580}
9581
9582/**
9583 * Called from i_loadHardware() to load the debugging settings of the
9584 * machine.
9585 *
9586 * @param pDbg Pointer to the settings.
9587 */
9588HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9589{
9590 mHWData->mDebugging = *pDbg;
9591 /* no more processing currently required, this will probably change. */
9592
9593 HRESULT rc = mGuestDebugControl->i_loadSettings(*pDbg);
9594 if (FAILED(rc)) return rc;
9595
9596 return S_OK;
9597}
9598
9599/**
9600 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9601 *
9602 * @param data storage settings.
9603 * @param puuidRegistry media registry ID to set media to or NULL;
9604 * see Machine::i_loadMachineDataFromSettings()
9605 * @param puuidSnapshot snapshot ID
9606 * @return
9607 */
9608HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9609 const Guid *puuidRegistry,
9610 const Guid *puuidSnapshot)
9611{
9612 AssertReturn(!i_isSessionMachine(), E_FAIL);
9613
9614 HRESULT rc = S_OK;
9615
9616 for (settings::StorageControllersList::const_iterator
9617 it = data.llStorageControllers.begin();
9618 it != data.llStorageControllers.end();
9619 ++it)
9620 {
9621 const settings::StorageController &ctlData = *it;
9622
9623 ComObjPtr<StorageController> pCtl;
9624 /* Try to find one with the name first. */
9625 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9626 if (SUCCEEDED(rc))
9627 return setError(VBOX_E_OBJECT_IN_USE,
9628 tr("Storage controller named '%s' already exists"),
9629 ctlData.strName.c_str());
9630
9631 pCtl.createObject();
9632 rc = pCtl->init(this,
9633 ctlData.strName,
9634 ctlData.storageBus,
9635 ctlData.ulInstance,
9636 ctlData.fBootable);
9637 if (FAILED(rc)) return rc;
9638
9639 mStorageControllers->push_back(pCtl);
9640
9641 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9642 if (FAILED(rc)) return rc;
9643
9644 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9645 if (FAILED(rc)) return rc;
9646
9647 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9648 if (FAILED(rc)) return rc;
9649
9650 /* Load the attached devices now. */
9651 rc = i_loadStorageDevices(pCtl,
9652 ctlData,
9653 puuidRegistry,
9654 puuidSnapshot);
9655 if (FAILED(rc)) return rc;
9656 }
9657
9658 return S_OK;
9659}
9660
9661/**
9662 * Called from i_loadStorageControllers for a controller's devices.
9663 *
9664 * @param aStorageController
9665 * @param data
9666 * @param puuidRegistry media registry ID to set media to or NULL; see
9667 * Machine::i_loadMachineDataFromSettings()
9668 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9669 * @return
9670 */
9671HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9672 const settings::StorageController &data,
9673 const Guid *puuidRegistry,
9674 const Guid *puuidSnapshot)
9675{
9676 HRESULT rc = S_OK;
9677
9678 /* paranoia: detect duplicate attachments */
9679 for (settings::AttachedDevicesList::const_iterator
9680 it = data.llAttachedDevices.begin();
9681 it != data.llAttachedDevices.end();
9682 ++it)
9683 {
9684 const settings::AttachedDevice &ad = *it;
9685
9686 for (settings::AttachedDevicesList::const_iterator it2 = it;
9687 it2 != data.llAttachedDevices.end();
9688 ++it2)
9689 {
9690 if (it == it2)
9691 continue;
9692
9693 const settings::AttachedDevice &ad2 = *it2;
9694
9695 if ( ad.lPort == ad2.lPort
9696 && ad.lDevice == ad2.lDevice)
9697 {
9698 return setError(E_FAIL,
9699 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9700 aStorageController->i_getName().c_str(),
9701 ad.lPort,
9702 ad.lDevice,
9703 mUserData->s.strName.c_str());
9704 }
9705 }
9706 }
9707
9708 for (settings::AttachedDevicesList::const_iterator
9709 it = data.llAttachedDevices.begin();
9710 it != data.llAttachedDevices.end();
9711 ++it)
9712 {
9713 const settings::AttachedDevice &dev = *it;
9714 ComObjPtr<Medium> medium;
9715
9716 switch (dev.deviceType)
9717 {
9718 case DeviceType_Floppy:
9719 case DeviceType_DVD:
9720 if (dev.strHostDriveSrc.isNotEmpty())
9721 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9722 false /* fRefresh */, medium);
9723 else
9724 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9725 dev.uuid,
9726 false /* fRefresh */,
9727 false /* aSetError */,
9728 medium);
9729 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9730 // This is not an error. The host drive or UUID might have vanished, so just go
9731 // ahead without this removeable medium attachment
9732 rc = S_OK;
9733 break;
9734
9735 case DeviceType_HardDisk:
9736 {
9737 /* find a hard disk by UUID */
9738 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9739 if (FAILED(rc))
9740 {
9741 if (i_isSnapshotMachine())
9742 {
9743 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9744 // so the user knows that the bad disk is in a snapshot somewhere
9745 com::ErrorInfo info;
9746 return setError(E_FAIL,
9747 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9748 puuidSnapshot->raw(),
9749 info.getText().raw());
9750 }
9751 else
9752 return rc;
9753 }
9754
9755 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9756
9757 if (medium->i_getType() == MediumType_Immutable)
9758 {
9759 if (i_isSnapshotMachine())
9760 return setError(E_FAIL,
9761 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9762 "of the virtual machine '%s' ('%s')"),
9763 medium->i_getLocationFull().c_str(),
9764 dev.uuid.raw(),
9765 puuidSnapshot->raw(),
9766 mUserData->s.strName.c_str(),
9767 mData->m_strConfigFileFull.c_str());
9768
9769 return setError(E_FAIL,
9770 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9771 medium->i_getLocationFull().c_str(),
9772 dev.uuid.raw(),
9773 mUserData->s.strName.c_str(),
9774 mData->m_strConfigFileFull.c_str());
9775 }
9776
9777 if (medium->i_getType() == MediumType_MultiAttach)
9778 {
9779 if (i_isSnapshotMachine())
9780 return setError(E_FAIL,
9781 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9782 "of the virtual machine '%s' ('%s')"),
9783 medium->i_getLocationFull().c_str(),
9784 dev.uuid.raw(),
9785 puuidSnapshot->raw(),
9786 mUserData->s.strName.c_str(),
9787 mData->m_strConfigFileFull.c_str());
9788
9789 return setError(E_FAIL,
9790 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9791 medium->i_getLocationFull().c_str(),
9792 dev.uuid.raw(),
9793 mUserData->s.strName.c_str(),
9794 mData->m_strConfigFileFull.c_str());
9795 }
9796
9797 if ( !i_isSnapshotMachine()
9798 && medium->i_getChildren().size() != 0
9799 )
9800 return setError(E_FAIL,
9801 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9802 "because it has %d differencing child hard disks"),
9803 medium->i_getLocationFull().c_str(),
9804 dev.uuid.raw(),
9805 mUserData->s.strName.c_str(),
9806 mData->m_strConfigFileFull.c_str(),
9807 medium->i_getChildren().size());
9808
9809 if (i_findAttachment(*mMediumAttachments.data(),
9810 medium))
9811 return setError(E_FAIL,
9812 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9813 medium->i_getLocationFull().c_str(),
9814 dev.uuid.raw(),
9815 mUserData->s.strName.c_str(),
9816 mData->m_strConfigFileFull.c_str());
9817
9818 break;
9819 }
9820
9821 default:
9822 return setError(E_FAIL,
9823 tr("Controller '%s' port %u unit %u has device with unknown type (%d) - virtual machine '%s' ('%s')"),
9824 data.strName.c_str(), dev.lPort, dev.lDevice, dev.deviceType,
9825 mUserData->s.strName.c_str(), mData->m_strConfigFileFull.c_str());
9826 }
9827
9828 if (FAILED(rc))
9829 break;
9830
9831 /* Bandwidth groups are loaded at this point. */
9832 ComObjPtr<BandwidthGroup> pBwGroup;
9833
9834 if (!dev.strBwGroup.isEmpty())
9835 {
9836 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9837 if (FAILED(rc))
9838 return setError(E_FAIL,
9839 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9840 medium->i_getLocationFull().c_str(),
9841 dev.strBwGroup.c_str(),
9842 mUserData->s.strName.c_str(),
9843 mData->m_strConfigFileFull.c_str());
9844 pBwGroup->i_reference();
9845 }
9846
9847 const Utf8Str controllerName = aStorageController->i_getName();
9848 ComObjPtr<MediumAttachment> pAttachment;
9849 pAttachment.createObject();
9850 rc = pAttachment->init(this,
9851 medium,
9852 controllerName,
9853 dev.lPort,
9854 dev.lDevice,
9855 dev.deviceType,
9856 false,
9857 dev.fPassThrough,
9858 dev.fTempEject,
9859 dev.fNonRotational,
9860 dev.fDiscard,
9861 dev.fHotPluggable,
9862 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9863 if (FAILED(rc)) break;
9864
9865 /* associate the medium with this machine and snapshot */
9866 if (!medium.isNull())
9867 {
9868 AutoCaller medCaller(medium);
9869 if (FAILED(medCaller.rc())) return medCaller.rc();
9870 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9871
9872 if (i_isSnapshotMachine())
9873 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9874 else
9875 rc = medium->i_addBackReference(mData->mUuid);
9876 /* If the medium->addBackReference fails it sets an appropriate
9877 * error message, so no need to do any guesswork here. */
9878
9879 if (puuidRegistry)
9880 // caller wants registry ID to be set on all attached media (OVF import case)
9881 medium->i_addRegistry(*puuidRegistry);
9882 }
9883
9884 if (FAILED(rc))
9885 break;
9886
9887 /* back up mMediumAttachments to let registeredInit() properly rollback
9888 * on failure (= limited accessibility) */
9889 i_setModified(IsModified_Storage);
9890 mMediumAttachments.backup();
9891 mMediumAttachments->push_back(pAttachment);
9892 }
9893
9894 return rc;
9895}
9896
9897/**
9898 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9899 *
9900 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9901 * @param aSnapshot where to return the found snapshot
9902 * @param aSetError true to set extended error info on failure
9903 */
9904HRESULT Machine::i_findSnapshotById(const Guid &aId,
9905 ComObjPtr<Snapshot> &aSnapshot,
9906 bool aSetError /* = false */)
9907{
9908 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9909
9910 if (!mData->mFirstSnapshot)
9911 {
9912 if (aSetError)
9913 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9914 return E_FAIL;
9915 }
9916
9917 if (aId.isZero())
9918 aSnapshot = mData->mFirstSnapshot;
9919 else
9920 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9921
9922 if (!aSnapshot)
9923 {
9924 if (aSetError)
9925 return setError(E_FAIL,
9926 tr("Could not find a snapshot with UUID {%s}"),
9927 aId.toString().c_str());
9928 return E_FAIL;
9929 }
9930
9931 return S_OK;
9932}
9933
9934/**
9935 * Returns the snapshot with the given name or fails of no such snapshot.
9936 *
9937 * @param strName snapshot name to find
9938 * @param aSnapshot where to return the found snapshot
9939 * @param aSetError true to set extended error info on failure
9940 */
9941HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9942 ComObjPtr<Snapshot> &aSnapshot,
9943 bool aSetError /* = false */)
9944{
9945 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9946
9947 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9948
9949 if (!mData->mFirstSnapshot)
9950 {
9951 if (aSetError)
9952 return setError(VBOX_E_OBJECT_NOT_FOUND,
9953 tr("This machine does not have any snapshots"));
9954 return VBOX_E_OBJECT_NOT_FOUND;
9955 }
9956
9957 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9958
9959 if (!aSnapshot)
9960 {
9961 if (aSetError)
9962 return setError(VBOX_E_OBJECT_NOT_FOUND,
9963 tr("Could not find a snapshot named '%s'"), strName.c_str());
9964 return VBOX_E_OBJECT_NOT_FOUND;
9965 }
9966
9967 return S_OK;
9968}
9969
9970/**
9971 * Returns a storage controller object with the given name.
9972 *
9973 * @param aName storage controller name to find
9974 * @param aStorageController where to return the found storage controller
9975 * @param aSetError true to set extended error info on failure
9976 */
9977HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9978 ComObjPtr<StorageController> &aStorageController,
9979 bool aSetError /* = false */)
9980{
9981 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9982
9983 for (StorageControllerList::const_iterator
9984 it = mStorageControllers->begin();
9985 it != mStorageControllers->end();
9986 ++it)
9987 {
9988 if ((*it)->i_getName() == aName)
9989 {
9990 aStorageController = (*it);
9991 return S_OK;
9992 }
9993 }
9994
9995 if (aSetError)
9996 return setError(VBOX_E_OBJECT_NOT_FOUND,
9997 tr("Could not find a storage controller named '%s'"),
9998 aName.c_str());
9999 return VBOX_E_OBJECT_NOT_FOUND;
10000}
10001
10002/**
10003 * Returns a USB controller object with the given name.
10004 *
10005 * @param aName USB controller name to find
10006 * @param aUSBController where to return the found USB controller
10007 * @param aSetError true to set extended error info on failure
10008 */
10009HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
10010 ComObjPtr<USBController> &aUSBController,
10011 bool aSetError /* = false */)
10012{
10013 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
10014
10015 for (USBControllerList::const_iterator
10016 it = mUSBControllers->begin();
10017 it != mUSBControllers->end();
10018 ++it)
10019 {
10020 if ((*it)->i_getName() == aName)
10021 {
10022 aUSBController = (*it);
10023 return S_OK;
10024 }
10025 }
10026
10027 if (aSetError)
10028 return setError(VBOX_E_OBJECT_NOT_FOUND,
10029 tr("Could not find a storage controller named '%s'"),
10030 aName.c_str());
10031 return VBOX_E_OBJECT_NOT_FOUND;
10032}
10033
10034/**
10035 * Returns the number of USB controller instance of the given type.
10036 *
10037 * @param enmType USB controller type.
10038 */
10039ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
10040{
10041 ULONG cCtrls = 0;
10042
10043 for (USBControllerList::const_iterator
10044 it = mUSBControllers->begin();
10045 it != mUSBControllers->end();
10046 ++it)
10047 {
10048 if ((*it)->i_getControllerType() == enmType)
10049 cCtrls++;
10050 }
10051
10052 return cCtrls;
10053}
10054
10055HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
10056 MediumAttachmentList &atts)
10057{
10058 AutoCaller autoCaller(this);
10059 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10060
10061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10062
10063 for (MediumAttachmentList::const_iterator
10064 it = mMediumAttachments->begin();
10065 it != mMediumAttachments->end();
10066 ++it)
10067 {
10068 const ComObjPtr<MediumAttachment> &pAtt = *it;
10069 // should never happen, but deal with NULL pointers in the list.
10070 AssertContinue(!pAtt.isNull());
10071
10072 // getControllerName() needs caller+read lock
10073 AutoCaller autoAttCaller(pAtt);
10074 if (FAILED(autoAttCaller.rc()))
10075 {
10076 atts.clear();
10077 return autoAttCaller.rc();
10078 }
10079 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
10080
10081 if (pAtt->i_getControllerName() == aName)
10082 atts.push_back(pAtt);
10083 }
10084
10085 return S_OK;
10086}
10087
10088
10089/**
10090 * Helper for #i_saveSettings. Cares about renaming the settings directory and
10091 * file if the machine name was changed and about creating a new settings file
10092 * if this is a new machine.
10093 *
10094 * @note Must be never called directly but only from #saveSettings().
10095 */
10096HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
10097 bool *pfSettingsFileIsNew)
10098{
10099 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10100
10101 HRESULT rc = S_OK;
10102
10103 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
10104 /// @todo need to handle primary group change, too
10105
10106 /* attempt to rename the settings file if machine name is changed */
10107 if ( mUserData->s.fNameSync
10108 && mUserData.isBackedUp()
10109 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
10110 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
10111 )
10112 {
10113 bool dirRenamed = false;
10114 bool fileRenamed = false;
10115
10116 Utf8Str configFile, newConfigFile;
10117 Utf8Str configFilePrev, newConfigFilePrev;
10118 Utf8Str NVRAMFile, newNVRAMFile;
10119 Utf8Str configDir, newConfigDir;
10120
10121 do
10122 {
10123 int vrc = VINF_SUCCESS;
10124
10125 Utf8Str name = mUserData.backedUpData()->s.strName;
10126 Utf8Str newName = mUserData->s.strName;
10127 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
10128 if (group == "/")
10129 group.setNull();
10130 Utf8Str newGroup = mUserData->s.llGroups.front();
10131 if (newGroup == "/")
10132 newGroup.setNull();
10133
10134 configFile = mData->m_strConfigFileFull;
10135
10136 /* first, rename the directory if it matches the group and machine name */
10137 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
10138 /** @todo hack, make somehow use of ComposeMachineFilename */
10139 if (mUserData->s.fDirectoryIncludesUUID)
10140 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10141 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
10142 /** @todo hack, make somehow use of ComposeMachineFilename */
10143 if (mUserData->s.fDirectoryIncludesUUID)
10144 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10145 configDir = configFile;
10146 configDir.stripFilename();
10147 newConfigDir = configDir;
10148 if ( configDir.length() >= groupPlusName.length()
10149 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
10150 groupPlusName.c_str()))
10151 {
10152 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10153 Utf8Str newConfigBaseDir(newConfigDir);
10154 newConfigDir.append(newGroupPlusName);
10155 /* consistency: use \ if appropriate on the platform */
10156 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10157 /* new dir and old dir cannot be equal here because of 'if'
10158 * above and because name != newName */
10159 Assert(configDir != newConfigDir);
10160 if (!fSettingsFileIsNew)
10161 {
10162 /* perform real rename only if the machine is not new */
10163 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10164 if ( vrc == VERR_FILE_NOT_FOUND
10165 || vrc == VERR_PATH_NOT_FOUND)
10166 {
10167 /* create the parent directory, then retry renaming */
10168 Utf8Str parent(newConfigDir);
10169 parent.stripFilename();
10170 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10171 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10172 }
10173 if (RT_FAILURE(vrc))
10174 {
10175 rc = setErrorBoth(E_FAIL, vrc,
10176 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10177 configDir.c_str(),
10178 newConfigDir.c_str(),
10179 vrc);
10180 break;
10181 }
10182 /* delete subdirectories which are no longer needed */
10183 Utf8Str dir(configDir);
10184 dir.stripFilename();
10185 while (dir != newConfigBaseDir && dir != ".")
10186 {
10187 vrc = RTDirRemove(dir.c_str());
10188 if (RT_FAILURE(vrc))
10189 break;
10190 dir.stripFilename();
10191 }
10192 dirRenamed = true;
10193 }
10194 }
10195
10196 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10197
10198 /* then try to rename the settings file itself */
10199 if (newConfigFile != configFile)
10200 {
10201 /* get the path to old settings file in renamed directory */
10202 Assert(mData->m_strConfigFileFull == configFile);
10203 configFile.printf("%s%c%s",
10204 newConfigDir.c_str(),
10205 RTPATH_DELIMITER,
10206 RTPathFilename(mData->m_strConfigFileFull.c_str()));
10207 if (!fSettingsFileIsNew)
10208 {
10209 /* perform real rename only if the machine is not new */
10210 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10211 if (RT_FAILURE(vrc))
10212 {
10213 rc = setErrorBoth(E_FAIL, vrc,
10214 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10215 configFile.c_str(),
10216 newConfigFile.c_str(),
10217 vrc);
10218 break;
10219 }
10220 fileRenamed = true;
10221 configFilePrev = configFile;
10222 configFilePrev += "-prev";
10223 newConfigFilePrev = newConfigFile;
10224 newConfigFilePrev += "-prev";
10225 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10226 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
10227 if (NVRAMFile.isNotEmpty())
10228 {
10229 // in the NVRAM file path, replace the old directory with the new directory
10230 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
10231 {
10232 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
10233 NVRAMFile = newConfigDir + strNVRAMFile;
10234 }
10235 newNVRAMFile = newConfigFile;
10236 newNVRAMFile.stripSuffix();
10237 newNVRAMFile += ".nvram";
10238 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
10239 }
10240 }
10241 }
10242
10243 // update m_strConfigFileFull amd mConfigFile
10244 mData->m_strConfigFileFull = newConfigFile;
10245 // compute the relative path too
10246 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10247
10248 // store the old and new so that VirtualBox::i_saveSettings() can update
10249 // the media registry
10250 if ( mData->mRegistered
10251 && (configDir != newConfigDir || configFile != newConfigFile))
10252 {
10253 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10254
10255 if (pfNeedsGlobalSaveSettings)
10256 *pfNeedsGlobalSaveSettings = true;
10257 }
10258
10259 // in the saved state file path, replace the old directory with the new directory
10260 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10261 {
10262 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
10263 mSSData->strStateFilePath = newConfigDir + strStateFileName;
10264 }
10265 if (newNVRAMFile.isNotEmpty())
10266 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
10267
10268 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
10269 if (mData->mFirstSnapshot)
10270 {
10271 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10272 newConfigDir.c_str());
10273 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
10274 newConfigDir.c_str());
10275 }
10276 }
10277 while (0);
10278
10279 if (FAILED(rc))
10280 {
10281 /* silently try to rename everything back */
10282 if (fileRenamed)
10283 {
10284 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10285 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10286 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
10287 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
10288 }
10289 if (dirRenamed)
10290 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10291 }
10292
10293 if (FAILED(rc)) return rc;
10294 }
10295
10296 if (fSettingsFileIsNew)
10297 {
10298 /* create a virgin config file */
10299 int vrc = VINF_SUCCESS;
10300
10301 /* ensure the settings directory exists */
10302 Utf8Str path(mData->m_strConfigFileFull);
10303 path.stripFilename();
10304 if (!RTDirExists(path.c_str()))
10305 {
10306 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10307 if (RT_FAILURE(vrc))
10308 {
10309 return setErrorBoth(E_FAIL, vrc,
10310 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10311 path.c_str(),
10312 vrc);
10313 }
10314 }
10315
10316 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10317 path = mData->m_strConfigFileFull;
10318 RTFILE f = NIL_RTFILE;
10319 vrc = RTFileOpen(&f, path.c_str(),
10320 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10321 if (RT_FAILURE(vrc))
10322 return setErrorBoth(E_FAIL, vrc,
10323 tr("Could not create the settings file '%s' (%Rrc)"),
10324 path.c_str(),
10325 vrc);
10326 RTFileClose(f);
10327 }
10328 if (pfSettingsFileIsNew)
10329 *pfSettingsFileIsNew = fSettingsFileIsNew;
10330
10331 return rc;
10332}
10333
10334/**
10335 * Saves and commits machine data, user data and hardware data.
10336 *
10337 * Note that on failure, the data remains uncommitted.
10338 *
10339 * @a aFlags may combine the following flags:
10340 *
10341 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10342 * Used when saving settings after an operation that makes them 100%
10343 * correspond to the settings from the current snapshot.
10344 * - SaveS_Force: settings will be saved without doing a deep compare of the
10345 * settings structures. This is used when this is called because snapshots
10346 * have changed to avoid the overhead of the deep compare.
10347 *
10348 * @note Must be called from under this object's write lock. Locks children for
10349 * writing.
10350 *
10351 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10352 * initialized to false and that will be set to true by this function if
10353 * the caller must invoke VirtualBox::i_saveSettings() because the global
10354 * settings have changed. This will happen if a machine rename has been
10355 * saved and the global machine and media registries will therefore need
10356 * updating.
10357 * @param alock Reference to the lock for this machine object.
10358 * @param aFlags Flags.
10359 */
10360HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10361 AutoWriteLock &alock,
10362 int aFlags /*= 0*/)
10363{
10364 LogFlowThisFuncEnter();
10365
10366 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10367
10368 /* make sure child objects are unable to modify the settings while we are
10369 * saving them */
10370 i_ensureNoStateDependencies(alock);
10371
10372 AssertReturn(!i_isSnapshotMachine(),
10373 E_FAIL);
10374
10375 if (!mData->mAccessible)
10376 return setError(VBOX_E_INVALID_VM_STATE,
10377 tr("The machine is not accessible, so cannot save settings"));
10378
10379 HRESULT rc = S_OK;
10380 PCVBOXCRYPTOIF pCryptoIf = NULL;
10381 const char *pszPassword = NULL;
10382 SecretKey *pKey = NULL;
10383
10384#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10385 if (mData->mstrKeyId.isNotEmpty())
10386 {
10387 /* VM is going to be encrypted. */
10388 alock.release(); /** @todo Revise the locking. */
10389 rc = mParent->i_retainCryptoIf(&pCryptoIf);
10390 alock.acquire();
10391 if (FAILED(rc)) return rc; /* Error is set. */
10392
10393 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
10394 if (RT_SUCCESS(vrc))
10395 pszPassword = (const char *)pKey->getKeyBuffer();
10396 else
10397 {
10398 mParent->i_releaseCryptoIf(pCryptoIf);
10399 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
10400 tr("Failed to retain VM encryption password using ID '%s' with %Rrc"),
10401 mData->mstrKeyId.c_str(), vrc);
10402 }
10403 }
10404#else
10405 RT_NOREF(pKey);
10406#endif
10407
10408 bool fNeedsWrite = false;
10409 bool fSettingsFileIsNew = false;
10410
10411 /* First, prepare to save settings. It will care about renaming the
10412 * settings directory and file if the machine name was changed and about
10413 * creating a new settings file if this is a new machine. */
10414 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings,
10415 &fSettingsFileIsNew);
10416 if (FAILED(rc))
10417 {
10418#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10419 if (pCryptoIf)
10420 {
10421 alock.release(); /** @todo Revise the locking. */
10422 mParent->i_releaseCryptoIf(pCryptoIf);
10423 alock.acquire();
10424 }
10425 if (pKey)
10426 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10427#endif
10428 return rc;
10429 }
10430
10431 // keep a pointer to the current settings structures
10432 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10433 settings::MachineConfigFile *pNewConfig = NULL;
10434
10435 try
10436 {
10437 // make a fresh one to have everyone write stuff into
10438 pNewConfig = new settings::MachineConfigFile(NULL);
10439 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10440#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10441 pNewConfig->strKeyId = mData->mstrKeyId;
10442 pNewConfig->strKeyStore = mData->mstrKeyStore;
10443#endif
10444
10445 // now go and copy all the settings data from COM to the settings structures
10446 // (this calls i_saveSettings() on all the COM objects in the machine)
10447 i_copyMachineDataToSettings(*pNewConfig);
10448
10449 if (aFlags & SaveS_ResetCurStateModified)
10450 {
10451 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10452 mData->mCurrentStateModified = FALSE;
10453 fNeedsWrite = true; // always, no need to compare
10454 }
10455 else if (aFlags & SaveS_Force)
10456 {
10457 fNeedsWrite = true; // always, no need to compare
10458 }
10459 else
10460 {
10461 if (!mData->mCurrentStateModified)
10462 {
10463 // do a deep compare of the settings that we just saved with the settings
10464 // previously stored in the config file; this invokes MachineConfigFile::operator==
10465 // which does a deep compare of all the settings, which is expensive but less expensive
10466 // than writing out XML in vain
10467 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10468
10469 // could still be modified if any settings changed
10470 mData->mCurrentStateModified = fAnySettingsChanged;
10471
10472 fNeedsWrite = fAnySettingsChanged;
10473 }
10474 else
10475 fNeedsWrite = true;
10476 }
10477
10478 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10479
10480 if (fNeedsWrite)
10481 {
10482 // now spit it all out!
10483 pNewConfig->write(mData->m_strConfigFileFull, pCryptoIf, pszPassword);
10484 if (aFlags & SaveS_RemoveBackup)
10485 i_deleteFile(mData->m_strConfigFileFull + "-prev", true /* fIgnoreFailures */);
10486 }
10487
10488 mData->pMachineConfigFile = pNewConfig;
10489 delete pOldConfig;
10490 i_commit();
10491
10492 // after saving settings, we are no longer different from the XML on disk
10493 mData->flModifications = 0;
10494 }
10495 catch (HRESULT err)
10496 {
10497 // we assume that error info is set by the thrower
10498 rc = err;
10499
10500 // delete any newly created settings file
10501 if (fSettingsFileIsNew)
10502 i_deleteFile(mData->m_strConfigFileFull, true /* fIgnoreFailures */);
10503
10504 // restore old config
10505 delete pNewConfig;
10506 mData->pMachineConfigFile = pOldConfig;
10507 }
10508 catch (...)
10509 {
10510 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10511 }
10512
10513#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10514 if (pCryptoIf)
10515 {
10516 alock.release(); /** @todo Revise the locking. */
10517 mParent->i_releaseCryptoIf(pCryptoIf);
10518 alock.acquire();
10519 }
10520 if (pKey)
10521 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10522#endif
10523
10524 if (fNeedsWrite)
10525 {
10526 /* Fire the data change event, even on failure (since we've already
10527 * committed all data). This is done only for SessionMachines because
10528 * mutable Machine instances are always not registered (i.e. private
10529 * to the client process that creates them) and thus don't need to
10530 * inform callbacks. */
10531 if (i_isSessionMachine())
10532 mParent->i_onMachineDataChanged(mData->mUuid);
10533 }
10534
10535 LogFlowThisFunc(("rc=%08X\n", rc));
10536 LogFlowThisFuncLeave();
10537 return rc;
10538}
10539
10540/**
10541 * Implementation for saving the machine settings into the given
10542 * settings::MachineConfigFile instance. This copies machine extradata
10543 * from the previous machine config file in the instance data, if any.
10544 *
10545 * This gets called from two locations:
10546 *
10547 * -- Machine::i_saveSettings(), during the regular XML writing;
10548 *
10549 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10550 * exported to OVF and we write the VirtualBox proprietary XML
10551 * into a <vbox:Machine> tag.
10552 *
10553 * This routine fills all the fields in there, including snapshots, *except*
10554 * for the following:
10555 *
10556 * -- fCurrentStateModified. There is some special logic associated with that.
10557 *
10558 * The caller can then call MachineConfigFile::write() or do something else
10559 * with it.
10560 *
10561 * Caller must hold the machine lock!
10562 *
10563 * This throws XML errors and HRESULT, so the caller must have a catch block!
10564 */
10565void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10566{
10567 // deep copy extradata, being extra careful with self assignment (the STL
10568 // map assignment on Mac OS X clang based Xcode isn't checking)
10569 if (&config != mData->pMachineConfigFile)
10570 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10571
10572 config.uuid = mData->mUuid;
10573
10574 // copy name, description, OS type, teleport, UTC etc.
10575 config.machineUserData = mUserData->s;
10576
10577#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10578 config.strStateKeyId = mSSData->strStateKeyId;
10579 config.strStateKeyStore = mSSData->strStateKeyStore;
10580 config.strLogKeyId = mData->mstrLogKeyId;
10581 config.strLogKeyStore = mData->mstrLogKeyStore;
10582#endif
10583
10584 if ( mData->mMachineState == MachineState_Saved
10585 || mData->mMachineState == MachineState_AbortedSaved
10586 || mData->mMachineState == MachineState_Restoring
10587 // when doing certain snapshot operations we may or may not have
10588 // a saved state in the current state, so keep everything as is
10589 || ( ( mData->mMachineState == MachineState_Snapshotting
10590 || mData->mMachineState == MachineState_DeletingSnapshot
10591 || mData->mMachineState == MachineState_RestoringSnapshot)
10592 && (!mSSData->strStateFilePath.isEmpty())
10593 )
10594 )
10595 {
10596 Assert(!mSSData->strStateFilePath.isEmpty());
10597 /* try to make the file name relative to the settings file dir */
10598 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10599 }
10600 else
10601 {
10602 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10603 config.strStateFile.setNull();
10604 }
10605
10606 if (mData->mCurrentSnapshot)
10607 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10608 else
10609 config.uuidCurrentSnapshot.clear();
10610
10611 config.timeLastStateChange = mData->mLastStateChange;
10612 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10613 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10614
10615 HRESULT hrc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart, config.recordingSettings);
10616 if (FAILED(hrc)) throw hrc;
10617
10618 // save machine's media registry if this is VirtualBox 4.0 or later
10619 if (config.canHaveOwnMediaRegistry())
10620 {
10621 // determine machine folder
10622 Utf8Str strMachineFolder = i_getSettingsFileFull();
10623 strMachineFolder.stripFilename();
10624 mParent->i_saveMediaRegistry(config.mediaRegistry,
10625 i_getId(), // only media with registry ID == machine UUID
10626 strMachineFolder);
10627 // this throws HRESULT
10628 }
10629
10630 // save snapshots
10631 hrc = i_saveAllSnapshots(config);
10632 if (FAILED(hrc)) throw hrc;
10633}
10634
10635/**
10636 * Saves all snapshots of the machine into the given machine config file. Called
10637 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10638 * @param config
10639 * @return
10640 */
10641HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10642{
10643 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10644
10645 HRESULT rc = S_OK;
10646
10647 try
10648 {
10649 config.llFirstSnapshot.clear();
10650
10651 if (mData->mFirstSnapshot)
10652 {
10653 // the settings use a list for "the first snapshot"
10654 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10655
10656 // get reference to the snapshot on the list and work on that
10657 // element straight in the list to avoid excessive copying later
10658 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10659 if (FAILED(rc)) throw rc;
10660 }
10661
10662// if (mType == IsSessionMachine)
10663// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10664
10665 }
10666 catch (HRESULT err)
10667 {
10668 /* we assume that error info is set by the thrower */
10669 rc = err;
10670 }
10671 catch (...)
10672 {
10673 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10674 }
10675
10676 return rc;
10677}
10678
10679/**
10680 * Saves the VM hardware configuration. It is assumed that the
10681 * given node is empty.
10682 *
10683 * @param data Reference to the settings object for the hardware config.
10684 * @param pDbg Pointer to the settings object for the debugging config
10685 * which happens to live in mHWData.
10686 * @param pAutostart Pointer to the settings object for the autostart config
10687 * which happens to live in mHWData.
10688 * @param recording Reference to reecording settings.
10689 */
10690HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10691 settings::Autostart *pAutostart, settings::RecordingSettings &recording)
10692{
10693 HRESULT rc = S_OK;
10694
10695 try
10696 {
10697 /* The hardware version attribute (optional).
10698 Automatically upgrade from 1 to current default hardware version
10699 when there is no saved state. (ugly!) */
10700 if ( mHWData->mHWVersion == "1"
10701 && mSSData->strStateFilePath.isEmpty()
10702 )
10703 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10704
10705 data.strVersion = mHWData->mHWVersion;
10706 data.uuid = mHWData->mHardwareUUID;
10707
10708 // CPU
10709 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10710 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10711 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10712 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10713 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10714 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10715 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10716 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10717 data.fPAE = !!mHWData->mPAEEnabled;
10718 data.enmLongMode = mHWData->mLongMode;
10719 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10720 data.fAPIC = !!mHWData->mAPIC;
10721 data.fX2APIC = !!mHWData->mX2APIC;
10722 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10723 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10724 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10725 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10726 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10727 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10728 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10729 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10730 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10731 data.cCPUs = mHWData->mCPUCount;
10732 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10733 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10734 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10735 data.strCpuProfile = mHWData->mCpuProfile;
10736
10737 data.llCpus.clear();
10738 if (data.fCpuHotPlug)
10739 {
10740 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10741 {
10742 if (mHWData->mCPUAttached[idx])
10743 {
10744 settings::Cpu cpu;
10745 cpu.ulId = idx;
10746 data.llCpus.push_back(cpu);
10747 }
10748 }
10749 }
10750
10751 /* Standard and Extended CPUID leafs. */
10752 data.llCpuIdLeafs.clear();
10753 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10754
10755 // memory
10756 data.ulMemorySizeMB = mHWData->mMemorySize;
10757 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10758
10759 // firmware
10760 data.firmwareType = mHWData->mFirmwareType;
10761
10762 // HID
10763 data.pointingHIDType = mHWData->mPointingHIDType;
10764 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10765
10766 // chipset
10767 data.chipsetType = mHWData->mChipsetType;
10768
10769 // iommu
10770 data.iommuType = mHWData->mIommuType;
10771
10772 // paravirt
10773 data.paravirtProvider = mHWData->mParavirtProvider;
10774 data.strParavirtDebug = mHWData->mParavirtDebug;
10775
10776 // emulated USB card reader
10777 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10778
10779 // HPET
10780 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10781
10782 // boot order
10783 data.mapBootOrder.clear();
10784 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10785 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10786
10787 /* VRDEServer settings (optional) */
10788 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10789 if (FAILED(rc)) throw rc;
10790
10791 /* BIOS settings (required) */
10792 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10793 if (FAILED(rc)) throw rc;
10794
10795 /* Recording settings. */
10796 rc = mRecordingSettings->i_saveSettings(recording);
10797 if (FAILED(rc)) throw rc;
10798
10799 /* Trusted Platform Module settings (required) */
10800 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10801 if (FAILED(rc)) throw rc;
10802
10803 /* NVRAM settings (required) */
10804 rc = mNvramStore->i_saveSettings(data.nvramSettings);
10805 if (FAILED(rc)) throw rc;
10806
10807 /* GraphicsAdapter settings (required) */
10808 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10809 if (FAILED(rc)) throw rc;
10810
10811 /* USB Controller (required) */
10812 data.usbSettings.llUSBControllers.clear();
10813 for (USBControllerList::const_iterator
10814 it = mUSBControllers->begin();
10815 it != mUSBControllers->end();
10816 ++it)
10817 {
10818 ComObjPtr<USBController> ctrl = *it;
10819 settings::USBController settingsCtrl;
10820
10821 settingsCtrl.strName = ctrl->i_getName();
10822 settingsCtrl.enmType = ctrl->i_getControllerType();
10823
10824 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10825 }
10826
10827 /* USB device filters (required) */
10828 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10829 if (FAILED(rc)) throw rc;
10830
10831 /* Network adapters (required) */
10832 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10833 data.llNetworkAdapters.clear();
10834 /* Write out only the nominal number of network adapters for this
10835 * chipset type. Since Machine::commit() hasn't been called there
10836 * may be extra NIC settings in the vector. */
10837 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10838 {
10839 settings::NetworkAdapter nic;
10840 nic.ulSlot = (uint32_t)slot;
10841 /* paranoia check... must not be NULL, but must not crash either. */
10842 if (mNetworkAdapters[slot])
10843 {
10844 if (mNetworkAdapters[slot]->i_hasDefaults())
10845 continue;
10846
10847 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10848 if (FAILED(rc)) throw rc;
10849
10850 data.llNetworkAdapters.push_back(nic);
10851 }
10852 }
10853
10854 /* Serial ports */
10855 data.llSerialPorts.clear();
10856 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10857 {
10858 if (mSerialPorts[slot]->i_hasDefaults())
10859 continue;
10860
10861 settings::SerialPort s;
10862 s.ulSlot = slot;
10863 rc = mSerialPorts[slot]->i_saveSettings(s);
10864 if (FAILED(rc)) return rc;
10865
10866 data.llSerialPorts.push_back(s);
10867 }
10868
10869 /* Parallel ports */
10870 data.llParallelPorts.clear();
10871 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10872 {
10873 if (mParallelPorts[slot]->i_hasDefaults())
10874 continue;
10875
10876 settings::ParallelPort p;
10877 p.ulSlot = slot;
10878 rc = mParallelPorts[slot]->i_saveSettings(p);
10879 if (FAILED(rc)) return rc;
10880
10881 data.llParallelPorts.push_back(p);
10882 }
10883
10884 /* Audio settings */
10885 rc = mAudioSettings->i_saveSettings(data.audioAdapter);
10886 if (FAILED(rc)) return rc;
10887
10888 rc = i_saveStorageControllers(data.storage);
10889 if (FAILED(rc)) return rc;
10890
10891 /* Shared folders */
10892 data.llSharedFolders.clear();
10893 for (HWData::SharedFolderList::const_iterator
10894 it = mHWData->mSharedFolders.begin();
10895 it != mHWData->mSharedFolders.end();
10896 ++it)
10897 {
10898 SharedFolder *pSF = *it;
10899 AutoCaller sfCaller(pSF);
10900 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10901 settings::SharedFolder sf;
10902 sf.strName = pSF->i_getName();
10903 sf.strHostPath = pSF->i_getHostPath();
10904 sf.fWritable = !!pSF->i_isWritable();
10905 sf.fAutoMount = !!pSF->i_isAutoMounted();
10906 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10907
10908 data.llSharedFolders.push_back(sf);
10909 }
10910
10911 // clipboard
10912 data.clipboardMode = mHWData->mClipboardMode;
10913 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10914
10915 // drag'n'drop
10916 data.dndMode = mHWData->mDnDMode;
10917
10918 /* Guest */
10919 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10920
10921 // IO settings
10922 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10923 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10924
10925 /* BandwidthControl (required) */
10926 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10927 if (FAILED(rc)) throw rc;
10928
10929 /* Host PCI devices */
10930 data.pciAttachments.clear();
10931 for (HWData::PCIDeviceAssignmentList::const_iterator
10932 it = mHWData->mPCIDeviceAssignments.begin();
10933 it != mHWData->mPCIDeviceAssignments.end();
10934 ++it)
10935 {
10936 ComObjPtr<PCIDeviceAttachment> pda = *it;
10937 settings::HostPCIDeviceAttachment hpda;
10938
10939 rc = pda->i_saveSettings(hpda);
10940 if (FAILED(rc)) throw rc;
10941
10942 data.pciAttachments.push_back(hpda);
10943 }
10944
10945 // guest properties
10946 data.llGuestProperties.clear();
10947#ifdef VBOX_WITH_GUEST_PROPS
10948 for (HWData::GuestPropertyMap::const_iterator
10949 it = mHWData->mGuestProperties.begin();
10950 it != mHWData->mGuestProperties.end();
10951 ++it)
10952 {
10953 HWData::GuestProperty property = it->second;
10954
10955 /* Remove transient guest properties at shutdown unless we
10956 * are saving state. Note that restoring snapshot intentionally
10957 * keeps them, they will be removed if appropriate once the final
10958 * machine state is set (as crashes etc. need to work). */
10959 if ( ( mData->mMachineState == MachineState_PoweredOff
10960 || mData->mMachineState == MachineState_Aborted
10961 || mData->mMachineState == MachineState_Teleported)
10962 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10963 continue;
10964 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10965 prop.strName = it->first;
10966 prop.strValue = property.strValue;
10967 prop.timestamp = (uint64_t)property.mTimestamp;
10968 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10969 GuestPropWriteFlags(property.mFlags, szFlags);
10970 prop.strFlags = szFlags;
10971
10972 data.llGuestProperties.push_back(prop);
10973 }
10974
10975 /* I presume this doesn't require a backup(). */
10976 mData->mGuestPropertiesModified = FALSE;
10977#endif /* VBOX_WITH_GUEST_PROPS defined */
10978
10979 rc = mGuestDebugControl->i_saveSettings(mHWData->mDebugging);
10980 if (FAILED(rc)) throw rc;
10981
10982 *pDbg = mHWData->mDebugging; /// @todo r=aeichner: Move this to guest debug control. */
10983 *pAutostart = mHWData->mAutostart;
10984
10985 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10986 }
10987 catch (std::bad_alloc &)
10988 {
10989 return E_OUTOFMEMORY;
10990 }
10991
10992 AssertComRC(rc);
10993 return rc;
10994}
10995
10996/**
10997 * Saves the storage controller configuration.
10998 *
10999 * @param data storage settings.
11000 */
11001HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
11002{
11003 data.llStorageControllers.clear();
11004
11005 for (StorageControllerList::const_iterator
11006 it = mStorageControllers->begin();
11007 it != mStorageControllers->end();
11008 ++it)
11009 {
11010 HRESULT rc;
11011 ComObjPtr<StorageController> pCtl = *it;
11012
11013 settings::StorageController ctl;
11014 ctl.strName = pCtl->i_getName();
11015 ctl.controllerType = pCtl->i_getControllerType();
11016 ctl.storageBus = pCtl->i_getStorageBus();
11017 ctl.ulInstance = pCtl->i_getInstance();
11018 ctl.fBootable = pCtl->i_getBootable();
11019
11020 /* Save the port count. */
11021 ULONG portCount;
11022 rc = pCtl->COMGETTER(PortCount)(&portCount);
11023 ComAssertComRCRet(rc, rc);
11024 ctl.ulPortCount = portCount;
11025
11026 /* Save fUseHostIOCache */
11027 BOOL fUseHostIOCache;
11028 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
11029 ComAssertComRCRet(rc, rc);
11030 ctl.fUseHostIOCache = !!fUseHostIOCache;
11031
11032 /* save the devices now. */
11033 rc = i_saveStorageDevices(pCtl, ctl);
11034 ComAssertComRCRet(rc, rc);
11035
11036 data.llStorageControllers.push_back(ctl);
11037 }
11038
11039 return S_OK;
11040}
11041
11042/**
11043 * Saves the hard disk configuration.
11044 */
11045HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
11046 settings::StorageController &data)
11047{
11048 MediumAttachmentList atts;
11049
11050 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
11051 if (FAILED(rc)) return rc;
11052
11053 data.llAttachedDevices.clear();
11054 for (MediumAttachmentList::const_iterator
11055 it = atts.begin();
11056 it != atts.end();
11057 ++it)
11058 {
11059 settings::AttachedDevice dev;
11060 IMediumAttachment *iA = *it;
11061 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
11062 Medium *pMedium = pAttach->i_getMedium();
11063
11064 dev.deviceType = pAttach->i_getType();
11065 dev.lPort = pAttach->i_getPort();
11066 dev.lDevice = pAttach->i_getDevice();
11067 dev.fPassThrough = pAttach->i_getPassthrough();
11068 dev.fHotPluggable = pAttach->i_getHotPluggable();
11069 if (pMedium)
11070 {
11071 if (pMedium->i_isHostDrive())
11072 dev.strHostDriveSrc = pMedium->i_getLocationFull();
11073 else
11074 dev.uuid = pMedium->i_getId();
11075 dev.fTempEject = pAttach->i_getTempEject();
11076 dev.fNonRotational = pAttach->i_getNonRotational();
11077 dev.fDiscard = pAttach->i_getDiscard();
11078 }
11079
11080 dev.strBwGroup = pAttach->i_getBandwidthGroup();
11081
11082 data.llAttachedDevices.push_back(dev);
11083 }
11084
11085 return S_OK;
11086}
11087
11088/**
11089 * Saves machine state settings as defined by aFlags
11090 * (SaveSTS_* values).
11091 *
11092 * @param aFlags Combination of SaveSTS_* flags.
11093 *
11094 * @note Locks objects for writing.
11095 */
11096HRESULT Machine::i_saveStateSettings(int aFlags)
11097{
11098 if (aFlags == 0)
11099 return S_OK;
11100
11101 AutoCaller autoCaller(this);
11102 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11103
11104 /* This object's write lock is also necessary to serialize file access
11105 * (prevent concurrent reads and writes) */
11106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11107
11108 HRESULT rc = S_OK;
11109
11110 Assert(mData->pMachineConfigFile);
11111
11112 try
11113 {
11114 if (aFlags & SaveSTS_CurStateModified)
11115 mData->pMachineConfigFile->fCurrentStateModified = true;
11116
11117 if (aFlags & SaveSTS_StateFilePath)
11118 {
11119 if (!mSSData->strStateFilePath.isEmpty())
11120 /* try to make the file name relative to the settings file dir */
11121 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
11122 else
11123 mData->pMachineConfigFile->strStateFile.setNull();
11124 }
11125
11126 if (aFlags & SaveSTS_StateTimeStamp)
11127 {
11128 Assert( mData->mMachineState != MachineState_Aborted
11129 || mSSData->strStateFilePath.isEmpty());
11130
11131 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
11132
11133 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
11134 || mData->mMachineState == MachineState_AbortedSaved);
11135/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
11136 }
11137
11138 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
11139 }
11140 catch (...)
11141 {
11142 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
11143 }
11144
11145 return rc;
11146}
11147
11148/**
11149 * Ensures that the given medium is added to a media registry. If this machine
11150 * was created with 4.0 or later, then the machine registry is used. Otherwise
11151 * the global VirtualBox media registry is used.
11152 *
11153 * Caller must NOT hold machine lock, media tree or any medium locks!
11154 *
11155 * @param pMedium
11156 */
11157void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
11158{
11159 /* Paranoia checks: do not hold machine or media tree locks. */
11160 AssertReturnVoid(!isWriteLockOnCurrentThread());
11161 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
11162
11163 ComObjPtr<Medium> pBase;
11164 {
11165 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11166 pBase = pMedium->i_getBase();
11167 }
11168
11169 /* Paranoia checks: do not hold medium locks. */
11170 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
11171 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
11172
11173 // decide which medium registry to use now that the medium is attached:
11174 Guid uuid;
11175 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
11176 if (fCanHaveOwnMediaRegistry)
11177 // machine XML is VirtualBox 4.0 or higher:
11178 uuid = i_getId(); // machine UUID
11179 else
11180 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
11181
11182 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
11183 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11184 if (pMedium->i_addRegistry(uuid))
11185 mParent->i_markRegistryModified(uuid);
11186
11187 /* For more complex hard disk structures it can happen that the base
11188 * medium isn't yet associated with any medium registry. Do that now. */
11189 if (pMedium != pBase)
11190 {
11191 /* Tree lock needed by Medium::addRegistryAll. */
11192 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11193 if (fCanHaveOwnMediaRegistry && pBase->i_removeRegistryAll(mParent->i_getGlobalRegistryId()))
11194 {
11195 treeLock.release();
11196 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11197 treeLock.acquire();
11198 }
11199 if (pBase->i_addRegistryAll(uuid))
11200 {
11201 treeLock.release();
11202 mParent->i_markRegistryModified(uuid);
11203 }
11204 }
11205}
11206
11207/**
11208 * Physically deletes a file belonging to a machine.
11209 *
11210 * @returns HRESULT
11211 * @retval VBOX_E_FILE_ERROR on failure.
11212 * @param strFile File to delete.
11213 * @param fIgnoreFailures Whether to ignore deletion failures. Defaults to \c false.
11214 * VERR_FILE_NOT_FOUND and VERR_PATH_NOT_FOUND always will be ignored.
11215 * @param strWhat File hint which will be used when setting an error. Optional.
11216 * @param prc Where to return IPRT's error code on failure. Optional and can be NULL.
11217 */
11218HRESULT Machine::i_deleteFile(const Utf8Str &strFile, bool fIgnoreFailures /* = false */,
11219 const Utf8Str &strWhat /* = "" */, int *prc /* = NULL */)
11220{
11221 AssertReturn(strFile.isNotEmpty(), E_INVALIDARG);
11222
11223 HRESULT hrc = S_OK;
11224
11225 LogFunc(("Deleting file '%s'\n", strFile.c_str()));
11226
11227 int vrc = RTFileDelete(strFile.c_str());
11228 if (RT_FAILURE(vrc))
11229 {
11230 if ( !fIgnoreFailures
11231 /* Don't (externally) bitch about stuff which doesn't exist. */
11232 && ( vrc != VERR_FILE_NOT_FOUND
11233 && vrc != VERR_PATH_NOT_FOUND
11234 )
11235 )
11236 {
11237 LogRel(("Deleting file '%s' failed: %Rrc\n", strFile.c_str(), vrc));
11238
11239 Utf8StrFmt strError("Error deleting %s '%s' (%Rrc)",
11240 strWhat.isEmpty() ? tr("file") : strWhat.c_str(), strFile.c_str(), vrc);
11241 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, strError.c_str(),
11242 strFile.c_str(), vrc);
11243 }
11244
11245 if (prc)
11246 *prc = vrc;
11247 }
11248
11249 return hrc;
11250}
11251
11252/**
11253 * Creates differencing hard disks for all normal hard disks attached to this
11254 * machine and a new set of attachments to refer to created disks.
11255 *
11256 * Used when taking a snapshot or when deleting the current state. Gets called
11257 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
11258 *
11259 * This method assumes that mMediumAttachments contains the original hard disk
11260 * attachments it needs to create diffs for. On success, these attachments will
11261 * be replaced with the created diffs.
11262 *
11263 * Attachments with non-normal hard disks are left as is.
11264 *
11265 * If @a aOnline is @c false then the original hard disks that require implicit
11266 * diffs will be locked for reading. Otherwise it is assumed that they are
11267 * already locked for writing (when the VM was started). Note that in the latter
11268 * case it is responsibility of the caller to lock the newly created diffs for
11269 * writing if this method succeeds.
11270 *
11271 * @param aProgress Progress object to run (must contain at least as
11272 * many operations left as the number of hard disks
11273 * attached).
11274 * @param aWeight Weight of this operation.
11275 * @param aOnline Whether the VM was online prior to this operation.
11276 *
11277 * @note The progress object is not marked as completed, neither on success nor
11278 * on failure. This is a responsibility of the caller.
11279 *
11280 * @note Locks this object and the media tree for writing.
11281 */
11282HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
11283 ULONG aWeight,
11284 bool aOnline)
11285{
11286 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11287
11288 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
11289 AssertReturn(!!pProgressControl, E_INVALIDARG);
11290
11291 AutoCaller autoCaller(this);
11292 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11293
11294 AutoMultiWriteLock2 alock(this->lockHandle(),
11295 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11296
11297 /* must be in a protective state because we release the lock below */
11298 AssertReturn( mData->mMachineState == MachineState_Snapshotting
11299 || mData->mMachineState == MachineState_OnlineSnapshotting
11300 || mData->mMachineState == MachineState_LiveSnapshotting
11301 || mData->mMachineState == MachineState_RestoringSnapshot
11302 || mData->mMachineState == MachineState_DeletingSnapshot
11303 , E_FAIL);
11304
11305 HRESULT rc = S_OK;
11306
11307 // use appropriate locked media map (online or offline)
11308 MediumLockListMap lockedMediaOffline;
11309 MediumLockListMap *lockedMediaMap;
11310 if (aOnline)
11311 lockedMediaMap = &mData->mSession.mLockedMedia;
11312 else
11313 lockedMediaMap = &lockedMediaOffline;
11314
11315 try
11316 {
11317 if (!aOnline)
11318 {
11319 /* lock all attached hard disks early to detect "in use"
11320 * situations before creating actual diffs */
11321 for (MediumAttachmentList::const_iterator
11322 it = mMediumAttachments->begin();
11323 it != mMediumAttachments->end();
11324 ++it)
11325 {
11326 MediumAttachment *pAtt = *it;
11327 if (pAtt->i_getType() == DeviceType_HardDisk)
11328 {
11329 Medium *pMedium = pAtt->i_getMedium();
11330 Assert(pMedium);
11331
11332 MediumLockList *pMediumLockList(new MediumLockList());
11333 alock.release();
11334 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11335 NULL /* pToLockWrite */,
11336 false /* fMediumLockWriteAll */,
11337 NULL,
11338 *pMediumLockList);
11339 alock.acquire();
11340 if (FAILED(rc))
11341 {
11342 delete pMediumLockList;
11343 throw rc;
11344 }
11345 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11346 if (FAILED(rc))
11347 {
11348 throw setError(rc,
11349 tr("Collecting locking information for all attached media failed"));
11350 }
11351 }
11352 }
11353
11354 /* Now lock all media. If this fails, nothing is locked. */
11355 alock.release();
11356 rc = lockedMediaMap->Lock();
11357 alock.acquire();
11358 if (FAILED(rc))
11359 {
11360 throw setError(rc,
11361 tr("Locking of attached media failed"));
11362 }
11363 }
11364
11365 /* remember the current list (note that we don't use backup() since
11366 * mMediumAttachments may be already backed up) */
11367 MediumAttachmentList atts = *mMediumAttachments.data();
11368
11369 /* start from scratch */
11370 mMediumAttachments->clear();
11371
11372 /* go through remembered attachments and create diffs for normal hard
11373 * disks and attach them */
11374 for (MediumAttachmentList::const_iterator
11375 it = atts.begin();
11376 it != atts.end();
11377 ++it)
11378 {
11379 MediumAttachment *pAtt = *it;
11380
11381 DeviceType_T devType = pAtt->i_getType();
11382 Medium *pMedium = pAtt->i_getMedium();
11383
11384 if ( devType != DeviceType_HardDisk
11385 || pMedium == NULL
11386 || pMedium->i_getType() != MediumType_Normal)
11387 {
11388 /* copy the attachment as is */
11389
11390 /** @todo the progress object created in SessionMachine::TakeSnaphot
11391 * only expects operations for hard disks. Later other
11392 * device types need to show up in the progress as well. */
11393 if (devType == DeviceType_HardDisk)
11394 {
11395 if (pMedium == NULL)
11396 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11397 aWeight); // weight
11398 else
11399 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11400 pMedium->i_getBase()->i_getName().c_str()).raw(),
11401 aWeight); // weight
11402 }
11403
11404 mMediumAttachments->push_back(pAtt);
11405 continue;
11406 }
11407
11408 /* need a diff */
11409 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11410 pMedium->i_getBase()->i_getName().c_str()).raw(),
11411 aWeight); // weight
11412
11413 Utf8Str strFullSnapshotFolder;
11414 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11415
11416 ComObjPtr<Medium> diff;
11417 diff.createObject();
11418 // store the diff in the same registry as the parent
11419 // (this cannot fail here because we can't create implicit diffs for
11420 // unregistered images)
11421 Guid uuidRegistryParent;
11422 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11423 Assert(fInRegistry); NOREF(fInRegistry);
11424 rc = diff->init(mParent,
11425 pMedium->i_getPreferredDiffFormat(),
11426 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11427 uuidRegistryParent,
11428 DeviceType_HardDisk);
11429 if (FAILED(rc)) throw rc;
11430
11431 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11432 * the push_back? Looks like we're going to release medium with the
11433 * wrong kind of lock (general issue with if we fail anywhere at all)
11434 * and an orphaned VDI in the snapshots folder. */
11435
11436 /* update the appropriate lock list */
11437 MediumLockList *pMediumLockList;
11438 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11439 AssertComRCThrowRC(rc);
11440 if (aOnline)
11441 {
11442 alock.release();
11443 /* The currently attached medium will be read-only, change
11444 * the lock type to read. */
11445 rc = pMediumLockList->Update(pMedium, false);
11446 alock.acquire();
11447 AssertComRCThrowRC(rc);
11448 }
11449
11450 /* release the locks before the potentially lengthy operation */
11451 alock.release();
11452 rc = pMedium->i_createDiffStorage(diff,
11453 pMedium->i_getPreferredDiffVariant(),
11454 pMediumLockList,
11455 NULL /* aProgress */,
11456 true /* aWait */,
11457 false /* aNotify */);
11458 alock.acquire();
11459 if (FAILED(rc)) throw rc;
11460
11461 /* actual lock list update is done in Machine::i_commitMedia */
11462
11463 rc = diff->i_addBackReference(mData->mUuid);
11464 AssertComRCThrowRC(rc);
11465
11466 /* add a new attachment */
11467 ComObjPtr<MediumAttachment> attachment;
11468 attachment.createObject();
11469 rc = attachment->init(this,
11470 diff,
11471 pAtt->i_getControllerName(),
11472 pAtt->i_getPort(),
11473 pAtt->i_getDevice(),
11474 DeviceType_HardDisk,
11475 true /* aImplicit */,
11476 false /* aPassthrough */,
11477 false /* aTempEject */,
11478 pAtt->i_getNonRotational(),
11479 pAtt->i_getDiscard(),
11480 pAtt->i_getHotPluggable(),
11481 pAtt->i_getBandwidthGroup());
11482 if (FAILED(rc)) throw rc;
11483
11484 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11485 AssertComRCThrowRC(rc);
11486 mMediumAttachments->push_back(attachment);
11487 }
11488 }
11489 catch (HRESULT aRC) { rc = aRC; }
11490
11491 /* unlock all hard disks we locked when there is no VM */
11492 if (!aOnline)
11493 {
11494 ErrorInfoKeeper eik;
11495
11496 HRESULT rc1 = lockedMediaMap->Clear();
11497 AssertComRC(rc1);
11498 }
11499
11500 return rc;
11501}
11502
11503/**
11504 * Deletes implicit differencing hard disks created either by
11505 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11506 * mMediumAttachments.
11507 *
11508 * Note that to delete hard disks created by #attachDevice() this method is
11509 * called from #i_rollbackMedia() when the changes are rolled back.
11510 *
11511 * @note Locks this object and the media tree for writing.
11512 */
11513HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11514{
11515 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11516
11517 AutoCaller autoCaller(this);
11518 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11519
11520 AutoMultiWriteLock2 alock(this->lockHandle(),
11521 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11522
11523 /* We absolutely must have backed up state. */
11524 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11525
11526 /* Check if there are any implicitly created diff images. */
11527 bool fImplicitDiffs = false;
11528 for (MediumAttachmentList::const_iterator
11529 it = mMediumAttachments->begin();
11530 it != mMediumAttachments->end();
11531 ++it)
11532 {
11533 const ComObjPtr<MediumAttachment> &pAtt = *it;
11534 if (pAtt->i_isImplicit())
11535 {
11536 fImplicitDiffs = true;
11537 break;
11538 }
11539 }
11540 /* If there is nothing to do, leave early. This saves lots of image locking
11541 * effort. It also avoids a MachineStateChanged event without real reason.
11542 * This is important e.g. when loading a VM config, because there should be
11543 * no events. Otherwise API clients can become thoroughly confused for
11544 * inaccessible VMs (the code for loading VM configs uses this method for
11545 * cleanup if the config makes no sense), as they take such events as an
11546 * indication that the VM is alive, and they would force the VM config to
11547 * be reread, leading to an endless loop. */
11548 if (!fImplicitDiffs)
11549 return S_OK;
11550
11551 HRESULT rc = S_OK;
11552 MachineState_T oldState = mData->mMachineState;
11553
11554 /* will release the lock before the potentially lengthy operation,
11555 * so protect with the special state (unless already protected) */
11556 if ( oldState != MachineState_Snapshotting
11557 && oldState != MachineState_OnlineSnapshotting
11558 && oldState != MachineState_LiveSnapshotting
11559 && oldState != MachineState_RestoringSnapshot
11560 && oldState != MachineState_DeletingSnapshot
11561 && oldState != MachineState_DeletingSnapshotOnline
11562 && oldState != MachineState_DeletingSnapshotPaused
11563 )
11564 i_setMachineState(MachineState_SettingUp);
11565
11566 // use appropriate locked media map (online or offline)
11567 MediumLockListMap lockedMediaOffline;
11568 MediumLockListMap *lockedMediaMap;
11569 if (aOnline)
11570 lockedMediaMap = &mData->mSession.mLockedMedia;
11571 else
11572 lockedMediaMap = &lockedMediaOffline;
11573
11574 try
11575 {
11576 if (!aOnline)
11577 {
11578 /* lock all attached hard disks early to detect "in use"
11579 * situations before deleting actual diffs */
11580 for (MediumAttachmentList::const_iterator
11581 it = mMediumAttachments->begin();
11582 it != mMediumAttachments->end();
11583 ++it)
11584 {
11585 MediumAttachment *pAtt = *it;
11586 if (pAtt->i_getType() == DeviceType_HardDisk)
11587 {
11588 Medium *pMedium = pAtt->i_getMedium();
11589 Assert(pMedium);
11590
11591 MediumLockList *pMediumLockList(new MediumLockList());
11592 alock.release();
11593 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11594 NULL /* pToLockWrite */,
11595 false /* fMediumLockWriteAll */,
11596 NULL,
11597 *pMediumLockList);
11598 alock.acquire();
11599
11600 if (FAILED(rc))
11601 {
11602 delete pMediumLockList;
11603 throw rc;
11604 }
11605
11606 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11607 if (FAILED(rc))
11608 throw rc;
11609 }
11610 }
11611
11612 if (FAILED(rc))
11613 throw rc;
11614 } // end of offline
11615
11616 /* Lock lists are now up to date and include implicitly created media */
11617
11618 /* Go through remembered attachments and delete all implicitly created
11619 * diffs and fix up the attachment information */
11620 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11621 MediumAttachmentList implicitAtts;
11622 for (MediumAttachmentList::const_iterator
11623 it = mMediumAttachments->begin();
11624 it != mMediumAttachments->end();
11625 ++it)
11626 {
11627 ComObjPtr<MediumAttachment> pAtt = *it;
11628 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11629 if (pMedium.isNull())
11630 continue;
11631
11632 // Implicit attachments go on the list for deletion and back references are removed.
11633 if (pAtt->i_isImplicit())
11634 {
11635 /* Deassociate and mark for deletion */
11636 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11637 rc = pMedium->i_removeBackReference(mData->mUuid);
11638 if (FAILED(rc))
11639 throw rc;
11640 implicitAtts.push_back(pAtt);
11641 continue;
11642 }
11643
11644 /* Was this medium attached before? */
11645 if (!i_findAttachment(oldAtts, pMedium))
11646 {
11647 /* no: de-associate */
11648 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11649 rc = pMedium->i_removeBackReference(mData->mUuid);
11650 if (FAILED(rc))
11651 throw rc;
11652 continue;
11653 }
11654 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11655 }
11656
11657 /* If there are implicit attachments to delete, throw away the lock
11658 * map contents (which will unlock all media) since the medium
11659 * attachments will be rolled back. Below we need to completely
11660 * recreate the lock map anyway since it is infinitely complex to
11661 * do this incrementally (would need reconstructing each attachment
11662 * change, which would be extremely hairy). */
11663 if (implicitAtts.size() != 0)
11664 {
11665 ErrorInfoKeeper eik;
11666
11667 HRESULT rc1 = lockedMediaMap->Clear();
11668 AssertComRC(rc1);
11669 }
11670
11671 /* rollback hard disk changes */
11672 mMediumAttachments.rollback();
11673
11674 MultiResult mrc(S_OK);
11675
11676 // Delete unused implicit diffs.
11677 if (implicitAtts.size() != 0)
11678 {
11679 alock.release();
11680
11681 for (MediumAttachmentList::const_iterator
11682 it = implicitAtts.begin();
11683 it != implicitAtts.end();
11684 ++it)
11685 {
11686 // Remove medium associated with this attachment.
11687 ComObjPtr<MediumAttachment> pAtt = *it;
11688 Assert(pAtt);
11689 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11690 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11691 Assert(pMedium);
11692
11693 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11694 // continue on delete failure, just collect error messages
11695 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11696 pMedium->i_getLocationFull().c_str() ));
11697 mrc = rc;
11698 }
11699 // Clear the list of deleted implicit attachments now, while not
11700 // holding the lock, as it will ultimately trigger Medium::uninit()
11701 // calls which assume that the media tree lock isn't held.
11702 implicitAtts.clear();
11703
11704 alock.acquire();
11705
11706 /* if there is a VM recreate media lock map as mentioned above,
11707 * otherwise it is a waste of time and we leave things unlocked */
11708 if (aOnline)
11709 {
11710 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11711 /* must never be NULL, but better safe than sorry */
11712 if (!pMachine.isNull())
11713 {
11714 alock.release();
11715 rc = mData->mSession.mMachine->i_lockMedia();
11716 alock.acquire();
11717 if (FAILED(rc))
11718 throw rc;
11719 }
11720 }
11721 }
11722 }
11723 catch (HRESULT aRC) {rc = aRC;}
11724
11725 if (mData->mMachineState == MachineState_SettingUp)
11726 i_setMachineState(oldState);
11727
11728 /* unlock all hard disks we locked when there is no VM */
11729 if (!aOnline)
11730 {
11731 ErrorInfoKeeper eik;
11732
11733 HRESULT rc1 = lockedMediaMap->Clear();
11734 AssertComRC(rc1);
11735 }
11736
11737 return rc;
11738}
11739
11740
11741/**
11742 * Looks through the given list of media attachments for one with the given parameters
11743 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11744 * can be searched as well if needed.
11745 *
11746 * @param ll
11747 * @param aControllerName
11748 * @param aControllerPort
11749 * @param aDevice
11750 * @return
11751 */
11752MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11753 const Utf8Str &aControllerName,
11754 LONG aControllerPort,
11755 LONG aDevice)
11756{
11757 for (MediumAttachmentList::const_iterator
11758 it = ll.begin();
11759 it != ll.end();
11760 ++it)
11761 {
11762 MediumAttachment *pAttach = *it;
11763 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11764 return pAttach;
11765 }
11766
11767 return NULL;
11768}
11769
11770/**
11771 * Looks through the given list of media attachments for one with the given parameters
11772 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11773 * can be searched as well if needed.
11774 *
11775 * @param ll
11776 * @param pMedium
11777 * @return
11778 */
11779MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11780 ComObjPtr<Medium> pMedium)
11781{
11782 for (MediumAttachmentList::const_iterator
11783 it = ll.begin();
11784 it != ll.end();
11785 ++it)
11786 {
11787 MediumAttachment *pAttach = *it;
11788 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11789 if (pMediumThis == pMedium)
11790 return pAttach;
11791 }
11792
11793 return NULL;
11794}
11795
11796/**
11797 * Looks through the given list of media attachments for one with the given parameters
11798 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11799 * can be searched as well if needed.
11800 *
11801 * @param ll
11802 * @param id
11803 * @return
11804 */
11805MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11806 Guid &id)
11807{
11808 for (MediumAttachmentList::const_iterator
11809 it = ll.begin();
11810 it != ll.end();
11811 ++it)
11812 {
11813 MediumAttachment *pAttach = *it;
11814 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11815 if (pMediumThis->i_getId() == id)
11816 return pAttach;
11817 }
11818
11819 return NULL;
11820}
11821
11822/**
11823 * Main implementation for Machine::DetachDevice. This also gets called
11824 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11825 *
11826 * @param pAttach Medium attachment to detach.
11827 * @param writeLock Machine write lock which the caller must have locked once.
11828 * This may be released temporarily in here.
11829 * @param pSnapshot If NULL, then the detachment is for the current machine.
11830 * Otherwise this is for a SnapshotMachine, and this must be
11831 * its snapshot.
11832 * @return
11833 */
11834HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11835 AutoWriteLock &writeLock,
11836 Snapshot *pSnapshot)
11837{
11838 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11839 DeviceType_T mediumType = pAttach->i_getType();
11840
11841 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11842
11843 if (pAttach->i_isImplicit())
11844 {
11845 /* attempt to implicitly delete the implicitly created diff */
11846
11847 /// @todo move the implicit flag from MediumAttachment to Medium
11848 /// and forbid any hard disk operation when it is implicit. Or maybe
11849 /// a special media state for it to make it even more simple.
11850
11851 Assert(mMediumAttachments.isBackedUp());
11852
11853 /* will release the lock before the potentially lengthy operation, so
11854 * protect with the special state */
11855 MachineState_T oldState = mData->mMachineState;
11856 i_setMachineState(MachineState_SettingUp);
11857
11858 writeLock.release();
11859
11860 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11861 true /*aWait*/,
11862 false /*aNotify*/);
11863
11864 writeLock.acquire();
11865
11866 i_setMachineState(oldState);
11867
11868 if (FAILED(rc)) return rc;
11869 }
11870
11871 i_setModified(IsModified_Storage);
11872 mMediumAttachments.backup();
11873 mMediumAttachments->remove(pAttach);
11874
11875 if (!oldmedium.isNull())
11876 {
11877 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11878 if (pSnapshot)
11879 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11880 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11881 else if (mediumType != DeviceType_HardDisk)
11882 oldmedium->i_removeBackReference(mData->mUuid);
11883 }
11884
11885 return S_OK;
11886}
11887
11888/**
11889 * Goes thru all media of the given list and
11890 *
11891 * 1) calls i_detachDevice() on each of them for this machine and
11892 * 2) adds all Medium objects found in the process to the given list,
11893 * depending on cleanupMode.
11894 *
11895 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11896 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11897 * media to the list.
11898 *
11899 * This gets called from Machine::Unregister, both for the actual Machine and
11900 * the SnapshotMachine objects that might be found in the snapshots.
11901 *
11902 * Requires caller and locking. The machine lock must be passed in because it
11903 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11904 *
11905 * @param writeLock Machine lock from top-level caller; this gets passed to
11906 * i_detachDevice.
11907 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11908 * object if called for a SnapshotMachine.
11909 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11910 * added to llMedia; if Full, then all media get added;
11911 * otherwise no media get added.
11912 * @param llMedia Caller's list to receive Medium objects which got detached so
11913 * caller can close() them, depending on cleanupMode.
11914 * @return
11915 */
11916HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11917 Snapshot *pSnapshot,
11918 CleanupMode_T cleanupMode,
11919 MediaList &llMedia)
11920{
11921 Assert(isWriteLockOnCurrentThread());
11922
11923 HRESULT rc;
11924
11925 // make a temporary list because i_detachDevice invalidates iterators into
11926 // mMediumAttachments
11927 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11928
11929 for (MediumAttachmentList::iterator
11930 it = llAttachments2.begin();
11931 it != llAttachments2.end();
11932 ++it)
11933 {
11934 ComObjPtr<MediumAttachment> &pAttach = *it;
11935 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11936
11937 if (!pMedium.isNull())
11938 {
11939 AutoCaller mac(pMedium);
11940 if (FAILED(mac.rc())) return mac.rc();
11941 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11942 DeviceType_T devType = pMedium->i_getDeviceType();
11943 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11944 && devType == DeviceType_HardDisk)
11945 || (cleanupMode == CleanupMode_Full)
11946 )
11947 {
11948 llMedia.push_back(pMedium);
11949 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11950 /* Not allowed to keep this lock as below we need the parent
11951 * medium lock, and the lock order is parent to child. */
11952 lock.release();
11953 /*
11954 * Search for medias which are not attached to any machine, but
11955 * in the chain to an attached disk. Mediums are only consided
11956 * if they are:
11957 * - have only one child
11958 * - no references to any machines
11959 * - are of normal medium type
11960 */
11961 while (!pParent.isNull())
11962 {
11963 AutoCaller mac1(pParent);
11964 if (FAILED(mac1.rc())) return mac1.rc();
11965 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11966 if (pParent->i_getChildren().size() == 1)
11967 {
11968 if ( pParent->i_getMachineBackRefCount() == 0
11969 && pParent->i_getType() == MediumType_Normal
11970 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11971 llMedia.push_back(pParent);
11972 }
11973 else
11974 break;
11975 pParent = pParent->i_getParent();
11976 }
11977 }
11978 }
11979
11980 // real machine: then we need to use the proper method
11981 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11982
11983 if (FAILED(rc))
11984 return rc;
11985 }
11986
11987 return S_OK;
11988}
11989
11990/**
11991 * Perform deferred hard disk detachments.
11992 *
11993 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11994 * changed (not backed up).
11995 *
11996 * If @a aOnline is @c true then this method will also unlock the old hard
11997 * disks for which the new implicit diffs were created and will lock these new
11998 * diffs for writing.
11999 *
12000 * @param aOnline Whether the VM was online prior to this operation.
12001 *
12002 * @note Locks this object for writing!
12003 */
12004void Machine::i_commitMedia(bool aOnline /*= false*/)
12005{
12006 AutoCaller autoCaller(this);
12007 AssertComRCReturnVoid(autoCaller.rc());
12008
12009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12010
12011 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
12012
12013 HRESULT rc = S_OK;
12014
12015 /* no attach/detach operations -- nothing to do */
12016 if (!mMediumAttachments.isBackedUp())
12017 return;
12018
12019 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
12020 bool fMediaNeedsLocking = false;
12021
12022 /* enumerate new attachments */
12023 for (MediumAttachmentList::const_iterator
12024 it = mMediumAttachments->begin();
12025 it != mMediumAttachments->end();
12026 ++it)
12027 {
12028 MediumAttachment *pAttach = *it;
12029
12030 pAttach->i_commit();
12031
12032 Medium *pMedium = pAttach->i_getMedium();
12033 bool fImplicit = pAttach->i_isImplicit();
12034
12035 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
12036 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
12037 fImplicit));
12038
12039 /** @todo convert all this Machine-based voodoo to MediumAttachment
12040 * based commit logic. */
12041 if (fImplicit)
12042 {
12043 /* convert implicit attachment to normal */
12044 pAttach->i_setImplicit(false);
12045
12046 if ( aOnline
12047 && pMedium
12048 && pAttach->i_getType() == DeviceType_HardDisk
12049 )
12050 {
12051 /* update the appropriate lock list */
12052 MediumLockList *pMediumLockList;
12053 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
12054 AssertComRC(rc);
12055 if (pMediumLockList)
12056 {
12057 /* unlock if there's a need to change the locking */
12058 if (!fMediaNeedsLocking)
12059 {
12060 rc = mData->mSession.mLockedMedia.Unlock();
12061 AssertComRC(rc);
12062 fMediaNeedsLocking = true;
12063 }
12064 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
12065 AssertComRC(rc);
12066 rc = pMediumLockList->Append(pMedium, true);
12067 AssertComRC(rc);
12068 }
12069 }
12070
12071 continue;
12072 }
12073
12074 if (pMedium)
12075 {
12076 /* was this medium attached before? */
12077 for (MediumAttachmentList::iterator
12078 oldIt = oldAtts.begin();
12079 oldIt != oldAtts.end();
12080 ++oldIt)
12081 {
12082 MediumAttachment *pOldAttach = *oldIt;
12083 if (pOldAttach->i_getMedium() == pMedium)
12084 {
12085 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
12086
12087 /* yes: remove from old to avoid de-association */
12088 oldAtts.erase(oldIt);
12089 break;
12090 }
12091 }
12092 }
12093 }
12094
12095 /* enumerate remaining old attachments and de-associate from the
12096 * current machine state */
12097 for (MediumAttachmentList::const_iterator
12098 it = oldAtts.begin();
12099 it != oldAtts.end();
12100 ++it)
12101 {
12102 MediumAttachment *pAttach = *it;
12103 Medium *pMedium = pAttach->i_getMedium();
12104
12105 /* Detach only hard disks, since DVD/floppy media is detached
12106 * instantly in MountMedium. */
12107 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
12108 {
12109 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
12110
12111 /* now de-associate from the current machine state */
12112 rc = pMedium->i_removeBackReference(mData->mUuid);
12113 AssertComRC(rc);
12114
12115 if (aOnline)
12116 {
12117 /* unlock since medium is not used anymore */
12118 MediumLockList *pMediumLockList;
12119 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
12120 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
12121 {
12122 /* this happens for online snapshots, there the attachment
12123 * is changing, but only to a diff image created under
12124 * the old one, so there is no separate lock list */
12125 Assert(!pMediumLockList);
12126 }
12127 else
12128 {
12129 AssertComRC(rc);
12130 if (pMediumLockList)
12131 {
12132 rc = mData->mSession.mLockedMedia.Remove(pAttach);
12133 AssertComRC(rc);
12134 }
12135 }
12136 }
12137 }
12138 }
12139
12140 /* take media locks again so that the locking state is consistent */
12141 if (fMediaNeedsLocking)
12142 {
12143 Assert(aOnline);
12144 rc = mData->mSession.mLockedMedia.Lock();
12145 AssertComRC(rc);
12146 }
12147
12148 /* commit the hard disk changes */
12149 mMediumAttachments.commit();
12150
12151 if (i_isSessionMachine())
12152 {
12153 /*
12154 * Update the parent machine to point to the new owner.
12155 * This is necessary because the stored parent will point to the
12156 * session machine otherwise and cause crashes or errors later
12157 * when the session machine gets invalid.
12158 */
12159 /** @todo Change the MediumAttachment class to behave like any other
12160 * class in this regard by creating peer MediumAttachment
12161 * objects for session machines and share the data with the peer
12162 * machine.
12163 */
12164 for (MediumAttachmentList::const_iterator
12165 it = mMediumAttachments->begin();
12166 it != mMediumAttachments->end();
12167 ++it)
12168 (*it)->i_updateParentMachine(mPeer);
12169
12170 /* attach new data to the primary machine and reshare it */
12171 mPeer->mMediumAttachments.attach(mMediumAttachments);
12172 }
12173
12174 return;
12175}
12176
12177/**
12178 * Perform deferred deletion of implicitly created diffs.
12179 *
12180 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
12181 * changed (not backed up).
12182 *
12183 * @note Locks this object for writing!
12184 */
12185void Machine::i_rollbackMedia()
12186{
12187 AutoCaller autoCaller(this);
12188 AssertComRCReturnVoid(autoCaller.rc());
12189
12190 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12191 LogFlowThisFunc(("Entering rollbackMedia\n"));
12192
12193 HRESULT rc = S_OK;
12194
12195 /* no attach/detach operations -- nothing to do */
12196 if (!mMediumAttachments.isBackedUp())
12197 return;
12198
12199 /* enumerate new attachments */
12200 for (MediumAttachmentList::const_iterator
12201 it = mMediumAttachments->begin();
12202 it != mMediumAttachments->end();
12203 ++it)
12204 {
12205 MediumAttachment *pAttach = *it;
12206 /* Fix up the backrefs for DVD/floppy media. */
12207 if (pAttach->i_getType() != DeviceType_HardDisk)
12208 {
12209 Medium *pMedium = pAttach->i_getMedium();
12210 if (pMedium)
12211 {
12212 rc = pMedium->i_removeBackReference(mData->mUuid);
12213 AssertComRC(rc);
12214 }
12215 }
12216
12217 (*it)->i_rollback();
12218
12219 pAttach = *it;
12220 /* Fix up the backrefs for DVD/floppy media. */
12221 if (pAttach->i_getType() != DeviceType_HardDisk)
12222 {
12223 Medium *pMedium = pAttach->i_getMedium();
12224 if (pMedium)
12225 {
12226 rc = pMedium->i_addBackReference(mData->mUuid);
12227 AssertComRC(rc);
12228 }
12229 }
12230 }
12231
12232 /** @todo convert all this Machine-based voodoo to MediumAttachment
12233 * based rollback logic. */
12234 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
12235
12236 return;
12237}
12238
12239/**
12240 * Returns true if the settings file is located in the directory named exactly
12241 * as the machine; this means, among other things, that the machine directory
12242 * should be auto-renamed.
12243 *
12244 * @param aSettingsDir if not NULL, the full machine settings file directory
12245 * name will be assigned there.
12246 *
12247 * @note Doesn't lock anything.
12248 * @note Not thread safe (must be called from this object's lock).
12249 */
12250bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
12251{
12252 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12253 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
12254 if (aSettingsDir)
12255 *aSettingsDir = strMachineDirName;
12256 strMachineDirName.stripPath(); // vmname
12257 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12258 strConfigFileOnly.stripPath() // vmname.vbox
12259 .stripSuffix(); // vmname
12260 /** @todo hack, make somehow use of ComposeMachineFilename */
12261 if (mUserData->s.fDirectoryIncludesUUID)
12262 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
12263
12264 AssertReturn(!strMachineDirName.isEmpty(), false);
12265 AssertReturn(!strConfigFileOnly.isEmpty(), false);
12266
12267 return strMachineDirName == strConfigFileOnly;
12268}
12269
12270/**
12271 * Discards all changes to machine settings.
12272 *
12273 * @param aNotify Whether to notify the direct session about changes or not.
12274 *
12275 * @note Locks objects for writing!
12276 */
12277void Machine::i_rollback(bool aNotify)
12278{
12279 AutoCaller autoCaller(this);
12280 AssertComRCReturn(autoCaller.rc(), (void)0);
12281
12282 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12283
12284 if (!mStorageControllers.isNull())
12285 {
12286 if (mStorageControllers.isBackedUp())
12287 {
12288 /* unitialize all new devices (absent in the backed up list). */
12289 StorageControllerList *backedList = mStorageControllers.backedUpData();
12290 for (StorageControllerList::const_iterator
12291 it = mStorageControllers->begin();
12292 it != mStorageControllers->end();
12293 ++it)
12294 {
12295 if ( std::find(backedList->begin(), backedList->end(), *it)
12296 == backedList->end()
12297 )
12298 {
12299 (*it)->uninit();
12300 }
12301 }
12302
12303 /* restore the list */
12304 mStorageControllers.rollback();
12305 }
12306
12307 /* rollback any changes to devices after restoring the list */
12308 if (mData->flModifications & IsModified_Storage)
12309 {
12310 for (StorageControllerList::const_iterator
12311 it = mStorageControllers->begin();
12312 it != mStorageControllers->end();
12313 ++it)
12314 {
12315 (*it)->i_rollback();
12316 }
12317 }
12318 }
12319
12320 if (!mUSBControllers.isNull())
12321 {
12322 if (mUSBControllers.isBackedUp())
12323 {
12324 /* unitialize all new devices (absent in the backed up list). */
12325 USBControllerList *backedList = mUSBControllers.backedUpData();
12326 for (USBControllerList::const_iterator
12327 it = mUSBControllers->begin();
12328 it != mUSBControllers->end();
12329 ++it)
12330 {
12331 if ( std::find(backedList->begin(), backedList->end(), *it)
12332 == backedList->end()
12333 )
12334 {
12335 (*it)->uninit();
12336 }
12337 }
12338
12339 /* restore the list */
12340 mUSBControllers.rollback();
12341 }
12342
12343 /* rollback any changes to devices after restoring the list */
12344 if (mData->flModifications & IsModified_USB)
12345 {
12346 for (USBControllerList::const_iterator
12347 it = mUSBControllers->begin();
12348 it != mUSBControllers->end();
12349 ++it)
12350 {
12351 (*it)->i_rollback();
12352 }
12353 }
12354 }
12355
12356 mUserData.rollback();
12357
12358 mHWData.rollback();
12359
12360 if (mData->flModifications & IsModified_Storage)
12361 i_rollbackMedia();
12362
12363 if (mBIOSSettings)
12364 mBIOSSettings->i_rollback();
12365
12366 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
12367 mRecordingSettings->i_rollback();
12368
12369 if (mTrustedPlatformModule)
12370 mTrustedPlatformModule->i_rollback();
12371
12372 if (mNvramStore)
12373 mNvramStore->i_rollback();
12374
12375 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
12376 mGraphicsAdapter->i_rollback();
12377
12378 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12379 mVRDEServer->i_rollback();
12380
12381 if (mAudioSettings && (mData->flModifications & IsModified_AudioSettings))
12382 mAudioSettings->i_rollback();
12383
12384 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12385 mUSBDeviceFilters->i_rollback();
12386
12387 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12388 mBandwidthControl->i_rollback();
12389
12390 if (mGuestDebugControl && (mData->flModifications & IsModified_GuestDebugControl))
12391 mGuestDebugControl->i_rollback();
12392
12393 if (!mHWData.isNull())
12394 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12395 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12396 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12397 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12398
12399 if (mData->flModifications & IsModified_NetworkAdapters)
12400 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12401 if ( mNetworkAdapters[slot]
12402 && mNetworkAdapters[slot]->i_isModified())
12403 {
12404 mNetworkAdapters[slot]->i_rollback();
12405 networkAdapters[slot] = mNetworkAdapters[slot];
12406 }
12407
12408 if (mData->flModifications & IsModified_SerialPorts)
12409 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12410 if ( mSerialPorts[slot]
12411 && mSerialPorts[slot]->i_isModified())
12412 {
12413 mSerialPorts[slot]->i_rollback();
12414 serialPorts[slot] = mSerialPorts[slot];
12415 }
12416
12417 if (mData->flModifications & IsModified_ParallelPorts)
12418 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12419 if ( mParallelPorts[slot]
12420 && mParallelPorts[slot]->i_isModified())
12421 {
12422 mParallelPorts[slot]->i_rollback();
12423 parallelPorts[slot] = mParallelPorts[slot];
12424 }
12425
12426 if (aNotify)
12427 {
12428 /* inform the direct session about changes */
12429
12430 ComObjPtr<Machine> that = this;
12431 uint32_t flModifications = mData->flModifications;
12432 alock.release();
12433
12434 if (flModifications & IsModified_SharedFolders)
12435 that->i_onSharedFolderChange();
12436
12437 if (flModifications & IsModified_VRDEServer)
12438 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12439 if (flModifications & IsModified_USB)
12440 that->i_onUSBControllerChange();
12441
12442 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12443 if (networkAdapters[slot])
12444 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12445 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12446 if (serialPorts[slot])
12447 that->i_onSerialPortChange(serialPorts[slot]);
12448 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12449 if (parallelPorts[slot])
12450 that->i_onParallelPortChange(parallelPorts[slot]);
12451
12452 if (flModifications & IsModified_Storage)
12453 {
12454 for (StorageControllerList::const_iterator
12455 it = mStorageControllers->begin();
12456 it != mStorageControllers->end();
12457 ++it)
12458 {
12459 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
12460 }
12461 }
12462
12463 if (flModifications & IsModified_GuestDebugControl)
12464 that->i_onGuestDebugControlChange(mGuestDebugControl);
12465
12466#if 0
12467 if (flModifications & IsModified_BandwidthControl)
12468 that->onBandwidthControlChange();
12469#endif
12470 }
12471}
12472
12473/**
12474 * Commits all the changes to machine settings.
12475 *
12476 * Note that this operation is supposed to never fail.
12477 *
12478 * @note Locks this object and children for writing.
12479 */
12480void Machine::i_commit()
12481{
12482 AutoCaller autoCaller(this);
12483 AssertComRCReturnVoid(autoCaller.rc());
12484
12485 AutoCaller peerCaller(mPeer);
12486 AssertComRCReturnVoid(peerCaller.rc());
12487
12488 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12489
12490 /*
12491 * use safe commit to ensure Snapshot machines (that share mUserData)
12492 * will still refer to a valid memory location
12493 */
12494 mUserData.commitCopy();
12495
12496 mHWData.commit();
12497
12498 if (mMediumAttachments.isBackedUp())
12499 i_commitMedia(Global::IsOnline(mData->mMachineState));
12500
12501 mBIOSSettings->i_commit();
12502 mRecordingSettings->i_commit();
12503 mTrustedPlatformModule->i_commit();
12504 mNvramStore->i_commit();
12505 mGraphicsAdapter->i_commit();
12506 mVRDEServer->i_commit();
12507 mAudioSettings->i_commit();
12508 mUSBDeviceFilters->i_commit();
12509 mBandwidthControl->i_commit();
12510 mGuestDebugControl->i_commit();
12511
12512 /* Since mNetworkAdapters is a list which might have been changed (resized)
12513 * without using the Backupable<> template we need to handle the copying
12514 * of the list entries manually, including the creation of peers for the
12515 * new objects. */
12516 bool commitNetworkAdapters = false;
12517 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12518 if (mPeer)
12519 {
12520 /* commit everything, even the ones which will go away */
12521 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12522 mNetworkAdapters[slot]->i_commit();
12523 /* copy over the new entries, creating a peer and uninit the original */
12524 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12525 for (size_t slot = 0; slot < newSize; slot++)
12526 {
12527 /* look if this adapter has a peer device */
12528 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12529 if (!peer)
12530 {
12531 /* no peer means the adapter is a newly created one;
12532 * create a peer owning data this data share it with */
12533 peer.createObject();
12534 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12535 }
12536 mPeer->mNetworkAdapters[slot] = peer;
12537 }
12538 /* uninit any no longer needed network adapters */
12539 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12540 mNetworkAdapters[slot]->uninit();
12541 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12542 {
12543 if (mPeer->mNetworkAdapters[slot])
12544 mPeer->mNetworkAdapters[slot]->uninit();
12545 }
12546 /* Keep the original network adapter count until this point, so that
12547 * discarding a chipset type change will not lose settings. */
12548 mNetworkAdapters.resize(newSize);
12549 mPeer->mNetworkAdapters.resize(newSize);
12550 }
12551 else
12552 {
12553 /* we have no peer (our parent is the newly created machine);
12554 * just commit changes to the network adapters */
12555 commitNetworkAdapters = true;
12556 }
12557 if (commitNetworkAdapters)
12558 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12559 mNetworkAdapters[slot]->i_commit();
12560
12561 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12562 mSerialPorts[slot]->i_commit();
12563 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12564 mParallelPorts[slot]->i_commit();
12565
12566 bool commitStorageControllers = false;
12567
12568 if (mStorageControllers.isBackedUp())
12569 {
12570 mStorageControllers.commit();
12571
12572 if (mPeer)
12573 {
12574 /* Commit all changes to new controllers (this will reshare data with
12575 * peers for those who have peers) */
12576 StorageControllerList *newList = new StorageControllerList();
12577 for (StorageControllerList::const_iterator
12578 it = mStorageControllers->begin();
12579 it != mStorageControllers->end();
12580 ++it)
12581 {
12582 (*it)->i_commit();
12583
12584 /* look if this controller has a peer device */
12585 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12586 if (!peer)
12587 {
12588 /* no peer means the device is a newly created one;
12589 * create a peer owning data this device share it with */
12590 peer.createObject();
12591 peer->init(mPeer, *it, true /* aReshare */);
12592 }
12593 else
12594 {
12595 /* remove peer from the old list */
12596 mPeer->mStorageControllers->remove(peer);
12597 }
12598 /* and add it to the new list */
12599 newList->push_back(peer);
12600 }
12601
12602 /* uninit old peer's controllers that are left */
12603 for (StorageControllerList::const_iterator
12604 it = mPeer->mStorageControllers->begin();
12605 it != mPeer->mStorageControllers->end();
12606 ++it)
12607 {
12608 (*it)->uninit();
12609 }
12610
12611 /* attach new list of controllers to our peer */
12612 mPeer->mStorageControllers.attach(newList);
12613 }
12614 else
12615 {
12616 /* we have no peer (our parent is the newly created machine);
12617 * just commit changes to devices */
12618 commitStorageControllers = true;
12619 }
12620 }
12621 else
12622 {
12623 /* the list of controllers itself is not changed,
12624 * just commit changes to controllers themselves */
12625 commitStorageControllers = true;
12626 }
12627
12628 if (commitStorageControllers)
12629 {
12630 for (StorageControllerList::const_iterator
12631 it = mStorageControllers->begin();
12632 it != mStorageControllers->end();
12633 ++it)
12634 {
12635 (*it)->i_commit();
12636 }
12637 }
12638
12639 bool commitUSBControllers = false;
12640
12641 if (mUSBControllers.isBackedUp())
12642 {
12643 mUSBControllers.commit();
12644
12645 if (mPeer)
12646 {
12647 /* Commit all changes to new controllers (this will reshare data with
12648 * peers for those who have peers) */
12649 USBControllerList *newList = new USBControllerList();
12650 for (USBControllerList::const_iterator
12651 it = mUSBControllers->begin();
12652 it != mUSBControllers->end();
12653 ++it)
12654 {
12655 (*it)->i_commit();
12656
12657 /* look if this controller has a peer device */
12658 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12659 if (!peer)
12660 {
12661 /* no peer means the device is a newly created one;
12662 * create a peer owning data this device share it with */
12663 peer.createObject();
12664 peer->init(mPeer, *it, true /* aReshare */);
12665 }
12666 else
12667 {
12668 /* remove peer from the old list */
12669 mPeer->mUSBControllers->remove(peer);
12670 }
12671 /* and add it to the new list */
12672 newList->push_back(peer);
12673 }
12674
12675 /* uninit old peer's controllers that are left */
12676 for (USBControllerList::const_iterator
12677 it = mPeer->mUSBControllers->begin();
12678 it != mPeer->mUSBControllers->end();
12679 ++it)
12680 {
12681 (*it)->uninit();
12682 }
12683
12684 /* attach new list of controllers to our peer */
12685 mPeer->mUSBControllers.attach(newList);
12686 }
12687 else
12688 {
12689 /* we have no peer (our parent is the newly created machine);
12690 * just commit changes to devices */
12691 commitUSBControllers = true;
12692 }
12693 }
12694 else
12695 {
12696 /* the list of controllers itself is not changed,
12697 * just commit changes to controllers themselves */
12698 commitUSBControllers = true;
12699 }
12700
12701 if (commitUSBControllers)
12702 {
12703 for (USBControllerList::const_iterator
12704 it = mUSBControllers->begin();
12705 it != mUSBControllers->end();
12706 ++it)
12707 {
12708 (*it)->i_commit();
12709 }
12710 }
12711
12712 if (i_isSessionMachine())
12713 {
12714 /* attach new data to the primary machine and reshare it */
12715 mPeer->mUserData.attach(mUserData);
12716 mPeer->mHWData.attach(mHWData);
12717 /* mmMediumAttachments is reshared by fixupMedia */
12718 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12719 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12720 }
12721}
12722
12723/**
12724 * Copies all the hardware data from the given machine.
12725 *
12726 * Currently, only called when the VM is being restored from a snapshot. In
12727 * particular, this implies that the VM is not running during this method's
12728 * call.
12729 *
12730 * @note This method must be called from under this object's lock.
12731 *
12732 * @note This method doesn't call #i_commit(), so all data remains backed up and
12733 * unsaved.
12734 */
12735void Machine::i_copyFrom(Machine *aThat)
12736{
12737 AssertReturnVoid(!i_isSnapshotMachine());
12738 AssertReturnVoid(aThat->i_isSnapshotMachine());
12739
12740 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12741
12742 mHWData.assignCopy(aThat->mHWData);
12743
12744 // create copies of all shared folders (mHWData after attaching a copy
12745 // contains just references to original objects)
12746 for (HWData::SharedFolderList::iterator
12747 it = mHWData->mSharedFolders.begin();
12748 it != mHWData->mSharedFolders.end();
12749 ++it)
12750 {
12751 ComObjPtr<SharedFolder> folder;
12752 folder.createObject();
12753 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12754 AssertComRC(rc);
12755 *it = folder;
12756 }
12757
12758 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12759 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12760 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12761 mNvramStore->i_copyFrom(aThat->mNvramStore);
12762 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12763 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12764 mAudioSettings->i_copyFrom(aThat->mAudioSettings);
12765 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12766 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12767 mGuestDebugControl->i_copyFrom(aThat->mGuestDebugControl);
12768
12769 /* create private copies of all controllers */
12770 mStorageControllers.backup();
12771 mStorageControllers->clear();
12772 for (StorageControllerList::const_iterator
12773 it = aThat->mStorageControllers->begin();
12774 it != aThat->mStorageControllers->end();
12775 ++it)
12776 {
12777 ComObjPtr<StorageController> ctrl;
12778 ctrl.createObject();
12779 ctrl->initCopy(this, *it);
12780 mStorageControllers->push_back(ctrl);
12781 }
12782
12783 /* create private copies of all USB controllers */
12784 mUSBControllers.backup();
12785 mUSBControllers->clear();
12786 for (USBControllerList::const_iterator
12787 it = aThat->mUSBControllers->begin();
12788 it != aThat->mUSBControllers->end();
12789 ++it)
12790 {
12791 ComObjPtr<USBController> ctrl;
12792 ctrl.createObject();
12793 ctrl->initCopy(this, *it);
12794 mUSBControllers->push_back(ctrl);
12795 }
12796
12797 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12798 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12799 {
12800 if (mNetworkAdapters[slot].isNotNull())
12801 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12802 else
12803 {
12804 unconst(mNetworkAdapters[slot]).createObject();
12805 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12806 }
12807 }
12808 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12809 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12810 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12811 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12812}
12813
12814/**
12815 * Returns whether the given storage controller is hotplug capable.
12816 *
12817 * @returns true if the controller supports hotplugging
12818 * false otherwise.
12819 * @param enmCtrlType The controller type to check for.
12820 */
12821bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12822{
12823 ComPtr<ISystemProperties> systemProperties;
12824 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12825 if (FAILED(rc))
12826 return false;
12827
12828 BOOL aHotplugCapable = FALSE;
12829 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12830
12831 return RT_BOOL(aHotplugCapable);
12832}
12833
12834#ifdef VBOX_WITH_RESOURCE_USAGE_API
12835
12836void Machine::i_getDiskList(MediaList &list)
12837{
12838 for (MediumAttachmentList::const_iterator
12839 it = mMediumAttachments->begin();
12840 it != mMediumAttachments->end();
12841 ++it)
12842 {
12843 MediumAttachment *pAttach = *it;
12844 /* just in case */
12845 AssertContinue(pAttach);
12846
12847 AutoCaller localAutoCallerA(pAttach);
12848 if (FAILED(localAutoCallerA.rc())) continue;
12849
12850 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12851
12852 if (pAttach->i_getType() == DeviceType_HardDisk)
12853 list.push_back(pAttach->i_getMedium());
12854 }
12855}
12856
12857void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12858{
12859 AssertReturnVoid(isWriteLockOnCurrentThread());
12860 AssertPtrReturnVoid(aCollector);
12861
12862 pm::CollectorHAL *hal = aCollector->getHAL();
12863 /* Create sub metrics */
12864 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12865 "Percentage of processor time spent in user mode by the VM process.");
12866 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12867 "Percentage of processor time spent in kernel mode by the VM process.");
12868 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12869 "Size of resident portion of VM process in memory.");
12870 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12871 "Actual size of all VM disks combined.");
12872 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12873 "Network receive rate.");
12874 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12875 "Network transmit rate.");
12876 /* Create and register base metrics */
12877 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12878 cpuLoadUser, cpuLoadKernel);
12879 aCollector->registerBaseMetric(cpuLoad);
12880 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12881 ramUsageUsed);
12882 aCollector->registerBaseMetric(ramUsage);
12883 MediaList disks;
12884 i_getDiskList(disks);
12885 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12886 diskUsageUsed);
12887 aCollector->registerBaseMetric(diskUsage);
12888
12889 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12890 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12891 new pm::AggregateAvg()));
12892 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12893 new pm::AggregateMin()));
12894 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12895 new pm::AggregateMax()));
12896 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12897 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12898 new pm::AggregateAvg()));
12899 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12900 new pm::AggregateMin()));
12901 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12902 new pm::AggregateMax()));
12903
12904 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12905 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12906 new pm::AggregateAvg()));
12907 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12908 new pm::AggregateMin()));
12909 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12910 new pm::AggregateMax()));
12911
12912 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12913 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12914 new pm::AggregateAvg()));
12915 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12916 new pm::AggregateMin()));
12917 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12918 new pm::AggregateMax()));
12919
12920
12921 /* Guest metrics collector */
12922 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12923 aCollector->registerGuest(mCollectorGuest);
12924 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12925
12926 /* Create sub metrics */
12927 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12928 "Percentage of processor time spent in user mode as seen by the guest.");
12929 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12930 "Percentage of processor time spent in kernel mode as seen by the guest.");
12931 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12932 "Percentage of processor time spent idling as seen by the guest.");
12933
12934 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12935 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12936 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12937 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12938 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12939 pm::SubMetric *guestMemCache = new pm::SubMetric(
12940 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12941
12942 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12943 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12944
12945 /* Create and register base metrics */
12946 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12947 machineNetRx, machineNetTx);
12948 aCollector->registerBaseMetric(machineNetRate);
12949
12950 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12951 guestLoadUser, guestLoadKernel, guestLoadIdle);
12952 aCollector->registerBaseMetric(guestCpuLoad);
12953
12954 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12955 guestMemTotal, guestMemFree,
12956 guestMemBalloon, guestMemShared,
12957 guestMemCache, guestPagedTotal);
12958 aCollector->registerBaseMetric(guestCpuMem);
12959
12960 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12961 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12962 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12963 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12964
12965 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12966 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12967 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12968 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12969
12970 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12971 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12972 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12973 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12974
12975 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12976 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12977 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12978 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12979
12980 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12981 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12982 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12983 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12984
12985 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12986 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12987 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12988 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12989
12990 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12991 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12992 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12993 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12994
12995 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12996 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12997 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12998 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12999
13000 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
13001 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
13002 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
13003 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
13004
13005 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
13006 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
13007 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
13008 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
13009
13010 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
13011 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
13012 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
13013 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
13014}
13015
13016void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
13017{
13018 AssertReturnVoid(isWriteLockOnCurrentThread());
13019
13020 if (aCollector)
13021 {
13022 aCollector->unregisterMetricsFor(aMachine);
13023 aCollector->unregisterBaseMetricsFor(aMachine);
13024 }
13025}
13026
13027#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13028
13029
13030////////////////////////////////////////////////////////////////////////////////
13031
13032DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
13033
13034HRESULT SessionMachine::FinalConstruct()
13035{
13036 LogFlowThisFunc(("\n"));
13037
13038 mClientToken = NULL;
13039
13040 return BaseFinalConstruct();
13041}
13042
13043void SessionMachine::FinalRelease()
13044{
13045 LogFlowThisFunc(("\n"));
13046
13047 Assert(!mClientToken);
13048 /* paranoia, should not hang around any more */
13049 if (mClientToken)
13050 {
13051 delete mClientToken;
13052 mClientToken = NULL;
13053 }
13054
13055 uninit(Uninit::Unexpected);
13056
13057 BaseFinalRelease();
13058}
13059
13060/**
13061 * @note Must be called only by Machine::LockMachine() from its own write lock.
13062 */
13063HRESULT SessionMachine::init(Machine *aMachine)
13064{
13065 LogFlowThisFuncEnter();
13066 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
13067
13068 AssertReturn(aMachine, E_INVALIDARG);
13069
13070 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
13071
13072 /* Enclose the state transition NotReady->InInit->Ready */
13073 AutoInitSpan autoInitSpan(this);
13074 AssertReturn(autoInitSpan.isOk(), E_FAIL);
13075
13076 HRESULT rc = S_OK;
13077
13078 RT_ZERO(mAuthLibCtx);
13079
13080 /* create the machine client token */
13081 try
13082 {
13083 mClientToken = new ClientToken(aMachine, this);
13084 if (!mClientToken->isReady())
13085 {
13086 delete mClientToken;
13087 mClientToken = NULL;
13088 rc = E_FAIL;
13089 }
13090 }
13091 catch (std::bad_alloc &)
13092 {
13093 rc = E_OUTOFMEMORY;
13094 }
13095 if (FAILED(rc))
13096 return rc;
13097
13098 /* memorize the peer Machine */
13099 unconst(mPeer) = aMachine;
13100 /* share the parent pointer */
13101 unconst(mParent) = aMachine->mParent;
13102
13103 /* take the pointers to data to share */
13104 mData.share(aMachine->mData);
13105 mSSData.share(aMachine->mSSData);
13106
13107 mUserData.share(aMachine->mUserData);
13108 mHWData.share(aMachine->mHWData);
13109 mMediumAttachments.share(aMachine->mMediumAttachments);
13110
13111 mStorageControllers.allocate();
13112 for (StorageControllerList::const_iterator
13113 it = aMachine->mStorageControllers->begin();
13114 it != aMachine->mStorageControllers->end();
13115 ++it)
13116 {
13117 ComObjPtr<StorageController> ctl;
13118 ctl.createObject();
13119 ctl->init(this, *it);
13120 mStorageControllers->push_back(ctl);
13121 }
13122
13123 mUSBControllers.allocate();
13124 for (USBControllerList::const_iterator
13125 it = aMachine->mUSBControllers->begin();
13126 it != aMachine->mUSBControllers->end();
13127 ++it)
13128 {
13129 ComObjPtr<USBController> ctl;
13130 ctl.createObject();
13131 ctl->init(this, *it);
13132 mUSBControllers->push_back(ctl);
13133 }
13134
13135 unconst(mBIOSSettings).createObject();
13136 mBIOSSettings->init(this, aMachine->mBIOSSettings);
13137
13138 unconst(mRecordingSettings).createObject();
13139 mRecordingSettings->init(this, aMachine->mRecordingSettings);
13140
13141 unconst(mTrustedPlatformModule).createObject();
13142 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
13143
13144 unconst(mNvramStore).createObject();
13145 mNvramStore->init(this, aMachine->mNvramStore);
13146
13147 /* create another GraphicsAdapter object that will be mutable */
13148 unconst(mGraphicsAdapter).createObject();
13149 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
13150 /* create another VRDEServer object that will be mutable */
13151 unconst(mVRDEServer).createObject();
13152 mVRDEServer->init(this, aMachine->mVRDEServer);
13153 /* create another audio settings object that will be mutable */
13154 unconst(mAudioSettings).createObject();
13155 mAudioSettings->init(this, aMachine->mAudioSettings);
13156 /* create a list of serial ports that will be mutable */
13157 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
13158 {
13159 unconst(mSerialPorts[slot]).createObject();
13160 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
13161 }
13162 /* create a list of parallel ports that will be mutable */
13163 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
13164 {
13165 unconst(mParallelPorts[slot]).createObject();
13166 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
13167 }
13168
13169 /* create another USB device filters object that will be mutable */
13170 unconst(mUSBDeviceFilters).createObject();
13171 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
13172
13173 /* create a list of network adapters that will be mutable */
13174 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
13175 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13176 {
13177 unconst(mNetworkAdapters[slot]).createObject();
13178 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
13179 }
13180
13181 /* create another bandwidth control object that will be mutable */
13182 unconst(mBandwidthControl).createObject();
13183 mBandwidthControl->init(this, aMachine->mBandwidthControl);
13184
13185 unconst(mGuestDebugControl).createObject();
13186 mGuestDebugControl->init(this, aMachine->mGuestDebugControl);
13187
13188 /* default is to delete saved state on Saved -> PoweredOff transition */
13189 mRemoveSavedState = true;
13190
13191 /* Confirm a successful initialization when it's the case */
13192 autoInitSpan.setSucceeded();
13193
13194 miNATNetworksStarted = 0;
13195
13196 LogFlowThisFuncLeave();
13197 return rc;
13198}
13199
13200/**
13201 * Uninitializes this session object. If the reason is other than
13202 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
13203 * or the client watcher code.
13204 *
13205 * @param aReason uninitialization reason
13206 *
13207 * @note Locks mParent + this object for writing.
13208 */
13209void SessionMachine::uninit(Uninit::Reason aReason)
13210{
13211 LogFlowThisFuncEnter();
13212 LogFlowThisFunc(("reason=%d\n", aReason));
13213
13214 /*
13215 * Strongly reference ourselves to prevent this object deletion after
13216 * mData->mSession.mMachine.setNull() below (which can release the last
13217 * reference and call the destructor). Important: this must be done before
13218 * accessing any members (and before AutoUninitSpan that does it as well).
13219 * This self reference will be released as the very last step on return.
13220 */
13221 ComObjPtr<SessionMachine> selfRef;
13222 if (aReason != Uninit::Unexpected)
13223 selfRef = this;
13224
13225 /* Enclose the state transition Ready->InUninit->NotReady */
13226 AutoUninitSpan autoUninitSpan(this);
13227 if (autoUninitSpan.uninitDone())
13228 {
13229 LogFlowThisFunc(("Already uninitialized\n"));
13230 LogFlowThisFuncLeave();
13231 return;
13232 }
13233
13234 if (autoUninitSpan.initFailed())
13235 {
13236 /* We've been called by init() because it's failed. It's not really
13237 * necessary (nor it's safe) to perform the regular uninit sequence
13238 * below, the following is enough.
13239 */
13240 LogFlowThisFunc(("Initialization failed.\n"));
13241 /* destroy the machine client token */
13242 if (mClientToken)
13243 {
13244 delete mClientToken;
13245 mClientToken = NULL;
13246 }
13247 uninitDataAndChildObjects();
13248 mData.free();
13249 unconst(mParent) = NULL;
13250 unconst(mPeer) = NULL;
13251 LogFlowThisFuncLeave();
13252 return;
13253 }
13254
13255 MachineState_T lastState;
13256 {
13257 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
13258 lastState = mData->mMachineState;
13259 }
13260 NOREF(lastState);
13261
13262#ifdef VBOX_WITH_USB
13263 // release all captured USB devices, but do this before requesting the locks below
13264 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
13265 {
13266 /* Console::captureUSBDevices() is called in the VM process only after
13267 * setting the machine state to Starting or Restoring.
13268 * Console::detachAllUSBDevices() will be called upon successful
13269 * termination. So, we need to release USB devices only if there was
13270 * an abnormal termination of a running VM.
13271 *
13272 * This is identical to SessionMachine::DetachAllUSBDevices except
13273 * for the aAbnormal argument. */
13274 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13275 AssertComRC(rc);
13276 NOREF(rc);
13277
13278 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13279 if (service)
13280 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
13281 }
13282#endif /* VBOX_WITH_USB */
13283
13284 // we need to lock this object in uninit() because the lock is shared
13285 // with mPeer (as well as data we modify below). mParent lock is needed
13286 // by several calls to it.
13287 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
13288
13289#ifdef VBOX_WITH_RESOURCE_USAGE_API
13290 /*
13291 * It is safe to call Machine::i_unregisterMetrics() here because
13292 * PerformanceCollector::samplerCallback no longer accesses guest methods
13293 * holding the lock.
13294 */
13295 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
13296 /* The guest must be unregistered after its metrics (@bugref{5949}). */
13297 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
13298 if (mCollectorGuest)
13299 {
13300 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
13301 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
13302 mCollectorGuest = NULL;
13303 }
13304#endif
13305
13306 if (aReason == Uninit::Abnormal)
13307 {
13308 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
13309
13310 /*
13311 * Move the VM to the 'Aborted' machine state unless we are restoring a
13312 * VM that was in the 'Saved' machine state. In that case, if the VM
13313 * fails before reaching either the 'Restoring' machine state or the
13314 * 'Running' machine state then we set the machine state to
13315 * 'AbortedSaved' in order to preserve the saved state file so that the
13316 * VM can be restored in the future.
13317 */
13318 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
13319 i_setMachineState(MachineState_AbortedSaved);
13320 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
13321 i_setMachineState(MachineState_Aborted);
13322 }
13323
13324 // any machine settings modified?
13325 if (mData->flModifications)
13326 {
13327 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
13328 i_rollback(false /* aNotify */);
13329 }
13330
13331 mData->mSession.mPID = NIL_RTPROCESS;
13332
13333 if (aReason == Uninit::Unexpected)
13334 {
13335 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
13336 * client watcher thread to update the set of machines that have open
13337 * sessions. */
13338 mParent->i_updateClientWatcher();
13339 }
13340
13341 /* uninitialize all remote controls */
13342 if (mData->mSession.mRemoteControls.size())
13343 {
13344 LogFlowThisFunc(("Closing remote sessions (%d):\n",
13345 mData->mSession.mRemoteControls.size()));
13346
13347 /* Always restart a the beginning, since the iterator is invalidated
13348 * by using erase(). */
13349 for (Data::Session::RemoteControlList::iterator
13350 it = mData->mSession.mRemoteControls.begin();
13351 it != mData->mSession.mRemoteControls.end();
13352 it = mData->mSession.mRemoteControls.begin())
13353 {
13354 ComPtr<IInternalSessionControl> pControl = *it;
13355 mData->mSession.mRemoteControls.erase(it);
13356 multilock.release();
13357 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
13358 HRESULT rc = pControl->Uninitialize();
13359 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
13360 if (FAILED(rc))
13361 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
13362 multilock.acquire();
13363 }
13364 mData->mSession.mRemoteControls.clear();
13365 }
13366
13367 /* Remove all references to the NAT network service. The service will stop
13368 * if all references (also from other VMs) are removed. */
13369 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
13370 {
13371 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13372 {
13373 BOOL enabled;
13374 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13375 if ( FAILED(hrc)
13376 || !enabled)
13377 continue;
13378
13379 NetworkAttachmentType_T type;
13380 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13381 if ( SUCCEEDED(hrc)
13382 && type == NetworkAttachmentType_NATNetwork)
13383 {
13384 Bstr name;
13385 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13386 if (SUCCEEDED(hrc))
13387 {
13388 multilock.release();
13389 Utf8Str strName(name);
13390 LogRel(("VM '%s' stops using NAT network '%s'\n",
13391 mUserData->s.strName.c_str(), strName.c_str()));
13392 mParent->i_natNetworkRefDec(strName);
13393 multilock.acquire();
13394 }
13395 }
13396 }
13397 }
13398
13399 /*
13400 * An expected uninitialization can come only from #i_checkForDeath().
13401 * Otherwise it means that something's gone really wrong (for example,
13402 * the Session implementation has released the VirtualBox reference
13403 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
13404 * etc). However, it's also possible, that the client releases the IPC
13405 * semaphore correctly (i.e. before it releases the VirtualBox reference),
13406 * but the VirtualBox release event comes first to the server process.
13407 * This case is practically possible, so we should not assert on an
13408 * unexpected uninit, just log a warning.
13409 */
13410
13411 if (aReason == Uninit::Unexpected)
13412 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
13413
13414 if (aReason != Uninit::Normal)
13415 {
13416 mData->mSession.mDirectControl.setNull();
13417 }
13418 else
13419 {
13420 /* this must be null here (see #OnSessionEnd()) */
13421 Assert(mData->mSession.mDirectControl.isNull());
13422 Assert(mData->mSession.mState == SessionState_Unlocking);
13423 Assert(!mData->mSession.mProgress.isNull());
13424 }
13425 if (mData->mSession.mProgress)
13426 {
13427 if (aReason == Uninit::Normal)
13428 mData->mSession.mProgress->i_notifyComplete(S_OK);
13429 else
13430 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
13431 COM_IIDOF(ISession),
13432 getComponentName(),
13433 tr("The VM session was aborted"));
13434 mData->mSession.mProgress.setNull();
13435 }
13436
13437 if (mConsoleTaskData.mProgress)
13438 {
13439 Assert(aReason == Uninit::Abnormal);
13440 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
13441 COM_IIDOF(ISession),
13442 getComponentName(),
13443 tr("The VM session was aborted"));
13444 mConsoleTaskData.mProgress.setNull();
13445 }
13446
13447 /* remove the association between the peer machine and this session machine */
13448 Assert( (SessionMachine*)mData->mSession.mMachine == this
13449 || aReason == Uninit::Unexpected);
13450
13451 /* reset the rest of session data */
13452 mData->mSession.mLockType = LockType_Null;
13453 mData->mSession.mMachine.setNull();
13454 mData->mSession.mState = SessionState_Unlocked;
13455 mData->mSession.mName.setNull();
13456
13457 /* destroy the machine client token before leaving the exclusive lock */
13458 if (mClientToken)
13459 {
13460 delete mClientToken;
13461 mClientToken = NULL;
13462 }
13463
13464 /* fire an event */
13465 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
13466
13467 uninitDataAndChildObjects();
13468
13469 /* free the essential data structure last */
13470 mData.free();
13471
13472 /* release the exclusive lock before setting the below two to NULL */
13473 multilock.release();
13474
13475 unconst(mParent) = NULL;
13476 unconst(mPeer) = NULL;
13477
13478 AuthLibUnload(&mAuthLibCtx);
13479
13480 LogFlowThisFuncLeave();
13481}
13482
13483// util::Lockable interface
13484////////////////////////////////////////////////////////////////////////////////
13485
13486/**
13487 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13488 * with the primary Machine instance (mPeer).
13489 */
13490RWLockHandle *SessionMachine::lockHandle() const
13491{
13492 AssertReturn(mPeer != NULL, NULL);
13493 return mPeer->lockHandle();
13494}
13495
13496// IInternalMachineControl methods
13497////////////////////////////////////////////////////////////////////////////////
13498
13499/**
13500 * Passes collected guest statistics to performance collector object
13501 */
13502HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13503 ULONG aCpuKernel, ULONG aCpuIdle,
13504 ULONG aMemTotal, ULONG aMemFree,
13505 ULONG aMemBalloon, ULONG aMemShared,
13506 ULONG aMemCache, ULONG aPageTotal,
13507 ULONG aAllocVMM, ULONG aFreeVMM,
13508 ULONG aBalloonedVMM, ULONG aSharedVMM,
13509 ULONG aVmNetRx, ULONG aVmNetTx)
13510{
13511#ifdef VBOX_WITH_RESOURCE_USAGE_API
13512 if (mCollectorGuest)
13513 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13514 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13515 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13516 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13517
13518 return S_OK;
13519#else
13520 NOREF(aValidStats);
13521 NOREF(aCpuUser);
13522 NOREF(aCpuKernel);
13523 NOREF(aCpuIdle);
13524 NOREF(aMemTotal);
13525 NOREF(aMemFree);
13526 NOREF(aMemBalloon);
13527 NOREF(aMemShared);
13528 NOREF(aMemCache);
13529 NOREF(aPageTotal);
13530 NOREF(aAllocVMM);
13531 NOREF(aFreeVMM);
13532 NOREF(aBalloonedVMM);
13533 NOREF(aSharedVMM);
13534 NOREF(aVmNetRx);
13535 NOREF(aVmNetTx);
13536 return E_NOTIMPL;
13537#endif
13538}
13539
13540////////////////////////////////////////////////////////////////////////////////
13541//
13542// SessionMachine task records
13543//
13544////////////////////////////////////////////////////////////////////////////////
13545
13546/**
13547 * Task record for saving the machine state.
13548 */
13549class SessionMachine::SaveStateTask
13550 : public Machine::Task
13551{
13552public:
13553 SaveStateTask(SessionMachine *m,
13554 Progress *p,
13555 const Utf8Str &t,
13556 Reason_T enmReason,
13557 const Utf8Str &strStateFilePath)
13558 : Task(m, p, t),
13559 m_enmReason(enmReason),
13560 m_strStateFilePath(strStateFilePath)
13561 {}
13562
13563private:
13564 void handler()
13565 {
13566 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13567 }
13568
13569 Reason_T m_enmReason;
13570 Utf8Str m_strStateFilePath;
13571
13572 friend class SessionMachine;
13573};
13574
13575/**
13576 * Task thread implementation for SessionMachine::SaveState(), called from
13577 * SessionMachine::taskHandler().
13578 *
13579 * @note Locks this object for writing.
13580 *
13581 * @param task
13582 * @return
13583 */
13584void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13585{
13586 LogFlowThisFuncEnter();
13587
13588 AutoCaller autoCaller(this);
13589 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13590 if (FAILED(autoCaller.rc()))
13591 {
13592 /* we might have been uninitialized because the session was accidentally
13593 * closed by the client, so don't assert */
13594 HRESULT rc = setError(E_FAIL,
13595 tr("The session has been accidentally closed"));
13596 task.m_pProgress->i_notifyComplete(rc);
13597 LogFlowThisFuncLeave();
13598 return;
13599 }
13600
13601 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13602
13603 HRESULT rc = S_OK;
13604
13605 try
13606 {
13607 ComPtr<IInternalSessionControl> directControl;
13608 if (mData->mSession.mLockType == LockType_VM)
13609 directControl = mData->mSession.mDirectControl;
13610 if (directControl.isNull())
13611 throw setError(VBOX_E_INVALID_VM_STATE,
13612 tr("Trying to save state without a running VM"));
13613 alock.release();
13614 BOOL fSuspendedBySave;
13615 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13616 Assert(!fSuspendedBySave);
13617 alock.acquire();
13618
13619 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13620 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13621 throw E_FAIL);
13622
13623 if (SUCCEEDED(rc))
13624 {
13625 mSSData->strStateFilePath = task.m_strStateFilePath;
13626
13627 /* save all VM settings */
13628 rc = i_saveSettings(NULL, alock);
13629 // no need to check whether VirtualBox.xml needs saving also since
13630 // we can't have a name change pending at this point
13631 }
13632 else
13633 {
13634 // On failure, set the state to the state we had at the beginning.
13635 i_setMachineState(task.m_machineStateBackup);
13636 i_updateMachineStateOnClient();
13637
13638 // Delete the saved state file (might have been already created).
13639 // No need to check whether this is shared with a snapshot here
13640 // because we certainly created a fresh saved state file here.
13641 i_deleteFile(task.m_strStateFilePath, true /* fIgnoreFailures */);
13642 }
13643 }
13644 catch (HRESULT aRC) { rc = aRC; }
13645
13646 task.m_pProgress->i_notifyComplete(rc);
13647
13648 LogFlowThisFuncLeave();
13649}
13650
13651/**
13652 * @note Locks this object for writing.
13653 */
13654HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13655{
13656 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13657}
13658
13659HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13660{
13661 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13662
13663 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13664 if (FAILED(rc)) return rc;
13665
13666 if ( mData->mMachineState != MachineState_Running
13667 && mData->mMachineState != MachineState_Paused
13668 )
13669 return setError(VBOX_E_INVALID_VM_STATE,
13670 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13671 Global::stringifyMachineState(mData->mMachineState));
13672
13673 ComObjPtr<Progress> pProgress;
13674 pProgress.createObject();
13675 rc = pProgress->init(i_getVirtualBox(),
13676 static_cast<IMachine *>(this) /* aInitiator */,
13677 tr("Saving the execution state of the virtual machine"),
13678 FALSE /* aCancelable */);
13679 if (FAILED(rc))
13680 return rc;
13681
13682 Utf8Str strStateFilePath;
13683 i_composeSavedStateFilename(strStateFilePath);
13684
13685 /* create and start the task on a separate thread (note that it will not
13686 * start working until we release alock) */
13687 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13688 rc = pTask->createThread();
13689 if (FAILED(rc))
13690 return rc;
13691
13692 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13693 i_setMachineState(MachineState_Saving);
13694 i_updateMachineStateOnClient();
13695
13696 pProgress.queryInterfaceTo(aProgress.asOutParam());
13697
13698 return S_OK;
13699}
13700
13701/**
13702 * @note Locks this object for writing.
13703 */
13704HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13705{
13706 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13707
13708 HRESULT rc = i_checkStateDependency(MutableStateDep);
13709 if (FAILED(rc)) return rc;
13710
13711 if ( mData->mMachineState != MachineState_PoweredOff
13712 && mData->mMachineState != MachineState_Teleported
13713 && mData->mMachineState != MachineState_Aborted
13714 )
13715 return setError(VBOX_E_INVALID_VM_STATE,
13716 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13717 Global::stringifyMachineState(mData->mMachineState));
13718
13719 com::Utf8Str stateFilePathFull;
13720 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13721 if (RT_FAILURE(vrc))
13722 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13723 tr("Invalid saved state file path '%s' (%Rrc)"),
13724 aSavedStateFile.c_str(),
13725 vrc);
13726
13727 mSSData->strStateFilePath = stateFilePathFull;
13728
13729 /* The below i_setMachineState() will detect the state transition and will
13730 * update the settings file */
13731
13732 return i_setMachineState(MachineState_Saved);
13733}
13734
13735/**
13736 * @note Locks this object for writing.
13737 */
13738HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13739{
13740 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13741
13742 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13743 if (FAILED(rc)) return rc;
13744
13745 if ( mData->mMachineState != MachineState_Saved
13746 && mData->mMachineState != MachineState_AbortedSaved)
13747 return setError(VBOX_E_INVALID_VM_STATE,
13748 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13749 Global::stringifyMachineState(mData->mMachineState));
13750
13751 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13752
13753 /*
13754 * Saved -> PoweredOff transition will be detected in the SessionMachine
13755 * and properly handled.
13756 */
13757 rc = i_setMachineState(MachineState_PoweredOff);
13758 return rc;
13759}
13760
13761
13762/**
13763 * @note Locks the same as #i_setMachineState() does.
13764 */
13765HRESULT SessionMachine::updateState(MachineState_T aState)
13766{
13767 return i_setMachineState(aState);
13768}
13769
13770/**
13771 * @note Locks this object for writing.
13772 */
13773HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13774{
13775 IProgress *pProgress(aProgress);
13776
13777 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13778
13779 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13780
13781 if (mData->mSession.mState != SessionState_Locked)
13782 return VBOX_E_INVALID_OBJECT_STATE;
13783
13784 if (!mData->mSession.mProgress.isNull())
13785 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13786
13787 /* If we didn't reference the NAT network service yet, add a reference to
13788 * force a start */
13789 if (miNATNetworksStarted < 1)
13790 {
13791 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13792 {
13793 BOOL enabled;
13794 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13795 if ( FAILED(hrc)
13796 || !enabled)
13797 continue;
13798
13799 NetworkAttachmentType_T type;
13800 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13801 if ( SUCCEEDED(hrc)
13802 && type == NetworkAttachmentType_NATNetwork)
13803 {
13804 Bstr name;
13805 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13806 if (SUCCEEDED(hrc))
13807 {
13808 Utf8Str strName(name);
13809 LogRel(("VM '%s' starts using NAT network '%s'\n",
13810 mUserData->s.strName.c_str(), strName.c_str()));
13811 mPeer->lockHandle()->unlockWrite();
13812 mParent->i_natNetworkRefInc(strName);
13813#ifdef RT_LOCK_STRICT
13814 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13815#else
13816 mPeer->lockHandle()->lockWrite();
13817#endif
13818 }
13819 }
13820 }
13821 miNATNetworksStarted++;
13822 }
13823
13824 LogFlowThisFunc(("returns S_OK.\n"));
13825 return S_OK;
13826}
13827
13828/**
13829 * @note Locks this object for writing.
13830 */
13831HRESULT SessionMachine::endPowerUp(LONG aResult)
13832{
13833 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13834
13835 if (mData->mSession.mState != SessionState_Locked)
13836 return VBOX_E_INVALID_OBJECT_STATE;
13837
13838 /* Finalize the LaunchVMProcess progress object. */
13839 if (mData->mSession.mProgress)
13840 {
13841 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13842 mData->mSession.mProgress.setNull();
13843 }
13844
13845 if (SUCCEEDED((HRESULT)aResult))
13846 {
13847#ifdef VBOX_WITH_RESOURCE_USAGE_API
13848 /* The VM has been powered up successfully, so it makes sense
13849 * now to offer the performance metrics for a running machine
13850 * object. Doing it earlier wouldn't be safe. */
13851 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13852 mData->mSession.mPID);
13853#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13854 }
13855
13856 return S_OK;
13857}
13858
13859/**
13860 * @note Locks this object for writing.
13861 */
13862HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13863{
13864 LogFlowThisFuncEnter();
13865
13866 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13867
13868 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13869 E_FAIL);
13870
13871 /* create a progress object to track operation completion */
13872 ComObjPtr<Progress> pProgress;
13873 pProgress.createObject();
13874 pProgress->init(i_getVirtualBox(),
13875 static_cast<IMachine *>(this) /* aInitiator */,
13876 tr("Stopping the virtual machine"),
13877 FALSE /* aCancelable */);
13878
13879 /* fill in the console task data */
13880 mConsoleTaskData.mLastState = mData->mMachineState;
13881 mConsoleTaskData.mProgress = pProgress;
13882
13883 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13884 i_setMachineState(MachineState_Stopping);
13885
13886 pProgress.queryInterfaceTo(aProgress.asOutParam());
13887
13888 return S_OK;
13889}
13890
13891/**
13892 * @note Locks this object for writing.
13893 */
13894HRESULT SessionMachine::endPoweringDown(LONG aResult,
13895 const com::Utf8Str &aErrMsg)
13896{
13897 HRESULT const hrcResult = (HRESULT)aResult;
13898 LogFlowThisFuncEnter();
13899
13900 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13901
13902 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13903 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13904 && mConsoleTaskData.mLastState != MachineState_Null,
13905 E_FAIL);
13906
13907 /*
13908 * On failure, set the state to the state we had when BeginPoweringDown()
13909 * was called (this is expected by Console::PowerDown() and the associated
13910 * task). On success the VM process already changed the state to
13911 * MachineState_PoweredOff, so no need to do anything.
13912 */
13913 if (FAILED(hrcResult))
13914 i_setMachineState(mConsoleTaskData.mLastState);
13915
13916 /* notify the progress object about operation completion */
13917 Assert(mConsoleTaskData.mProgress);
13918 if (SUCCEEDED(hrcResult))
13919 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13920 else
13921 {
13922 if (aErrMsg.length())
13923 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13924 COM_IIDOF(ISession),
13925 getComponentName(),
13926 aErrMsg.c_str());
13927 else
13928 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13929 }
13930
13931 /* clear out the temporary saved state data */
13932 mConsoleTaskData.mLastState = MachineState_Null;
13933 mConsoleTaskData.mProgress.setNull();
13934
13935 LogFlowThisFuncLeave();
13936 return S_OK;
13937}
13938
13939
13940/**
13941 * Goes through the USB filters of the given machine to see if the given
13942 * device matches any filter or not.
13943 *
13944 * @note Locks the same as USBController::hasMatchingFilter() does.
13945 */
13946HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13947 BOOL *aMatched,
13948 ULONG *aMaskedInterfaces)
13949{
13950 LogFlowThisFunc(("\n"));
13951
13952#ifdef VBOX_WITH_USB
13953 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13954#else
13955 NOREF(aDevice);
13956 NOREF(aMaskedInterfaces);
13957 *aMatched = FALSE;
13958#endif
13959
13960 return S_OK;
13961}
13962
13963/**
13964 * @note Locks the same as Host::captureUSBDevice() does.
13965 */
13966HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13967{
13968 LogFlowThisFunc(("\n"));
13969
13970#ifdef VBOX_WITH_USB
13971 /* if captureDeviceForVM() fails, it must have set extended error info */
13972 clearError();
13973 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13974 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13975 return rc;
13976
13977 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13978 AssertReturn(service, E_FAIL);
13979 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13980#else
13981 RT_NOREF(aId, aCaptureFilename);
13982 return E_NOTIMPL;
13983#endif
13984}
13985
13986/**
13987 * @note Locks the same as Host::detachUSBDevice() does.
13988 */
13989HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13990 BOOL aDone)
13991{
13992 LogFlowThisFunc(("\n"));
13993
13994#ifdef VBOX_WITH_USB
13995 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13996 AssertReturn(service, E_FAIL);
13997 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13998#else
13999 NOREF(aId);
14000 NOREF(aDone);
14001 return E_NOTIMPL;
14002#endif
14003}
14004
14005/**
14006 * Inserts all machine filters to the USB proxy service and then calls
14007 * Host::autoCaptureUSBDevices().
14008 *
14009 * Called by Console from the VM process upon VM startup.
14010 *
14011 * @note Locks what called methods lock.
14012 */
14013HRESULT SessionMachine::autoCaptureUSBDevices()
14014{
14015 LogFlowThisFunc(("\n"));
14016
14017#ifdef VBOX_WITH_USB
14018 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
14019 AssertComRC(rc);
14020 NOREF(rc);
14021
14022 USBProxyService *service = mParent->i_host()->i_usbProxyService();
14023 AssertReturn(service, E_FAIL);
14024 return service->autoCaptureDevicesForVM(this);
14025#else
14026 return S_OK;
14027#endif
14028}
14029
14030/**
14031 * Removes all machine filters from the USB proxy service and then calls
14032 * Host::detachAllUSBDevices().
14033 *
14034 * Called by Console from the VM process upon normal VM termination or by
14035 * SessionMachine::uninit() upon abnormal VM termination (from under the
14036 * Machine/SessionMachine lock).
14037 *
14038 * @note Locks what called methods lock.
14039 */
14040HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
14041{
14042 LogFlowThisFunc(("\n"));
14043
14044#ifdef VBOX_WITH_USB
14045 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
14046 AssertComRC(rc);
14047 NOREF(rc);
14048
14049 USBProxyService *service = mParent->i_host()->i_usbProxyService();
14050 AssertReturn(service, E_FAIL);
14051 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
14052#else
14053 NOREF(aDone);
14054 return S_OK;
14055#endif
14056}
14057
14058/**
14059 * @note Locks this object for writing.
14060 */
14061HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
14062 ComPtr<IProgress> &aProgress)
14063{
14064 LogFlowThisFuncEnter();
14065
14066 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
14067 /*
14068 * We don't assert below because it might happen that a non-direct session
14069 * informs us it is closed right after we've been uninitialized -- it's ok.
14070 */
14071
14072 /* get IInternalSessionControl interface */
14073 ComPtr<IInternalSessionControl> control(aSession);
14074
14075 ComAssertRet(!control.isNull(), E_INVALIDARG);
14076
14077 /* Creating a Progress object requires the VirtualBox lock, and
14078 * thus locking it here is required by the lock order rules. */
14079 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
14080
14081 if (control == mData->mSession.mDirectControl)
14082 {
14083 /* The direct session is being normally closed by the client process
14084 * ----------------------------------------------------------------- */
14085
14086 /* go to the closing state (essential for all open*Session() calls and
14087 * for #i_checkForDeath()) */
14088 Assert(mData->mSession.mState == SessionState_Locked);
14089 mData->mSession.mState = SessionState_Unlocking;
14090
14091 /* set direct control to NULL to release the remote instance */
14092 mData->mSession.mDirectControl.setNull();
14093 LogFlowThisFunc(("Direct control is set to NULL\n"));
14094
14095 if (mData->mSession.mProgress)
14096 {
14097 /* finalize the progress, someone might wait if a frontend
14098 * closes the session before powering on the VM. */
14099 mData->mSession.mProgress->notifyComplete(E_FAIL,
14100 COM_IIDOF(ISession),
14101 getComponentName(),
14102 tr("The VM session was closed before any attempt to power it on"));
14103 mData->mSession.mProgress.setNull();
14104 }
14105
14106 /* Create the progress object the client will use to wait until
14107 * #i_checkForDeath() is called to uninitialize this session object after
14108 * it releases the IPC semaphore.
14109 * Note! Because we're "reusing" mProgress here, this must be a proxy
14110 * object just like for LaunchVMProcess. */
14111 Assert(mData->mSession.mProgress.isNull());
14112 ComObjPtr<ProgressProxy> progress;
14113 progress.createObject();
14114 ComPtr<IUnknown> pPeer(mPeer);
14115 progress->init(mParent, pPeer,
14116 Bstr(tr("Closing session")).raw(),
14117 FALSE /* aCancelable */);
14118 progress.queryInterfaceTo(aProgress.asOutParam());
14119 mData->mSession.mProgress = progress;
14120 }
14121 else
14122 {
14123 /* the remote session is being normally closed */
14124 bool found = false;
14125 for (Data::Session::RemoteControlList::iterator
14126 it = mData->mSession.mRemoteControls.begin();
14127 it != mData->mSession.mRemoteControls.end();
14128 ++it)
14129 {
14130 if (control == *it)
14131 {
14132 found = true;
14133 // This MUST be erase(it), not remove(*it) as the latter
14134 // triggers a very nasty use after free due to the place where
14135 // the value "lives".
14136 mData->mSession.mRemoteControls.erase(it);
14137 break;
14138 }
14139 }
14140 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
14141 E_INVALIDARG);
14142 }
14143
14144 /* signal the client watcher thread, because the client is going away */
14145 mParent->i_updateClientWatcher();
14146
14147 LogFlowThisFuncLeave();
14148 return S_OK;
14149}
14150
14151HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14152 std::vector<com::Utf8Str> &aValues,
14153 std::vector<LONG64> &aTimestamps,
14154 std::vector<com::Utf8Str> &aFlags)
14155{
14156 LogFlowThisFunc(("\n"));
14157
14158#ifdef VBOX_WITH_GUEST_PROPS
14159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14160
14161 size_t cEntries = mHWData->mGuestProperties.size();
14162 aNames.resize(cEntries);
14163 aValues.resize(cEntries);
14164 aTimestamps.resize(cEntries);
14165 aFlags.resize(cEntries);
14166
14167 size_t i = 0;
14168 for (HWData::GuestPropertyMap::const_iterator
14169 it = mHWData->mGuestProperties.begin();
14170 it != mHWData->mGuestProperties.end();
14171 ++it, ++i)
14172 {
14173 aNames[i] = it->first;
14174 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
14175 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
14176
14177 aValues[i] = it->second.strValue;
14178 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
14179 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
14180
14181 aTimestamps[i] = it->second.mTimestamp;
14182
14183 /* If it is NULL, keep it NULL. */
14184 if (it->second.mFlags)
14185 {
14186 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
14187 GuestPropWriteFlags(it->second.mFlags, szFlags);
14188 aFlags[i] = szFlags;
14189 }
14190 else
14191 aFlags[i] = "";
14192 }
14193 return S_OK;
14194#else
14195 ReturnComNotImplemented();
14196#endif
14197}
14198
14199HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
14200 const com::Utf8Str &aValue,
14201 LONG64 aTimestamp,
14202 const com::Utf8Str &aFlags,
14203 BOOL fWasDeleted)
14204{
14205 LogFlowThisFunc(("\n"));
14206
14207#ifdef VBOX_WITH_GUEST_PROPS
14208 try
14209 {
14210 /*
14211 * Convert input up front.
14212 */
14213 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
14214 if (aFlags.length())
14215 {
14216 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
14217 AssertRCReturn(vrc, E_INVALIDARG);
14218 }
14219
14220 /*
14221 * Now grab the object lock, validate the state and do the update.
14222 */
14223
14224 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14225
14226 if (!Global::IsOnline(mData->mMachineState))
14227 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
14228
14229 i_setModified(IsModified_MachineData);
14230 mHWData.backup();
14231
14232 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
14233 if (it != mHWData->mGuestProperties.end())
14234 {
14235 if (!fWasDeleted)
14236 {
14237 it->second.strValue = aValue;
14238 it->second.mTimestamp = aTimestamp;
14239 it->second.mFlags = fFlags;
14240 }
14241 else
14242 mHWData->mGuestProperties.erase(it);
14243
14244 mData->mGuestPropertiesModified = TRUE;
14245 }
14246 else if (!fWasDeleted)
14247 {
14248 HWData::GuestProperty prop;
14249 prop.strValue = aValue;
14250 prop.mTimestamp = aTimestamp;
14251 prop.mFlags = fFlags;
14252
14253 mHWData->mGuestProperties[aName] = prop;
14254 mData->mGuestPropertiesModified = TRUE;
14255 }
14256
14257 alock.release();
14258
14259 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fWasDeleted);
14260 }
14261 catch (...)
14262 {
14263 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
14264 }
14265 return S_OK;
14266#else
14267 ReturnComNotImplemented();
14268#endif
14269}
14270
14271
14272HRESULT SessionMachine::lockMedia()
14273{
14274 AutoMultiWriteLock2 alock(this->lockHandle(),
14275 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14276
14277 AssertReturn( mData->mMachineState == MachineState_Starting
14278 || mData->mMachineState == MachineState_Restoring
14279 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
14280
14281 clearError();
14282 alock.release();
14283 return i_lockMedia();
14284}
14285
14286HRESULT SessionMachine::unlockMedia()
14287{
14288 HRESULT hrc = i_unlockMedia();
14289 return hrc;
14290}
14291
14292HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14293 ComPtr<IMediumAttachment> &aNewAttachment)
14294{
14295 // request the host lock first, since might be calling Host methods for getting host drives;
14296 // next, protect the media tree all the while we're in here, as well as our member variables
14297 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
14298 this->lockHandle(),
14299 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14300
14301 IMediumAttachment *iAttach = aAttachment;
14302 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
14303
14304 Utf8Str ctrlName;
14305 LONG lPort;
14306 LONG lDevice;
14307 bool fTempEject;
14308 {
14309 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14310
14311 /* Need to query the details first, as the IMediumAttachment reference
14312 * might be to the original settings, which we are going to change. */
14313 ctrlName = pAttach->i_getControllerName();
14314 lPort = pAttach->i_getPort();
14315 lDevice = pAttach->i_getDevice();
14316 fTempEject = pAttach->i_getTempEject();
14317 }
14318
14319 if (!fTempEject)
14320 {
14321 /* Remember previously mounted medium. The medium before taking the
14322 * backup is not necessarily the same thing. */
14323 ComObjPtr<Medium> oldmedium;
14324 oldmedium = pAttach->i_getMedium();
14325
14326 i_setModified(IsModified_Storage);
14327 mMediumAttachments.backup();
14328
14329 // The backup operation makes the pAttach reference point to the
14330 // old settings. Re-get the correct reference.
14331 pAttach = i_findAttachment(*mMediumAttachments.data(),
14332 ctrlName,
14333 lPort,
14334 lDevice);
14335
14336 {
14337 AutoCaller autoAttachCaller(this);
14338 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
14339
14340 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14341 if (!oldmedium.isNull())
14342 oldmedium->i_removeBackReference(mData->mUuid);
14343
14344 pAttach->i_updateMedium(NULL);
14345 pAttach->i_updateEjected();
14346 }
14347
14348 i_setModified(IsModified_Storage);
14349 }
14350 else
14351 {
14352 {
14353 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14354 pAttach->i_updateEjected();
14355 }
14356 }
14357
14358 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
14359
14360 return S_OK;
14361}
14362
14363HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
14364 com::Utf8Str &aResult)
14365{
14366 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14367
14368 HRESULT hr = S_OK;
14369
14370 if (!mAuthLibCtx.hAuthLibrary)
14371 {
14372 /* Load the external authentication library. */
14373 Bstr authLibrary;
14374 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
14375
14376 Utf8Str filename = authLibrary;
14377
14378 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
14379 if (RT_FAILURE(vrc))
14380 hr = setErrorBoth(E_FAIL, vrc,
14381 tr("Could not load the external authentication library '%s' (%Rrc)"),
14382 filename.c_str(), vrc);
14383 }
14384
14385 /* The auth library might need the machine lock. */
14386 alock.release();
14387
14388 if (FAILED(hr))
14389 return hr;
14390
14391 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
14392 {
14393 enum VRDEAuthParams
14394 {
14395 parmUuid = 1,
14396 parmGuestJudgement,
14397 parmUser,
14398 parmPassword,
14399 parmDomain,
14400 parmClientId
14401 };
14402
14403 AuthResult result = AuthResultAccessDenied;
14404
14405 Guid uuid(aAuthParams[parmUuid]);
14406 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
14407 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
14408
14409 result = AuthLibAuthenticate(&mAuthLibCtx,
14410 uuid.raw(), guestJudgement,
14411 aAuthParams[parmUser].c_str(),
14412 aAuthParams[parmPassword].c_str(),
14413 aAuthParams[parmDomain].c_str(),
14414 u32ClientId);
14415
14416 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
14417 size_t cbPassword = aAuthParams[parmPassword].length();
14418 if (cbPassword)
14419 {
14420 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
14421 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
14422 }
14423
14424 if (result == AuthResultAccessGranted)
14425 aResult = "granted";
14426 else
14427 aResult = "denied";
14428
14429 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
14430 aAuthParams[parmUser].c_str(), aResult.c_str()));
14431 }
14432 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
14433 {
14434 enum VRDEAuthDisconnectParams
14435 {
14436 parmUuid = 1,
14437 parmClientId
14438 };
14439
14440 Guid uuid(aAuthParams[parmUuid]);
14441 uint32_t u32ClientId = 0;
14442 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
14443 }
14444 else
14445 {
14446 hr = E_INVALIDARG;
14447 }
14448
14449 return hr;
14450}
14451
14452// public methods only for internal purposes
14453/////////////////////////////////////////////////////////////////////////////
14454
14455#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14456/**
14457 * Called from the client watcher thread to check for expected or unexpected
14458 * death of the client process that has a direct session to this machine.
14459 *
14460 * On Win32 and on OS/2, this method is called only when we've got the
14461 * mutex (i.e. the client has either died or terminated normally) so it always
14462 * returns @c true (the client is terminated, the session machine is
14463 * uninitialized).
14464 *
14465 * On other platforms, the method returns @c true if the client process has
14466 * terminated normally or abnormally and the session machine was uninitialized,
14467 * and @c false if the client process is still alive.
14468 *
14469 * @note Locks this object for writing.
14470 */
14471bool SessionMachine::i_checkForDeath()
14472{
14473 Uninit::Reason reason;
14474 bool terminated = false;
14475
14476 /* Enclose autoCaller with a block because calling uninit() from under it
14477 * will deadlock. */
14478 {
14479 AutoCaller autoCaller(this);
14480 if (!autoCaller.isOk())
14481 {
14482 /* return true if not ready, to cause the client watcher to exclude
14483 * the corresponding session from watching */
14484 LogFlowThisFunc(("Already uninitialized!\n"));
14485 return true;
14486 }
14487
14488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14489
14490 /* Determine the reason of death: if the session state is Closing here,
14491 * everything is fine. Otherwise it means that the client did not call
14492 * OnSessionEnd() before it released the IPC semaphore. This may happen
14493 * either because the client process has abnormally terminated, or
14494 * because it simply forgot to call ISession::Close() before exiting. We
14495 * threat the latter also as an abnormal termination (see
14496 * Session::uninit() for details). */
14497 reason = mData->mSession.mState == SessionState_Unlocking ?
14498 Uninit::Normal :
14499 Uninit::Abnormal;
14500
14501 if (mClientToken)
14502 terminated = mClientToken->release();
14503 } /* AutoCaller block */
14504
14505 if (terminated)
14506 uninit(reason);
14507
14508 return terminated;
14509}
14510
14511void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14512{
14513 LogFlowThisFunc(("\n"));
14514
14515 strTokenId.setNull();
14516
14517 AutoCaller autoCaller(this);
14518 AssertComRCReturnVoid(autoCaller.rc());
14519
14520 Assert(mClientToken);
14521 if (mClientToken)
14522 mClientToken->getId(strTokenId);
14523}
14524#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14525IToken *SessionMachine::i_getToken()
14526{
14527 LogFlowThisFunc(("\n"));
14528
14529 AutoCaller autoCaller(this);
14530 AssertComRCReturn(autoCaller.rc(), NULL);
14531
14532 Assert(mClientToken);
14533 if (mClientToken)
14534 return mClientToken->getToken();
14535 else
14536 return NULL;
14537}
14538#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14539
14540Machine::ClientToken *SessionMachine::i_getClientToken()
14541{
14542 LogFlowThisFunc(("\n"));
14543
14544 AutoCaller autoCaller(this);
14545 AssertComRCReturn(autoCaller.rc(), NULL);
14546
14547 return mClientToken;
14548}
14549
14550
14551/**
14552 * @note Locks this object for reading.
14553 */
14554HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14555{
14556 LogFlowThisFunc(("\n"));
14557
14558 AutoCaller autoCaller(this);
14559 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14560
14561 ComPtr<IInternalSessionControl> directControl;
14562 {
14563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14564 if (mData->mSession.mLockType == LockType_VM)
14565 directControl = mData->mSession.mDirectControl;
14566 }
14567
14568 /* ignore notifications sent after #OnSessionEnd() is called */
14569 if (!directControl)
14570 return S_OK;
14571
14572 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14573}
14574
14575/**
14576 * @note Locks this object for reading.
14577 */
14578HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
14579 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
14580 const Utf8Str &aGuestIp, LONG aGuestPort)
14581{
14582 LogFlowThisFunc(("\n"));
14583
14584 AutoCaller autoCaller(this);
14585 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14586
14587 ComPtr<IInternalSessionControl> directControl;
14588 {
14589 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14590 if (mData->mSession.mLockType == LockType_VM)
14591 directControl = mData->mSession.mDirectControl;
14592 }
14593
14594 /* ignore notifications sent after #OnSessionEnd() is called */
14595 if (!directControl)
14596 return S_OK;
14597 /*
14598 * instead acting like callback we ask IVirtualBox deliver corresponding event
14599 */
14600
14601 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14602 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14603 return S_OK;
14604}
14605
14606/**
14607 * @note Locks this object for reading.
14608 */
14609HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14610{
14611 LogFlowThisFunc(("\n"));
14612
14613 AutoCaller autoCaller(this);
14614 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14615
14616 ComPtr<IInternalSessionControl> directControl;
14617 {
14618 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14619 if (mData->mSession.mLockType == LockType_VM)
14620 directControl = mData->mSession.mDirectControl;
14621 }
14622
14623 /* ignore notifications sent after #OnSessionEnd() is called */
14624 if (!directControl)
14625 return S_OK;
14626
14627 return directControl->OnAudioAdapterChange(audioAdapter);
14628}
14629
14630/**
14631 * @note Locks this object for reading.
14632 */
14633HRESULT SessionMachine::i_onHostAudioDeviceChange(IHostAudioDevice *aDevice, BOOL aNew, AudioDeviceState_T aState, IVirtualBoxErrorInfo *aErrInfo)
14634{
14635 LogFlowThisFunc(("\n"));
14636
14637 AutoCaller autoCaller(this);
14638 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14639
14640 ComPtr<IInternalSessionControl> directControl;
14641 {
14642 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14643 if (mData->mSession.mLockType == LockType_VM)
14644 directControl = mData->mSession.mDirectControl;
14645 }
14646
14647 /* ignore notifications sent after #OnSessionEnd() is called */
14648 if (!directControl)
14649 return S_OK;
14650
14651 return directControl->OnHostAudioDeviceChange(aDevice, aNew, aState, aErrInfo);
14652}
14653
14654/**
14655 * @note Locks this object for reading.
14656 */
14657HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14658{
14659 LogFlowThisFunc(("\n"));
14660
14661 AutoCaller autoCaller(this);
14662 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14663
14664 ComPtr<IInternalSessionControl> directControl;
14665 {
14666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14667 if (mData->mSession.mLockType == LockType_VM)
14668 directControl = mData->mSession.mDirectControl;
14669 }
14670
14671 /* ignore notifications sent after #OnSessionEnd() is called */
14672 if (!directControl)
14673 return S_OK;
14674
14675 return directControl->OnSerialPortChange(serialPort);
14676}
14677
14678/**
14679 * @note Locks this object for reading.
14680 */
14681HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14682{
14683 LogFlowThisFunc(("\n"));
14684
14685 AutoCaller autoCaller(this);
14686 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14687
14688 ComPtr<IInternalSessionControl> directControl;
14689 {
14690 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14691 if (mData->mSession.mLockType == LockType_VM)
14692 directControl = mData->mSession.mDirectControl;
14693 }
14694
14695 /* ignore notifications sent after #OnSessionEnd() is called */
14696 if (!directControl)
14697 return S_OK;
14698
14699 return directControl->OnParallelPortChange(parallelPort);
14700}
14701
14702/**
14703 * @note Locks this object for reading.
14704 */
14705HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14706{
14707 LogFlowThisFunc(("\n"));
14708
14709 AutoCaller autoCaller(this);
14710 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14711
14712 ComPtr<IInternalSessionControl> directControl;
14713 {
14714 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14715 if (mData->mSession.mLockType == LockType_VM)
14716 directControl = mData->mSession.mDirectControl;
14717 }
14718
14719 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14720
14721 /* ignore notifications sent after #OnSessionEnd() is called */
14722 if (!directControl)
14723 return S_OK;
14724
14725 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14726}
14727
14728/**
14729 * @note Locks this object for reading.
14730 */
14731HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14732{
14733 LogFlowThisFunc(("\n"));
14734
14735 AutoCaller autoCaller(this);
14736 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14737
14738 ComPtr<IInternalSessionControl> directControl;
14739 {
14740 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14741 if (mData->mSession.mLockType == LockType_VM)
14742 directControl = mData->mSession.mDirectControl;
14743 }
14744
14745 mParent->i_onMediumChanged(aAttachment);
14746
14747 /* ignore notifications sent after #OnSessionEnd() is called */
14748 if (!directControl)
14749 return S_OK;
14750
14751 return directControl->OnMediumChange(aAttachment, aForce);
14752}
14753
14754HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14755{
14756 LogFlowThisFunc(("\n"));
14757
14758 AutoCaller autoCaller(this);
14759 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14760
14761 ComPtr<IInternalSessionControl> directControl;
14762 {
14763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14764 if (mData->mSession.mLockType == LockType_VM)
14765 directControl = mData->mSession.mDirectControl;
14766 }
14767
14768 /* ignore notifications sent after #OnSessionEnd() is called */
14769 if (!directControl)
14770 return S_OK;
14771
14772 return directControl->OnVMProcessPriorityChange(aPriority);
14773}
14774
14775/**
14776 * @note Locks this object for reading.
14777 */
14778HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14779{
14780 LogFlowThisFunc(("\n"));
14781
14782 AutoCaller autoCaller(this);
14783 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14784
14785 ComPtr<IInternalSessionControl> directControl;
14786 {
14787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14788 if (mData->mSession.mLockType == LockType_VM)
14789 directControl = mData->mSession.mDirectControl;
14790 }
14791
14792 /* ignore notifications sent after #OnSessionEnd() is called */
14793 if (!directControl)
14794 return S_OK;
14795
14796 return directControl->OnCPUChange(aCPU, aRemove);
14797}
14798
14799HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14800{
14801 LogFlowThisFunc(("\n"));
14802
14803 AutoCaller autoCaller(this);
14804 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14805
14806 ComPtr<IInternalSessionControl> directControl;
14807 {
14808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14809 if (mData->mSession.mLockType == LockType_VM)
14810 directControl = mData->mSession.mDirectControl;
14811 }
14812
14813 /* ignore notifications sent after #OnSessionEnd() is called */
14814 if (!directControl)
14815 return S_OK;
14816
14817 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14818}
14819
14820/**
14821 * @note Locks this object for reading.
14822 */
14823HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14824{
14825 LogFlowThisFunc(("\n"));
14826
14827 AutoCaller autoCaller(this);
14828 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14829
14830 ComPtr<IInternalSessionControl> directControl;
14831 {
14832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14833 if (mData->mSession.mLockType == LockType_VM)
14834 directControl = mData->mSession.mDirectControl;
14835 }
14836
14837 /* ignore notifications sent after #OnSessionEnd() is called */
14838 if (!directControl)
14839 return S_OK;
14840
14841 return directControl->OnVRDEServerChange(aRestart);
14842}
14843
14844/**
14845 * @note Locks this object for reading.
14846 */
14847HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14848{
14849 LogFlowThisFunc(("\n"));
14850
14851 AutoCaller autoCaller(this);
14852 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14853
14854 ComPtr<IInternalSessionControl> directControl;
14855 {
14856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14857 if (mData->mSession.mLockType == LockType_VM)
14858 directControl = mData->mSession.mDirectControl;
14859 }
14860
14861 /* ignore notifications sent after #OnSessionEnd() is called */
14862 if (!directControl)
14863 return S_OK;
14864
14865 return directControl->OnRecordingChange(aEnable);
14866}
14867
14868/**
14869 * @note Locks this object for reading.
14870 */
14871HRESULT SessionMachine::i_onUSBControllerChange()
14872{
14873 LogFlowThisFunc(("\n"));
14874
14875 AutoCaller autoCaller(this);
14876 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14877
14878 ComPtr<IInternalSessionControl> directControl;
14879 {
14880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14881 if (mData->mSession.mLockType == LockType_VM)
14882 directControl = mData->mSession.mDirectControl;
14883 }
14884
14885 /* ignore notifications sent after #OnSessionEnd() is called */
14886 if (!directControl)
14887 return S_OK;
14888
14889 return directControl->OnUSBControllerChange();
14890}
14891
14892/**
14893 * @note Locks this object for reading.
14894 */
14895HRESULT SessionMachine::i_onSharedFolderChange()
14896{
14897 LogFlowThisFunc(("\n"));
14898
14899 AutoCaller autoCaller(this);
14900 AssertComRCReturnRC(autoCaller.rc());
14901
14902 ComPtr<IInternalSessionControl> directControl;
14903 {
14904 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14905 if (mData->mSession.mLockType == LockType_VM)
14906 directControl = mData->mSession.mDirectControl;
14907 }
14908
14909 /* ignore notifications sent after #OnSessionEnd() is called */
14910 if (!directControl)
14911 return S_OK;
14912
14913 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14914}
14915
14916/**
14917 * @note Locks this object for reading.
14918 */
14919HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14920{
14921 LogFlowThisFunc(("\n"));
14922
14923 AutoCaller autoCaller(this);
14924 AssertComRCReturnRC(autoCaller.rc());
14925
14926 ComPtr<IInternalSessionControl> directControl;
14927 {
14928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14929 if (mData->mSession.mLockType == LockType_VM)
14930 directControl = mData->mSession.mDirectControl;
14931 }
14932
14933 /* ignore notifications sent after #OnSessionEnd() is called */
14934 if (!directControl)
14935 return S_OK;
14936
14937 return directControl->OnClipboardModeChange(aClipboardMode);
14938}
14939
14940/**
14941 * @note Locks this object for reading.
14942 */
14943HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14944{
14945 LogFlowThisFunc(("\n"));
14946
14947 AutoCaller autoCaller(this);
14948 AssertComRCReturnRC(autoCaller.rc());
14949
14950 ComPtr<IInternalSessionControl> directControl;
14951 {
14952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14953 if (mData->mSession.mLockType == LockType_VM)
14954 directControl = mData->mSession.mDirectControl;
14955 }
14956
14957 /* ignore notifications sent after #OnSessionEnd() is called */
14958 if (!directControl)
14959 return S_OK;
14960
14961 return directControl->OnClipboardFileTransferModeChange(aEnable);
14962}
14963
14964/**
14965 * @note Locks this object for reading.
14966 */
14967HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14968{
14969 LogFlowThisFunc(("\n"));
14970
14971 AutoCaller autoCaller(this);
14972 AssertComRCReturnRC(autoCaller.rc());
14973
14974 ComPtr<IInternalSessionControl> directControl;
14975 {
14976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14977 if (mData->mSession.mLockType == LockType_VM)
14978 directControl = mData->mSession.mDirectControl;
14979 }
14980
14981 /* ignore notifications sent after #OnSessionEnd() is called */
14982 if (!directControl)
14983 return S_OK;
14984
14985 return directControl->OnDnDModeChange(aDnDMode);
14986}
14987
14988/**
14989 * @note Locks this object for reading.
14990 */
14991HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14992{
14993 LogFlowThisFunc(("\n"));
14994
14995 AutoCaller autoCaller(this);
14996 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14997
14998 ComPtr<IInternalSessionControl> directControl;
14999 {
15000 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15001 if (mData->mSession.mLockType == LockType_VM)
15002 directControl = mData->mSession.mDirectControl;
15003 }
15004
15005 /* ignore notifications sent after #OnSessionEnd() is called */
15006 if (!directControl)
15007 return S_OK;
15008
15009 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
15010}
15011
15012/**
15013 * @note Locks this object for reading.
15014 */
15015HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
15016{
15017 LogFlowThisFunc(("\n"));
15018
15019 AutoCaller autoCaller(this);
15020 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15021
15022 ComPtr<IInternalSessionControl> directControl;
15023 {
15024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15025 if (mData->mSession.mLockType == LockType_VM)
15026 directControl = mData->mSession.mDirectControl;
15027 }
15028
15029 /* ignore notifications sent after #OnSessionEnd() is called */
15030 if (!directControl)
15031 return S_OK;
15032
15033 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
15034}
15035
15036/**
15037 * @note Locks this object for reading.
15038 */
15039HRESULT SessionMachine::i_onGuestDebugControlChange(IGuestDebugControl *guestDebugControl)
15040{
15041 LogFlowThisFunc(("\n"));
15042
15043 AutoCaller autoCaller(this);
15044 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15045
15046 ComPtr<IInternalSessionControl> directControl;
15047 {
15048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15049 if (mData->mSession.mLockType == LockType_VM)
15050 directControl = mData->mSession.mDirectControl;
15051 }
15052
15053 /* ignore notifications sent after #OnSessionEnd() is called */
15054 if (!directControl)
15055 return S_OK;
15056
15057 return directControl->OnGuestDebugControlChange(guestDebugControl);
15058}
15059
15060/**
15061 * Returns @c true if this machine's USB controller reports it has a matching
15062 * filter for the given USB device and @c false otherwise.
15063 *
15064 * @note locks this object for reading.
15065 */
15066bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
15067{
15068 AutoCaller autoCaller(this);
15069 /* silently return if not ready -- this method may be called after the
15070 * direct machine session has been called */
15071 if (!autoCaller.isOk())
15072 return false;
15073
15074#ifdef VBOX_WITH_USB
15075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15076
15077 switch (mData->mMachineState)
15078 {
15079 case MachineState_Starting:
15080 case MachineState_Restoring:
15081 case MachineState_TeleportingIn:
15082 case MachineState_Paused:
15083 case MachineState_Running:
15084 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
15085 * elsewhere... */
15086 alock.release();
15087 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
15088 default: break;
15089 }
15090#else
15091 NOREF(aDevice);
15092 NOREF(aMaskedIfs);
15093#endif
15094 return false;
15095}
15096
15097/**
15098 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
15099 */
15100HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
15101 IVirtualBoxErrorInfo *aError,
15102 ULONG aMaskedIfs,
15103 const com::Utf8Str &aCaptureFilename)
15104{
15105 LogFlowThisFunc(("\n"));
15106
15107 AutoCaller autoCaller(this);
15108
15109 /* This notification may happen after the machine object has been
15110 * uninitialized (the session was closed), so don't assert. */
15111 if (FAILED(autoCaller.rc())) return autoCaller.rc();
15112
15113 ComPtr<IInternalSessionControl> directControl;
15114 {
15115 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15116 if (mData->mSession.mLockType == LockType_VM)
15117 directControl = mData->mSession.mDirectControl;
15118 }
15119
15120 /* fail on notifications sent after #OnSessionEnd() is called, it is
15121 * expected by the caller */
15122 if (!directControl)
15123 return E_FAIL;
15124
15125 /* No locks should be held at this point. */
15126 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
15127 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
15128
15129 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
15130}
15131
15132/**
15133 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
15134 */
15135HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
15136 IVirtualBoxErrorInfo *aError)
15137{
15138 LogFlowThisFunc(("\n"));
15139
15140 AutoCaller autoCaller(this);
15141
15142 /* This notification may happen after the machine object has been
15143 * uninitialized (the session was closed), so don't assert. */
15144 if (FAILED(autoCaller.rc())) return autoCaller.rc();
15145
15146 ComPtr<IInternalSessionControl> directControl;
15147 {
15148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15149 if (mData->mSession.mLockType == LockType_VM)
15150 directControl = mData->mSession.mDirectControl;
15151 }
15152
15153 /* fail on notifications sent after #OnSessionEnd() is called, it is
15154 * expected by the caller */
15155 if (!directControl)
15156 return E_FAIL;
15157
15158 /* No locks should be held at this point. */
15159 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
15160 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
15161
15162 return directControl->OnUSBDeviceDetach(aId, aError);
15163}
15164
15165// protected methods
15166/////////////////////////////////////////////////////////////////////////////
15167
15168/**
15169 * Deletes the given file if it is no longer in use by either the current machine state
15170 * (if the machine is "saved") or any of the machine's snapshots.
15171 *
15172 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
15173 * but is different for each SnapshotMachine. When calling this, the order of calling this
15174 * function on the one hand and changing that variable OR the snapshots tree on the other hand
15175 * is therefore critical. I know, it's all rather messy.
15176 *
15177 * @param strStateFile
15178 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
15179 * the test for whether the saved state file is in use.
15180 */
15181void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
15182 Snapshot *pSnapshotToIgnore)
15183{
15184 // it is safe to delete this saved state file if it is not currently in use by the machine ...
15185 if ( (strStateFile.isNotEmpty())
15186 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
15187 )
15188 // ... and it must also not be shared with other snapshots
15189 if ( !mData->mFirstSnapshot
15190 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
15191 // this checks the SnapshotMachine's state file paths
15192 )
15193 i_deleteFile(strStateFile, true /* fIgnoreFailures */);
15194}
15195
15196/**
15197 * Locks the attached media.
15198 *
15199 * All attached hard disks are locked for writing and DVD/floppy are locked for
15200 * reading. Parents of attached hard disks (if any) are locked for reading.
15201 *
15202 * This method also performs accessibility check of all media it locks: if some
15203 * media is inaccessible, the method will return a failure and a bunch of
15204 * extended error info objects per each inaccessible medium.
15205 *
15206 * Note that this method is atomic: if it returns a success, all media are
15207 * locked as described above; on failure no media is locked at all (all
15208 * succeeded individual locks will be undone).
15209 *
15210 * The caller is responsible for doing the necessary state sanity checks.
15211 *
15212 * The locks made by this method must be undone by calling #unlockMedia() when
15213 * no more needed.
15214 */
15215HRESULT SessionMachine::i_lockMedia()
15216{
15217 AutoCaller autoCaller(this);
15218 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15219
15220 AutoMultiWriteLock2 alock(this->lockHandle(),
15221 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
15222
15223 /* bail out if trying to lock things with already set up locking */
15224 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
15225
15226 MultiResult mrc(S_OK);
15227
15228 /* Collect locking information for all medium objects attached to the VM. */
15229 for (MediumAttachmentList::const_iterator
15230 it = mMediumAttachments->begin();
15231 it != mMediumAttachments->end();
15232 ++it)
15233 {
15234 MediumAttachment *pAtt = *it;
15235 DeviceType_T devType = pAtt->i_getType();
15236 Medium *pMedium = pAtt->i_getMedium();
15237
15238 MediumLockList *pMediumLockList(new MediumLockList());
15239 // There can be attachments without a medium (floppy/dvd), and thus
15240 // it's impossible to create a medium lock list. It still makes sense
15241 // to have the empty medium lock list in the map in case a medium is
15242 // attached later.
15243 if (pMedium != NULL)
15244 {
15245 MediumType_T mediumType = pMedium->i_getType();
15246 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
15247 || mediumType == MediumType_Shareable;
15248 bool fIsVitalImage = (devType == DeviceType_HardDisk);
15249
15250 alock.release();
15251 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
15252 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
15253 false /* fMediumLockWriteAll */,
15254 NULL,
15255 *pMediumLockList);
15256 alock.acquire();
15257 if (FAILED(mrc))
15258 {
15259 delete pMediumLockList;
15260 mData->mSession.mLockedMedia.Clear();
15261 break;
15262 }
15263 }
15264
15265 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
15266 if (FAILED(rc))
15267 {
15268 mData->mSession.mLockedMedia.Clear();
15269 mrc = setError(rc,
15270 tr("Collecting locking information for all attached media failed"));
15271 break;
15272 }
15273 }
15274
15275 if (SUCCEEDED(mrc))
15276 {
15277 /* Now lock all media. If this fails, nothing is locked. */
15278 alock.release();
15279 HRESULT rc = mData->mSession.mLockedMedia.Lock();
15280 alock.acquire();
15281 if (FAILED(rc))
15282 {
15283 mrc = setError(rc,
15284 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
15285 }
15286 }
15287
15288 return mrc;
15289}
15290
15291/**
15292 * Undoes the locks made by by #lockMedia().
15293 */
15294HRESULT SessionMachine::i_unlockMedia()
15295{
15296 AutoCaller autoCaller(this);
15297 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15298
15299 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15300
15301 /* we may be holding important error info on the current thread;
15302 * preserve it */
15303 ErrorInfoKeeper eik;
15304
15305 HRESULT rc = mData->mSession.mLockedMedia.Clear();
15306 AssertComRC(rc);
15307 return rc;
15308}
15309
15310/**
15311 * Helper to change the machine state (reimplementation).
15312 *
15313 * @note Locks this object for writing.
15314 * @note This method must not call i_saveSettings or SaveSettings, otherwise
15315 * it can cause crashes in random places due to unexpectedly committing
15316 * the current settings. The caller is responsible for that. The call
15317 * to saveStateSettings is fine, because this method does not commit.
15318 */
15319HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
15320{
15321 LogFlowThisFuncEnter();
15322
15323 AutoCaller autoCaller(this);
15324 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15325
15326 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15327
15328 MachineState_T oldMachineState = mData->mMachineState;
15329
15330 AssertMsgReturn(oldMachineState != aMachineState,
15331 ("oldMachineState=%s, aMachineState=%s\n",
15332 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
15333 E_FAIL);
15334
15335 HRESULT rc = S_OK;
15336
15337 int stsFlags = 0;
15338 bool deleteSavedState = false;
15339
15340 /* detect some state transitions */
15341
15342 if ( ( ( oldMachineState == MachineState_Saved
15343 || oldMachineState == MachineState_AbortedSaved
15344 )
15345 && aMachineState == MachineState_Restoring
15346 )
15347 || ( ( oldMachineState == MachineState_PoweredOff
15348 || oldMachineState == MachineState_Teleported
15349 || oldMachineState == MachineState_Aborted
15350 )
15351 && ( aMachineState == MachineState_TeleportingIn
15352 || aMachineState == MachineState_Starting
15353 )
15354 )
15355 )
15356 {
15357 /* The EMT thread is about to start */
15358
15359 /* Nothing to do here for now... */
15360
15361 /// @todo NEWMEDIA don't let mDVDDrive and other children
15362 /// change anything when in the Starting/Restoring state
15363 }
15364 else if ( ( oldMachineState == MachineState_Running
15365 || oldMachineState == MachineState_Paused
15366 || oldMachineState == MachineState_Teleporting
15367 || oldMachineState == MachineState_OnlineSnapshotting
15368 || oldMachineState == MachineState_LiveSnapshotting
15369 || oldMachineState == MachineState_Stuck
15370 || oldMachineState == MachineState_Starting
15371 || oldMachineState == MachineState_Stopping
15372 || oldMachineState == MachineState_Saving
15373 || oldMachineState == MachineState_Restoring
15374 || oldMachineState == MachineState_TeleportingPausedVM
15375 || oldMachineState == MachineState_TeleportingIn
15376 )
15377 && ( aMachineState == MachineState_PoweredOff
15378 || aMachineState == MachineState_Saved
15379 || aMachineState == MachineState_Teleported
15380 || aMachineState == MachineState_Aborted
15381 || aMachineState == MachineState_AbortedSaved
15382 )
15383 )
15384 {
15385 /* The EMT thread has just stopped, unlock attached media. Note that as
15386 * opposed to locking that is done from Console, we do unlocking here
15387 * because the VM process may have aborted before having a chance to
15388 * properly unlock all media it locked. */
15389
15390 unlockMedia();
15391 }
15392
15393 if (oldMachineState == MachineState_Restoring)
15394 {
15395 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
15396 {
15397 /*
15398 * delete the saved state file once the machine has finished
15399 * restoring from it (note that Console sets the state from
15400 * Restoring to AbortedSaved if the VM couldn't restore successfully,
15401 * to give the user an ability to fix an error and retry --
15402 * we keep the saved state file in this case)
15403 */
15404 deleteSavedState = true;
15405 }
15406 }
15407 else if ( oldMachineState == MachineState_Saved
15408 && ( aMachineState == MachineState_PoweredOff
15409 || aMachineState == MachineState_Teleported
15410 )
15411 )
15412 {
15413 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
15414 deleteSavedState = true;
15415 mData->mCurrentStateModified = TRUE;
15416 stsFlags |= SaveSTS_CurStateModified;
15417 }
15418 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
15419 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
15420
15421 if ( aMachineState == MachineState_Starting
15422 || aMachineState == MachineState_Restoring
15423 || aMachineState == MachineState_TeleportingIn
15424 )
15425 {
15426 /* set the current state modified flag to indicate that the current
15427 * state is no more identical to the state in the
15428 * current snapshot */
15429 if (!mData->mCurrentSnapshot.isNull())
15430 {
15431 mData->mCurrentStateModified = TRUE;
15432 stsFlags |= SaveSTS_CurStateModified;
15433 }
15434 }
15435
15436 if (deleteSavedState)
15437 {
15438 if (mRemoveSavedState)
15439 {
15440 Assert(!mSSData->strStateFilePath.isEmpty());
15441
15442 // it is safe to delete the saved state file if ...
15443 if ( !mData->mFirstSnapshot // ... we have no snapshots or
15444 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
15445 // ... none of the snapshots share the saved state file
15446 )
15447 i_deleteFile(mSSData->strStateFilePath, true /* fIgnoreFailures */);
15448 }
15449
15450 mSSData->strStateFilePath.setNull();
15451 stsFlags |= SaveSTS_StateFilePath;
15452 }
15453
15454 /* redirect to the underlying peer machine */
15455 mPeer->i_setMachineState(aMachineState);
15456
15457 if ( oldMachineState != MachineState_RestoringSnapshot
15458 && ( aMachineState == MachineState_PoweredOff
15459 || aMachineState == MachineState_Teleported
15460 || aMachineState == MachineState_Aborted
15461 || aMachineState == MachineState_AbortedSaved
15462 || aMachineState == MachineState_Saved))
15463 {
15464 /* the machine has stopped execution
15465 * (or the saved state file was adopted) */
15466 stsFlags |= SaveSTS_StateTimeStamp;
15467 }
15468
15469 if ( ( oldMachineState == MachineState_PoweredOff
15470 || oldMachineState == MachineState_Aborted
15471 || oldMachineState == MachineState_Teleported
15472 )
15473 && aMachineState == MachineState_Saved)
15474 {
15475 /* the saved state file was adopted */
15476 Assert(!mSSData->strStateFilePath.isEmpty());
15477 stsFlags |= SaveSTS_StateFilePath;
15478 }
15479
15480#ifdef VBOX_WITH_GUEST_PROPS
15481 if ( aMachineState == MachineState_PoweredOff
15482 || aMachineState == MachineState_Aborted
15483 || aMachineState == MachineState_Teleported)
15484 {
15485 /* Make sure any transient guest properties get removed from the
15486 * property store on shutdown. */
15487 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
15488
15489 /* remove it from the settings representation */
15490 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
15491 for (settings::GuestPropertiesList::iterator
15492 it = llGuestProperties.begin();
15493 it != llGuestProperties.end();
15494 /*nothing*/)
15495 {
15496 const settings::GuestProperty &prop = *it;
15497 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
15498 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
15499 {
15500 it = llGuestProperties.erase(it);
15501 fNeedsSaving = true;
15502 }
15503 else
15504 {
15505 ++it;
15506 }
15507 }
15508
15509 /* Additionally remove it from the HWData representation. Required to
15510 * keep everything in sync, as this is what the API keeps using. */
15511 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
15512 for (HWData::GuestPropertyMap::iterator
15513 it = llHWGuestProperties.begin();
15514 it != llHWGuestProperties.end();
15515 /*nothing*/)
15516 {
15517 uint32_t fFlags = it->second.mFlags;
15518 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
15519 {
15520 /* iterator where we need to continue after the erase call
15521 * (C++03 is a fact still, and it doesn't return the iterator
15522 * which would allow continuing) */
15523 HWData::GuestPropertyMap::iterator it2 = it;
15524 ++it2;
15525 llHWGuestProperties.erase(it);
15526 it = it2;
15527 fNeedsSaving = true;
15528 }
15529 else
15530 {
15531 ++it;
15532 }
15533 }
15534
15535 if (fNeedsSaving)
15536 {
15537 mData->mCurrentStateModified = TRUE;
15538 stsFlags |= SaveSTS_CurStateModified;
15539 }
15540 }
15541#endif /* VBOX_WITH_GUEST_PROPS */
15542
15543 rc = i_saveStateSettings(stsFlags);
15544
15545 if ( ( oldMachineState != MachineState_PoweredOff
15546 && oldMachineState != MachineState_Aborted
15547 && oldMachineState != MachineState_Teleported
15548 )
15549 && ( aMachineState == MachineState_PoweredOff
15550 || aMachineState == MachineState_Aborted
15551 || aMachineState == MachineState_Teleported
15552 )
15553 )
15554 {
15555 /* we've been shut down for any reason */
15556 /* no special action so far */
15557 }
15558
15559 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, ::stringifyMachineState(mData->mMachineState) ));
15560 LogFlowThisFuncLeave();
15561 return rc;
15562}
15563
15564/**
15565 * Sends the current machine state value to the VM process.
15566 *
15567 * @note Locks this object for reading, then calls a client process.
15568 */
15569HRESULT SessionMachine::i_updateMachineStateOnClient()
15570{
15571 AutoCaller autoCaller(this);
15572 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15573
15574 ComPtr<IInternalSessionControl> directControl;
15575 {
15576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15577 AssertReturn(!!mData, E_FAIL);
15578 if (mData->mSession.mLockType == LockType_VM)
15579 directControl = mData->mSession.mDirectControl;
15580
15581 /* directControl may be already set to NULL here in #OnSessionEnd()
15582 * called too early by the direct session process while there is still
15583 * some operation (like deleting the snapshot) in progress. The client
15584 * process in this case is waiting inside Session::close() for the
15585 * "end session" process object to complete, while #uninit() called by
15586 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15587 * operation to complete. For now, we accept this inconsistent behavior
15588 * and simply do nothing here. */
15589
15590 if (mData->mSession.mState == SessionState_Unlocking)
15591 return S_OK;
15592 }
15593
15594 /* ignore notifications sent after #OnSessionEnd() is called */
15595 if (!directControl)
15596 return S_OK;
15597
15598 return directControl->UpdateMachineState(mData->mMachineState);
15599}
15600
15601
15602/*static*/
15603HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15604{
15605 va_list args;
15606 va_start(args, pcszMsg);
15607 HRESULT rc = setErrorInternalV(aResultCode,
15608 getStaticClassIID(),
15609 getStaticComponentName(),
15610 pcszMsg, args,
15611 false /* aWarning */,
15612 true /* aLogIt */);
15613 va_end(args);
15614 return rc;
15615}
15616
15617
15618HRESULT Machine::updateState(MachineState_T aState)
15619{
15620 NOREF(aState);
15621 ReturnComNotImplemented();
15622}
15623
15624HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15625{
15626 NOREF(aProgress);
15627 ReturnComNotImplemented();
15628}
15629
15630HRESULT Machine::endPowerUp(LONG aResult)
15631{
15632 NOREF(aResult);
15633 ReturnComNotImplemented();
15634}
15635
15636HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15637{
15638 NOREF(aProgress);
15639 ReturnComNotImplemented();
15640}
15641
15642HRESULT Machine::endPoweringDown(LONG aResult,
15643 const com::Utf8Str &aErrMsg)
15644{
15645 NOREF(aResult);
15646 NOREF(aErrMsg);
15647 ReturnComNotImplemented();
15648}
15649
15650HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15651 BOOL *aMatched,
15652 ULONG *aMaskedInterfaces)
15653{
15654 NOREF(aDevice);
15655 NOREF(aMatched);
15656 NOREF(aMaskedInterfaces);
15657 ReturnComNotImplemented();
15658
15659}
15660
15661HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15662{
15663 NOREF(aId); NOREF(aCaptureFilename);
15664 ReturnComNotImplemented();
15665}
15666
15667HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15668 BOOL aDone)
15669{
15670 NOREF(aId);
15671 NOREF(aDone);
15672 ReturnComNotImplemented();
15673}
15674
15675HRESULT Machine::autoCaptureUSBDevices()
15676{
15677 ReturnComNotImplemented();
15678}
15679
15680HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15681{
15682 NOREF(aDone);
15683 ReturnComNotImplemented();
15684}
15685
15686HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15687 ComPtr<IProgress> &aProgress)
15688{
15689 NOREF(aSession);
15690 NOREF(aProgress);
15691 ReturnComNotImplemented();
15692}
15693
15694HRESULT Machine::finishOnlineMergeMedium()
15695{
15696 ReturnComNotImplemented();
15697}
15698
15699HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15700 std::vector<com::Utf8Str> &aValues,
15701 std::vector<LONG64> &aTimestamps,
15702 std::vector<com::Utf8Str> &aFlags)
15703{
15704 NOREF(aNames);
15705 NOREF(aValues);
15706 NOREF(aTimestamps);
15707 NOREF(aFlags);
15708 ReturnComNotImplemented();
15709}
15710
15711HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15712 const com::Utf8Str &aValue,
15713 LONG64 aTimestamp,
15714 const com::Utf8Str &aFlags,
15715 BOOL fWasDeleted)
15716{
15717 NOREF(aName);
15718 NOREF(aValue);
15719 NOREF(aTimestamp);
15720 NOREF(aFlags);
15721 NOREF(fWasDeleted);
15722 ReturnComNotImplemented();
15723}
15724
15725HRESULT Machine::lockMedia()
15726{
15727 ReturnComNotImplemented();
15728}
15729
15730HRESULT Machine::unlockMedia()
15731{
15732 ReturnComNotImplemented();
15733}
15734
15735HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15736 ComPtr<IMediumAttachment> &aNewAttachment)
15737{
15738 NOREF(aAttachment);
15739 NOREF(aNewAttachment);
15740 ReturnComNotImplemented();
15741}
15742
15743HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15744 ULONG aCpuUser,
15745 ULONG aCpuKernel,
15746 ULONG aCpuIdle,
15747 ULONG aMemTotal,
15748 ULONG aMemFree,
15749 ULONG aMemBalloon,
15750 ULONG aMemShared,
15751 ULONG aMemCache,
15752 ULONG aPagedTotal,
15753 ULONG aMemAllocTotal,
15754 ULONG aMemFreeTotal,
15755 ULONG aMemBalloonTotal,
15756 ULONG aMemSharedTotal,
15757 ULONG aVmNetRx,
15758 ULONG aVmNetTx)
15759{
15760 NOREF(aValidStats);
15761 NOREF(aCpuUser);
15762 NOREF(aCpuKernel);
15763 NOREF(aCpuIdle);
15764 NOREF(aMemTotal);
15765 NOREF(aMemFree);
15766 NOREF(aMemBalloon);
15767 NOREF(aMemShared);
15768 NOREF(aMemCache);
15769 NOREF(aPagedTotal);
15770 NOREF(aMemAllocTotal);
15771 NOREF(aMemFreeTotal);
15772 NOREF(aMemBalloonTotal);
15773 NOREF(aMemSharedTotal);
15774 NOREF(aVmNetRx);
15775 NOREF(aVmNetTx);
15776 ReturnComNotImplemented();
15777}
15778
15779HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15780 com::Utf8Str &aResult)
15781{
15782 NOREF(aAuthParams);
15783 NOREF(aResult);
15784 ReturnComNotImplemented();
15785}
15786
15787com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15788{
15789 com::Utf8Str strControllerName = "Unknown";
15790 switch (aBusType)
15791 {
15792 case StorageBus_IDE:
15793 {
15794 strControllerName = "IDE";
15795 break;
15796 }
15797 case StorageBus_SATA:
15798 {
15799 strControllerName = "SATA";
15800 break;
15801 }
15802 case StorageBus_SCSI:
15803 {
15804 strControllerName = "SCSI";
15805 break;
15806 }
15807 case StorageBus_Floppy:
15808 {
15809 strControllerName = "Floppy";
15810 break;
15811 }
15812 case StorageBus_SAS:
15813 {
15814 strControllerName = "SAS";
15815 break;
15816 }
15817 case StorageBus_USB:
15818 {
15819 strControllerName = "USB";
15820 break;
15821 }
15822 default:
15823 break;
15824 }
15825 return strControllerName;
15826}
15827
15828HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15829{
15830 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15831
15832 AutoCaller autoCaller(this);
15833 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15834
15835 HRESULT rc = S_OK;
15836
15837 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15838 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15839 rc = getUSBDeviceFilters(usbDeviceFilters);
15840 if (FAILED(rc)) return rc;
15841
15842 NOREF(aFlags);
15843 com::Utf8Str osTypeId;
15844 ComObjPtr<GuestOSType> osType = NULL;
15845
15846 /* Get the guest os type as a string from the VB. */
15847 rc = getOSTypeId(osTypeId);
15848 if (FAILED(rc)) return rc;
15849
15850 /* Get the os type obj that coresponds, can be used to get
15851 * the defaults for this guest OS. */
15852 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15853 if (FAILED(rc)) return rc;
15854
15855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15856
15857 /* Let the OS type select 64-bit ness. */
15858 mHWData->mLongMode = osType->i_is64Bit()
15859 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15860
15861 /* Let the OS type enable the X2APIC */
15862 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15863
15864 /* This one covers IOAPICEnabled. */
15865 mBIOSSettings->i_applyDefaults(osType);
15866
15867 /* Initialize default record settings. */
15868 mRecordingSettings->i_applyDefaults();
15869
15870 /* Initialize default BIOS settings here */
15871 /* Hardware virtualization must be ON by default */
15872 mHWData->mAPIC = true;
15873 mHWData->mHWVirtExEnabled = true;
15874
15875 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15876 if (FAILED(rc)) return rc;
15877
15878 rc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15879 if (FAILED(rc)) return rc;
15880
15881 /* Graphics stuff. */
15882 GraphicsControllerType_T graphicsController;
15883 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15884 if (FAILED(rc)) return rc;
15885
15886 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15887 if (FAILED(rc)) return rc;
15888
15889 ULONG vramSize;
15890 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15891 if (FAILED(rc)) return rc;
15892
15893 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15894 if (FAILED(rc)) return rc;
15895
15896 BOOL fAccelerate2DVideoEnabled;
15897 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15898 if (FAILED(rc)) return rc;
15899
15900 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15901 if (FAILED(rc)) return rc;
15902
15903 BOOL fAccelerate3DEnabled;
15904 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15905 if (FAILED(rc)) return rc;
15906
15907 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15908 if (FAILED(rc)) return rc;
15909
15910 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15911 if (FAILED(rc)) return rc;
15912
15913 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15914 if (FAILED(rc)) return rc;
15915
15916 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15917 if (FAILED(rc)) return rc;
15918
15919 BOOL mRTCUseUTC;
15920 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15921 if (FAILED(rc)) return rc;
15922
15923 setRTCUseUTC(mRTCUseUTC);
15924 if (FAILED(rc)) return rc;
15925
15926 /* the setter does more than just the assignment, so use it */
15927 ChipsetType_T enmChipsetType;
15928 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15929 if (FAILED(rc)) return rc;
15930
15931 rc = COMSETTER(ChipsetType)(enmChipsetType);
15932 if (FAILED(rc)) return rc;
15933
15934 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15935 if (FAILED(rc)) return rc;
15936
15937 /* Apply IOMMU defaults. */
15938 IommuType_T enmIommuType;
15939 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15940 if (FAILED(rc)) return rc;
15941
15942 rc = COMSETTER(IommuType)(enmIommuType);
15943 if (FAILED(rc)) return rc;
15944
15945 /* Apply network adapters defaults */
15946 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15947 mNetworkAdapters[slot]->i_applyDefaults(osType);
15948
15949 /* Apply serial port defaults */
15950 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15951 mSerialPorts[slot]->i_applyDefaults(osType);
15952
15953 /* Apply parallel port defaults - not OS dependent*/
15954 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15955 mParallelPorts[slot]->i_applyDefaults();
15956
15957 /* This one covers the TPM type. */
15958 mTrustedPlatformModule->i_applyDefaults(osType);
15959
15960 /* This one covers secure boot. */
15961 rc = mNvramStore->i_applyDefaults(osType);
15962 if (FAILED(rc)) return rc;
15963
15964 /* Audio stuff. */
15965 rc = mAudioSettings->i_applyDefaults(osType);
15966 if (FAILED(rc)) return rc;
15967
15968 /* Storage Controllers */
15969 StorageControllerType_T hdStorageControllerType;
15970 StorageBus_T hdStorageBusType;
15971 StorageControllerType_T dvdStorageControllerType;
15972 StorageBus_T dvdStorageBusType;
15973 BOOL recommendedFloppy;
15974 ComPtr<IStorageController> floppyController;
15975 ComPtr<IStorageController> hdController;
15976 ComPtr<IStorageController> dvdController;
15977 Utf8Str strFloppyName, strDVDName, strHDName;
15978
15979 /* GUI auto generates controller names using bus type. Do the same*/
15980 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15981
15982 /* Floppy recommended? add one. */
15983 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15984 if (FAILED(rc)) return rc;
15985 if (recommendedFloppy)
15986 {
15987 rc = addStorageController(strFloppyName,
15988 StorageBus_Floppy,
15989 floppyController);
15990 if (FAILED(rc)) return rc;
15991 }
15992
15993 /* Setup one DVD storage controller. */
15994 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15995 if (FAILED(rc)) return rc;
15996
15997 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15998 if (FAILED(rc)) return rc;
15999
16000 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
16001
16002 rc = addStorageController(strDVDName,
16003 dvdStorageBusType,
16004 dvdController);
16005 if (FAILED(rc)) return rc;
16006
16007 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
16008 if (FAILED(rc)) return rc;
16009
16010 /* Setup one HDD storage controller. */
16011 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
16012 if (FAILED(rc)) return rc;
16013
16014 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
16015 if (FAILED(rc)) return rc;
16016
16017 strHDName = i_controllerNameFromBusType(hdStorageBusType);
16018
16019 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
16020 {
16021 rc = addStorageController(strHDName,
16022 hdStorageBusType,
16023 hdController);
16024 if (FAILED(rc)) return rc;
16025
16026 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
16027 if (FAILED(rc)) return rc;
16028 }
16029 else
16030 {
16031 /* The HD controller is the same as DVD: */
16032 hdController = dvdController;
16033 }
16034
16035 /* Limit the AHCI port count if it's used because windows has trouble with
16036 * too many ports and other guest (OS X in particular) may take extra long
16037 * boot: */
16038
16039 // pParent = static_cast<Medium*>(aP)
16040 IStorageController *temp = hdController;
16041 ComObjPtr<StorageController> storageController;
16042 storageController = static_cast<StorageController *>(temp);
16043
16044 // tempHDController = aHDController;
16045 if (hdStorageControllerType == StorageControllerType_IntelAhci)
16046 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
16047 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
16048 storageController->COMSETTER(PortCount)(1);
16049
16050 /* USB stuff */
16051
16052 bool ohciEnabled = false;
16053
16054 ComPtr<IUSBController> usbController;
16055 BOOL recommendedUSB3;
16056 BOOL recommendedUSB;
16057 BOOL usbProxyAvailable;
16058
16059 getUSBProxyAvailable(&usbProxyAvailable);
16060 if (FAILED(rc)) return rc;
16061
16062 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
16063 if (FAILED(rc)) return rc;
16064 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
16065 if (FAILED(rc)) return rc;
16066
16067 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
16068 {
16069 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
16070 if (FAILED(rc)) return rc;
16071
16072 /* xHci includes OHCI */
16073 ohciEnabled = true;
16074 }
16075 if ( !ohciEnabled
16076 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
16077 {
16078 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16079 if (FAILED(rc)) return rc;
16080 ohciEnabled = true;
16081
16082 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
16083 if (FAILED(rc)) return rc;
16084 }
16085
16086 /* Set recommended human interface device types: */
16087 BOOL recommendedUSBHID;
16088 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
16089 if (FAILED(rc)) return rc;
16090
16091 if (recommendedUSBHID)
16092 {
16093 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
16094 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
16095 if (!ohciEnabled && !usbDeviceFilters.isNull())
16096 {
16097 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16098 if (FAILED(rc)) return rc;
16099 }
16100 }
16101
16102 BOOL recommendedUSBTablet;
16103 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
16104 if (FAILED(rc)) return rc;
16105
16106 if (recommendedUSBTablet)
16107 {
16108 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
16109 if (!ohciEnabled && !usbDeviceFilters.isNull())
16110 {
16111 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16112 if (FAILED(rc)) return rc;
16113 }
16114 }
16115
16116 /* Enable the VMMDev testing feature for bootsector VMs: */
16117 if (osTypeId == "VBoxBS_64")
16118 {
16119 rc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
16120 if (FAILED(rc))
16121 return rc;
16122 }
16123
16124 return S_OK;
16125}
16126
16127#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
16128/**
16129 * Task record for change encryption settins.
16130 */
16131class Machine::ChangeEncryptionTask
16132 : public Machine::Task
16133{
16134public:
16135 ChangeEncryptionTask(Machine *m,
16136 Progress *p,
16137 const Utf8Str &t,
16138 const com::Utf8Str &aCurrentPassword,
16139 const com::Utf8Str &aCipher,
16140 const com::Utf8Str &aNewPassword,
16141 const com::Utf8Str &aNewPasswordId,
16142 const BOOL aForce,
16143 const MediaList &llMedia)
16144 : Task(m, p, t),
16145 mstrNewPassword(aNewPassword),
16146 mstrCurrentPassword(aCurrentPassword),
16147 mstrCipher(aCipher),
16148 mstrNewPasswordId(aNewPasswordId),
16149 mForce(aForce),
16150 mllMedia(llMedia)
16151 {}
16152
16153 ~ChangeEncryptionTask()
16154 {
16155 if (mstrNewPassword.length())
16156 RTMemWipeThoroughly(mstrNewPassword.mutableRaw(), mstrNewPassword.length(), 10 /* cPasses */);
16157 if (mstrCurrentPassword.length())
16158 RTMemWipeThoroughly(mstrCurrentPassword.mutableRaw(), mstrCurrentPassword.length(), 10 /* cPasses */);
16159 if (m_pCryptoIf)
16160 {
16161 m_pMachine->i_getVirtualBox()->i_releaseCryptoIf(m_pCryptoIf);
16162 m_pCryptoIf = NULL;
16163 }
16164 }
16165
16166 Utf8Str mstrNewPassword;
16167 Utf8Str mstrCurrentPassword;
16168 Utf8Str mstrCipher;
16169 Utf8Str mstrNewPasswordId;
16170 BOOL mForce;
16171 MediaList mllMedia;
16172 PCVBOXCRYPTOIF m_pCryptoIf;
16173private:
16174 void handler()
16175 {
16176 try
16177 {
16178 m_pMachine->i_changeEncryptionHandler(*this);
16179 }
16180 catch (...)
16181 {
16182 LogRel(("Some exception in the function Machine::i_changeEncryptionHandler()\n"));
16183 }
16184 }
16185
16186 friend void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task);
16187};
16188
16189/**
16190 * Scans specified directory and fills list by files found
16191 *
16192 * @returns VBox status code.
16193 * @param lstFiles
16194 * @param strDir
16195 * @param filePattern
16196 */
16197int Machine::i_findFiles(std::list<com::Utf8Str> &lstFiles, const com::Utf8Str &strDir,
16198 const com::Utf8Str &strPattern)
16199{
16200 /* To get all entries including subdirectories. */
16201 char *pszFilePattern = RTPathJoinA(strDir.c_str(), "*");
16202 if (!pszFilePattern)
16203 return VERR_NO_STR_MEMORY;
16204
16205 PRTDIRENTRYEX pDirEntry = NULL;
16206 RTDIR hDir;
16207 size_t cbDirEntry = sizeof(RTDIRENTRYEX);
16208 int rc = RTDirOpenFiltered(&hDir, pszFilePattern, RTDIRFILTER_WINNT, 0 /*fFlags*/);
16209 if (RT_SUCCESS(rc))
16210 {
16211 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
16212 if (pDirEntry)
16213 {
16214 while ( (rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK))
16215 != VERR_NO_MORE_FILES)
16216 {
16217 char *pszFilePath = NULL;
16218
16219 if (rc == VERR_BUFFER_OVERFLOW)
16220 {
16221 /* allocate new buffer. */
16222 RTMemFree(pDirEntry);
16223 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbDirEntry);
16224 if (!pDirEntry)
16225 {
16226 rc = VERR_NO_MEMORY;
16227 break;
16228 }
16229 /* Retry. */
16230 rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
16231 if (RT_FAILURE(rc))
16232 break;
16233 }
16234 else if (RT_FAILURE(rc))
16235 break;
16236
16237 /* Exclude . and .. */
16238 if ( (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '\0')
16239 || (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '.' && pDirEntry->szName[2] == '\0'))
16240 continue;
16241 if (RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode))
16242 {
16243 char *pszSubDirPath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
16244 if (!pszSubDirPath)
16245 {
16246 rc = VERR_NO_STR_MEMORY;
16247 break;
16248 }
16249 rc = i_findFiles(lstFiles, pszSubDirPath, strPattern);
16250 RTMemFree(pszSubDirPath);
16251 if (RT_FAILURE(rc))
16252 break;
16253 continue;
16254 }
16255
16256 /* We got the new entry. */
16257 if (!RTFS_IS_FILE(pDirEntry->Info.Attr.fMode))
16258 continue;
16259
16260 if (!RTStrSimplePatternMatch(strPattern.c_str(), pDirEntry->szName))
16261 continue;
16262
16263 /* Prepend the path to the libraries. */
16264 pszFilePath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
16265 if (!pszFilePath)
16266 {
16267 rc = VERR_NO_STR_MEMORY;
16268 break;
16269 }
16270
16271 lstFiles.push_back(pszFilePath);
16272 RTStrFree(pszFilePath);
16273 }
16274
16275 RTMemFree(pDirEntry);
16276 }
16277 else
16278 rc = VERR_NO_MEMORY;
16279
16280 RTDirClose(hDir);
16281 }
16282 else
16283 {
16284 /* On Windows the above immediately signals that there are no
16285 * files matching, while on other platforms enumerating the
16286 * files below fails. Either way: stop searching. */
16287 }
16288
16289 if ( rc == VERR_NO_MORE_FILES
16290 || rc == VERR_FILE_NOT_FOUND
16291 || rc == VERR_PATH_NOT_FOUND)
16292 rc = VINF_SUCCESS;
16293 RTStrFree(pszFilePattern);
16294 return rc;
16295}
16296
16297/**
16298 * Helper to set up an I/O stream to read or write a possibly encrypted file.
16299 *
16300 * @returns VBox status code.
16301 * @param pszFilename The file to open.
16302 * @param pCryptoIf Pointer to the cryptographic interface if the file should be encrypted or contains encrypted data.
16303 * @param pszKeyStore The keystore if the file should be encrypted or contains encrypted data.
16304 * @param pszPassword The password if the file should be encrypted or contains encrypted data.
16305 * @param fOpen The open flags for the file.
16306 * @param phVfsIos Where to store the handle to the I/O stream on success.
16307 */
16308int Machine::i_createIoStreamForFile(const char *pszFilename, PCVBOXCRYPTOIF pCryptoIf,
16309 const char *pszKeyStore, const char *pszPassword,
16310 uint64_t fOpen, PRTVFSIOSTREAM phVfsIos)
16311{
16312 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
16313 int vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
16314 if (RT_SUCCESS(vrc))
16315 {
16316 if (pCryptoIf)
16317 {
16318 RTVFSFILE hVfsFileCrypto = NIL_RTVFSFILE;
16319 vrc = pCryptoIf->pfnCryptoFileFromVfsFile(hVfsFile, pszKeyStore, pszPassword, &hVfsFileCrypto);
16320 if (RT_SUCCESS(vrc))
16321 {
16322 RTVfsFileRelease(hVfsFile);
16323 hVfsFile = hVfsFileCrypto;
16324 }
16325 }
16326
16327 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
16328 RTVfsFileRelease(hVfsFile);
16329 }
16330
16331 return vrc;
16332}
16333
16334/**
16335 * Helper function processing all actions for one component (saved state files,
16336 * NVRAM files, etc). Used by Machine::i_changeEncryptionHandler only.
16337 *
16338 * @param task
16339 * @param strDirectory
16340 * @param strFilePattern
16341 * @param strMagic
16342 * @param strKeyStore
16343 * @param strKeyId
16344 * @return
16345 */
16346HRESULT Machine::i_changeEncryptionForComponent(ChangeEncryptionTask &task, const com::Utf8Str strDirectory,
16347 const com::Utf8Str strFilePattern, com::Utf8Str &strKeyStore,
16348 com::Utf8Str &strKeyId, int iCipherMode)
16349{
16350 bool fDecrypt = task.mstrCurrentPassword.isNotEmpty()
16351 && task.mstrCipher.isEmpty()
16352 && task.mstrNewPassword.isEmpty()
16353 && task.mstrNewPasswordId.isEmpty();
16354 bool fEncrypt = task.mstrCurrentPassword.isEmpty()
16355 && task.mstrCipher.isNotEmpty()
16356 && task.mstrNewPassword.isNotEmpty()
16357 && task.mstrNewPasswordId.isNotEmpty();
16358
16359 /* check if the cipher is changed which causes the reencryption*/
16360
16361 const char *pszTaskCipher = NULL;
16362 if (task.mstrCipher.isNotEmpty())
16363 pszTaskCipher = getCipherString(task.mstrCipher.c_str(), iCipherMode);
16364
16365 if (!task.mForce && !fDecrypt && !fEncrypt)
16366 {
16367 char *pszCipher = NULL;
16368 int vrc = task.m_pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(strKeyStore.c_str(),
16369 NULL /*pszPassword*/,
16370 NULL /*ppbKey*/,
16371 NULL /*pcbKey*/,
16372 &pszCipher);
16373 if (RT_SUCCESS(vrc))
16374 {
16375 task.mForce = strcmp(pszTaskCipher, pszCipher) != 0;
16376 RTMemFree(pszCipher);
16377 }
16378 else
16379 return setErrorBoth(E_FAIL, vrc, tr("Obtain cipher for '%s' files failed (%Rrc)"),
16380 strFilePattern.c_str(), vrc);
16381 }
16382
16383 /* Only the password needs to be changed */
16384 if (!task.mForce && !fDecrypt && !fEncrypt)
16385 {
16386 Assert(task.m_pCryptoIf);
16387
16388 VBOXCRYPTOCTX hCryptoCtx;
16389 int vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(strKeyStore.c_str(), task.mstrCurrentPassword.c_str(), &hCryptoCtx);
16390 if (RT_FAILURE(vrc))
16391 return setErrorBoth(E_FAIL, vrc, tr("Loading old key store for '%s' files failed, (%Rrc)"),
16392 strFilePattern.c_str(), vrc);
16393 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
16394 if (RT_FAILURE(vrc))
16395 return setErrorBoth(E_FAIL, vrc, tr("Changing the password for '%s' files failed, (%Rrc)"),
16396 strFilePattern.c_str(), vrc);
16397
16398 char *pszKeyStore = NULL;
16399 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16400 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16401 if (RT_FAILURE(vrc))
16402 return setErrorBoth(E_FAIL, vrc, tr("Saving the key store for '%s' files failed, (%Rrc)"),
16403 strFilePattern.c_str(), vrc);
16404 strKeyStore = pszKeyStore;
16405 RTMemFree(pszKeyStore);
16406 strKeyId = task.mstrNewPasswordId;
16407 return S_OK;
16408 }
16409
16410 /* Reencryption required */
16411 HRESULT rc = S_OK;
16412 int vrc = VINF_SUCCESS;
16413
16414 std::list<com::Utf8Str> lstFiles;
16415 if (SUCCEEDED(rc))
16416 {
16417 vrc = i_findFiles(lstFiles, strDirectory, strFilePattern);
16418 if (RT_FAILURE(vrc))
16419 rc = setErrorBoth(E_FAIL, vrc, tr("Getting file list for '%s' files failed, (%Rrc)"),
16420 strFilePattern.c_str(), vrc);
16421 }
16422 com::Utf8Str strNewKeyStore;
16423 if (SUCCEEDED(rc))
16424 {
16425 if (!fDecrypt)
16426 {
16427 VBOXCRYPTOCTX hCryptoCtx;
16428 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(pszTaskCipher, task.mstrNewPassword.c_str(), &hCryptoCtx);
16429 if (RT_FAILURE(vrc))
16430 return setErrorBoth(E_FAIL, vrc, tr("Create new key store for '%s' files failed, (%Rrc)"),
16431 strFilePattern.c_str(), vrc);
16432
16433 char *pszKeyStore = NULL;
16434 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16435 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16436 if (RT_FAILURE(vrc))
16437 return setErrorBoth(E_FAIL, vrc, tr("Saving the new key store for '%s' files failed, (%Rrc)"),
16438 strFilePattern.c_str(), vrc);
16439 strNewKeyStore = pszKeyStore;
16440 RTMemFree(pszKeyStore);
16441 }
16442
16443 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
16444 it != lstFiles.end();
16445 ++it)
16446 {
16447 RTVFSIOSTREAM hVfsIosOld = NIL_RTVFSIOSTREAM;
16448 RTVFSIOSTREAM hVfsIosNew = NIL_RTVFSIOSTREAM;
16449
16450 uint64_t fOpenForRead = RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE;
16451 uint64_t fOpenForWrite = RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE;
16452
16453 vrc = i_createIoStreamForFile((*it).c_str(),
16454 fEncrypt ? NULL : task.m_pCryptoIf,
16455 fEncrypt ? NULL : strKeyStore.c_str(),
16456 fEncrypt ? NULL : task.mstrCurrentPassword.c_str(),
16457 fOpenForRead, &hVfsIosOld);
16458 if (RT_SUCCESS(vrc))
16459 {
16460 vrc = i_createIoStreamForFile((*it + ".tmp").c_str(),
16461 fDecrypt ? NULL : task.m_pCryptoIf,
16462 fDecrypt ? NULL : strNewKeyStore.c_str(),
16463 fDecrypt ? NULL : task.mstrNewPassword.c_str(),
16464 fOpenForWrite, &hVfsIosNew);
16465 if (RT_FAILURE(vrc))
16466 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
16467 (*it + ".tmp").c_str(), vrc);
16468 }
16469 else
16470 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
16471 (*it).c_str(), vrc);
16472
16473 if (RT_SUCCESS(vrc))
16474 {
16475 vrc = RTVfsUtilPumpIoStreams(hVfsIosOld, hVfsIosNew, BUF_DATA_SIZE);
16476 if (RT_FAILURE(vrc))
16477 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Changing encryption of the file '%s' failed with %Rrc"),
16478 (*it).c_str(), vrc);
16479 }
16480
16481 if (hVfsIosOld != NIL_RTVFSIOSTREAM)
16482 RTVfsIoStrmRelease(hVfsIosOld);
16483 if (hVfsIosNew != NIL_RTVFSIOSTREAM)
16484 RTVfsIoStrmRelease(hVfsIosNew);
16485 }
16486 }
16487
16488 if (SUCCEEDED(rc))
16489 {
16490 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
16491 it != lstFiles.end();
16492 ++it)
16493 {
16494 vrc = RTFileRename((*it + ".tmp").c_str(), (*it).c_str(), RTPATHRENAME_FLAGS_REPLACE);
16495 if (RT_FAILURE(vrc))
16496 {
16497 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming the file '%s' failed, (%Rrc)"),
16498 (*it + ".tmp").c_str(), vrc);
16499 break;
16500 }
16501 }
16502 }
16503
16504 if (SUCCEEDED(rc))
16505 {
16506 strKeyStore = strNewKeyStore;
16507 strKeyId = task.mstrNewPasswordId;
16508 }
16509
16510 return rc;
16511}
16512
16513/**
16514 * Task thread implementation for Machine::changeEncryption(), called from
16515 * Machine::taskHandler().
16516 *
16517 * @note Locks this object for writing.
16518 *
16519 * @param task
16520 * @return
16521 */
16522void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task)
16523{
16524 LogFlowThisFuncEnter();
16525
16526 AutoCaller autoCaller(this);
16527 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
16528 if (FAILED(autoCaller.rc()))
16529 {
16530 /* we might have been uninitialized because the session was accidentally
16531 * closed by the client, so don't assert */
16532 HRESULT rc = setError(E_FAIL,
16533 tr("The session has been accidentally closed"));
16534 task.m_pProgress->i_notifyComplete(rc);
16535 LogFlowThisFuncLeave();
16536 return;
16537 }
16538
16539 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16540
16541 HRESULT rc = S_OK;
16542 com::Utf8Str strOldKeyId = mData->mstrKeyId;
16543 com::Utf8Str strOldKeyStore = mData->mstrKeyStore;
16544 try
16545 {
16546 rc = this->i_getVirtualBox()->i_retainCryptoIf(&task.m_pCryptoIf);
16547 if (FAILED(rc))
16548 throw rc;
16549
16550 if (task.mstrCurrentPassword.isEmpty())
16551 {
16552 if (mData->mstrKeyStore.isNotEmpty())
16553 throw setError(VBOX_E_PASSWORD_INCORRECT,
16554 tr("The password given for the encrypted VM is incorrect"));
16555 }
16556 else
16557 {
16558 if (mData->mstrKeyStore.isEmpty())
16559 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16560 tr("The VM is not configured for encryption"));
16561 rc = checkEncryptionPassword(task.mstrCurrentPassword);
16562 if (rc == VBOX_E_PASSWORD_INCORRECT)
16563 throw setError(VBOX_E_PASSWORD_INCORRECT,
16564 tr("The password to decrypt the VM is incorrect"));
16565 }
16566
16567 if (task.mstrCipher.isNotEmpty())
16568 {
16569 if ( task.mstrNewPassword.isEmpty()
16570 && task.mstrNewPasswordId.isEmpty()
16571 && task.mstrCurrentPassword.isNotEmpty())
16572 {
16573 /* An empty password and password ID will default to the current password. */
16574 task.mstrNewPassword = task.mstrCurrentPassword;
16575 }
16576 else if (task.mstrNewPassword.isEmpty())
16577 throw setError(VBOX_E_OBJECT_NOT_FOUND,
16578 tr("A password must be given for the VM encryption"));
16579 else if (task.mstrNewPasswordId.isEmpty())
16580 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16581 tr("A valid identifier for the password must be given"));
16582 }
16583 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
16584 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16585 tr("The password and password identifier must be empty if the output should be unencrypted"));
16586
16587 /*
16588 * Save config.
16589 * Must be first operation to prevent making encrypted copies
16590 * for old version of the config file.
16591 */
16592 int fSave = Machine::SaveS_Force;
16593 if (task.mstrNewPassword.isNotEmpty())
16594 {
16595 VBOXCRYPTOCTX hCryptoCtx;
16596
16597 int vrc = VINF_SUCCESS;
16598 if (task.mForce || task.mstrCurrentPassword.isEmpty() || task.mstrCipher.isNotEmpty())
16599 {
16600 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(getCipherString(task.mstrCipher.c_str(), CipherModeGcm),
16601 task.mstrNewPassword.c_str(), &hCryptoCtx);
16602 if (RT_FAILURE(vrc))
16603 throw setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
16604 }
16605 else
16606 {
16607 vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(mData->mstrKeyStore.c_str(),
16608 task.mstrCurrentPassword.c_str(),
16609 &hCryptoCtx);
16610 if (RT_FAILURE(vrc))
16611 throw setErrorBoth(E_FAIL, vrc, tr("Loading old key store failed, (%Rrc)"), vrc);
16612 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
16613 if (RT_FAILURE(vrc))
16614 throw setErrorBoth(E_FAIL, vrc, tr("Changing the password failed, (%Rrc)"), vrc);
16615 }
16616
16617 char *pszKeyStore;
16618 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16619 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16620 if (RT_FAILURE(vrc))
16621 throw setErrorBoth(E_FAIL, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
16622 mData->mstrKeyStore = pszKeyStore;
16623 RTStrFree(pszKeyStore);
16624 mData->mstrKeyId = task.mstrNewPasswordId;
16625 size_t cbPassword = task.mstrNewPassword.length() + 1;
16626 uint8_t *pbPassword = (uint8_t *)task.mstrNewPassword.c_str();
16627 mData->mpKeyStore->deleteSecretKey(task.mstrNewPasswordId);
16628 mData->mpKeyStore->addSecretKey(task.mstrNewPasswordId, pbPassword, cbPassword);
16629 mNvramStore->i_addPassword(task.mstrNewPasswordId, task.mstrNewPassword);
16630
16631 /*
16632 * Remove backuped config after saving because it can contain
16633 * unencrypted version of the config
16634 */
16635 fSave |= Machine::SaveS_RemoveBackup;
16636 }
16637 else
16638 {
16639 mData->mstrKeyId.setNull();
16640 mData->mstrKeyStore.setNull();
16641 }
16642
16643 Bstr bstrCurrentPassword(task.mstrCurrentPassword);
16644 Bstr bstrCipher(getCipherString(task.mstrCipher.c_str(), CipherModeXts));
16645 Bstr bstrNewPassword(task.mstrNewPassword);
16646 Bstr bstrNewPasswordId(task.mstrNewPasswordId);
16647 /* encrypt mediums */
16648 alock.release();
16649 for (MediaList::iterator it = task.mllMedia.begin();
16650 it != task.mllMedia.end();
16651 ++it)
16652 {
16653 ComPtr<IProgress> pProgress1;
16654 HRESULT hrc = (*it)->ChangeEncryption(bstrCurrentPassword.raw(), bstrCipher.raw(),
16655 bstrNewPassword.raw(), bstrNewPasswordId.raw(),
16656 pProgress1.asOutParam());
16657 if (FAILED(hrc)) throw hrc;
16658 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress1, 0 /* indefinite wait */);
16659 if (FAILED(hrc)) throw hrc;
16660 }
16661 alock.acquire();
16662
16663 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the SAV files")).raw(), 1);
16664
16665 Utf8Str strFullSnapshotFolder;
16666 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
16667
16668 /* .sav files (main and snapshots) */
16669 rc = i_changeEncryptionForComponent(task, strFullSnapshotFolder, "*.sav",
16670 mSSData->strStateKeyStore, mSSData->strStateKeyId, CipherModeGcm);
16671 if (FAILED(rc))
16672 /* the helper function already sets error object */
16673 throw rc;
16674
16675 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the NVRAM files")).raw(), 1);
16676
16677 /* .nvram files */
16678 com::Utf8Str strNVRAMKeyId;
16679 com::Utf8Str strNVRAMKeyStore;
16680 rc = mNvramStore->i_getEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16681 if (FAILED(rc))
16682 throw setError(rc, tr("Getting NVRAM encryption settings failed (%Rhrc)"), rc);
16683
16684 Utf8Str strMachineFolder;
16685 i_calculateFullPath(".", strMachineFolder);
16686
16687 rc = i_changeEncryptionForComponent(task, strMachineFolder, "*.nvram",
16688 strNVRAMKeyStore, strNVRAMKeyId, CipherModeGcm);
16689 if (FAILED(rc))
16690 /* the helper function already sets error object */
16691 throw rc;
16692
16693 rc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16694 if (FAILED(rc))
16695 throw setError(rc, tr("Setting NVRAM encryption settings failed (%Rhrc)"), rc);
16696
16697 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of log files")).raw(), 1);
16698
16699 /* .log files */
16700 com::Utf8Str strLogFolder;
16701 i_getLogFolder(strLogFolder);
16702 rc = i_changeEncryptionForComponent(task, strLogFolder, "VBox.log*",
16703 mData->mstrLogKeyStore, mData->mstrLogKeyId, CipherModeCtr);
16704 if (FAILED(rc))
16705 /* the helper function already sets error object */
16706 throw rc;
16707
16708 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the config file")).raw(), 1);
16709
16710 i_saveSettings(NULL, alock, fSave);
16711 }
16712 catch (HRESULT aRC)
16713 {
16714 rc = aRC;
16715 mData->mstrKeyId = strOldKeyId;
16716 mData->mstrKeyStore = strOldKeyStore;
16717 }
16718
16719 task.m_pProgress->i_notifyComplete(rc);
16720
16721 LogFlowThisFuncLeave();
16722}
16723#endif /*!VBOX_WITH_FULL_VM_ENCRYPTION*/
16724
16725HRESULT Machine::changeEncryption(const com::Utf8Str &aCurrentPassword,
16726 const com::Utf8Str &aCipher,
16727 const com::Utf8Str &aNewPassword,
16728 const com::Utf8Str &aNewPasswordId,
16729 BOOL aForce,
16730 ComPtr<IProgress> &aProgress)
16731{
16732 LogFlowFuncEnter();
16733
16734#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16735 RT_NOREF(aCurrentPassword, aCipher, aNewPassword, aNewPasswordId, aForce, aProgress);
16736 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16737#else
16738 /* make the VM accessible */
16739 if (!mData->mAccessible)
16740 {
16741 if ( aCurrentPassword.isEmpty()
16742 || mData->mstrKeyId.isEmpty())
16743 return setError(E_ACCESSDENIED, tr("Machine is inaccessible"));
16744
16745 HRESULT rc = addEncryptionPassword(mData->mstrKeyId, aCurrentPassword);
16746 if (FAILED(rc))
16747 return rc;
16748 }
16749
16750 AutoLimitedCaller autoCaller(this);
16751 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16752
16753 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16754
16755 /* define mediums to be change encryption */
16756
16757 MediaList llMedia;
16758 for (MediumAttachmentList::iterator
16759 it = mMediumAttachments->begin();
16760 it != mMediumAttachments->end();
16761 ++it)
16762 {
16763 ComObjPtr<MediumAttachment> &pAttach = *it;
16764 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
16765
16766 if (!pMedium.isNull())
16767 {
16768 AutoCaller mac(pMedium);
16769 if (FAILED(mac.rc())) return mac.rc();
16770 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
16771 DeviceType_T devType = pMedium->i_getDeviceType();
16772 if (devType == DeviceType_HardDisk)
16773 {
16774 /*
16775 * We need to move to last child because the Medium::changeEncryption
16776 * encrypts all chain of specified medium with its parents.
16777 * Also we perform cheking of back reference and children for
16778 * all media in the chain to raise error before we start any action.
16779 * So, we first move into root parent and then we will move to last child
16780 * keeping latter in the list for encryption.
16781 */
16782
16783 /* move to root parent */
16784 ComObjPtr<Medium> pTmpMedium = pMedium;
16785 while (pTmpMedium.isNotNull())
16786 {
16787 AutoCaller mediumAC(pTmpMedium);
16788 if (FAILED(mediumAC.rc())) return mac.rc();
16789 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16790
16791 /* Cannot encrypt media which are attached to more than one virtual machine. */
16792 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16793 if (cBackRefs > 1)
16794 return setError(VBOX_E_INVALID_OBJECT_STATE,
16795 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16796 pTmpMedium->i_getName().c_str(), cBackRefs);
16797
16798 size_t cChildren = pTmpMedium->i_getChildren().size();
16799 if (cChildren > 1)
16800 return setError(VBOX_E_INVALID_OBJECT_STATE,
16801 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16802 pTmpMedium->i_getName().c_str(), cChildren);
16803
16804 pTmpMedium = pTmpMedium->i_getParent();
16805 }
16806 /* move to last child */
16807 pTmpMedium = pMedium;
16808 while (pTmpMedium.isNotNull() && pTmpMedium->i_getChildren().size() != 0)
16809 {
16810 AutoCaller mediumAC(pTmpMedium);
16811 if (FAILED(mediumAC.rc())) return mac.rc();
16812 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16813
16814 /* Cannot encrypt media which are attached to more than one virtual machine. */
16815 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16816 if (cBackRefs > 1)
16817 return setError(VBOX_E_INVALID_OBJECT_STATE,
16818 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16819 pTmpMedium->i_getName().c_str(), cBackRefs);
16820
16821 size_t cChildren = pTmpMedium->i_getChildren().size();
16822 if (cChildren > 1)
16823 return setError(VBOX_E_INVALID_OBJECT_STATE,
16824 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16825 pTmpMedium->i_getName().c_str(), cChildren);
16826
16827 pTmpMedium = pTmpMedium->i_getChildren().front();
16828 }
16829 llMedia.push_back(pTmpMedium);
16830 }
16831 }
16832 }
16833
16834 ComObjPtr<Progress> pProgress;
16835 pProgress.createObject();
16836 HRESULT rc = pProgress->init(i_getVirtualBox(),
16837 static_cast<IMachine*>(this) /* aInitiator */,
16838 tr("Change encryption"),
16839 TRUE /* fCancellable */,
16840 (ULONG)(4 + + llMedia.size()), // cOperations
16841 tr("Change encryption of the mediuma"));
16842 if (FAILED(rc))
16843 return rc;
16844
16845 /* create and start the task on a separate thread (note that it will not
16846 * start working until we release alock) */
16847 ChangeEncryptionTask *pTask = new ChangeEncryptionTask(this, pProgress, "VM encryption",
16848 aCurrentPassword, aCipher, aNewPassword,
16849 aNewPasswordId, aForce, llMedia);
16850 rc = pTask->createThread();
16851 pTask = NULL;
16852 if (FAILED(rc))
16853 return rc;
16854
16855 pProgress.queryInterfaceTo(aProgress.asOutParam());
16856
16857 LogFlowFuncLeave();
16858
16859 return S_OK;
16860#endif
16861}
16862
16863HRESULT Machine::getEncryptionSettings(com::Utf8Str &aCipher,
16864 com::Utf8Str &aPasswordId)
16865{
16866#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16867 RT_NOREF(aCipher, aPasswordId);
16868 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16869#else
16870 AutoLimitedCaller autoCaller(this);
16871 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16872
16873 PCVBOXCRYPTOIF pCryptoIf = NULL;
16874 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16875 if (FAILED(hrc)) return hrc; /* Error is set */
16876
16877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16878
16879 if (mData->mstrKeyStore.isNotEmpty())
16880 {
16881 char *pszCipher = NULL;
16882 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), NULL /*pszPassword*/,
16883 NULL /*ppbKey*/, NULL /*pcbKey*/, &pszCipher);
16884 if (RT_SUCCESS(vrc))
16885 {
16886 aCipher = getCipherStringWithoutMode(pszCipher);
16887 RTStrFree(pszCipher);
16888 aPasswordId = mData->mstrKeyId;
16889 }
16890 else
16891 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
16892 tr("Failed to query the encryption settings with %Rrc"),
16893 vrc);
16894 }
16895 else
16896 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16897
16898 mParent->i_releaseCryptoIf(pCryptoIf);
16899
16900 return hrc;
16901#endif
16902}
16903
16904HRESULT Machine::checkEncryptionPassword(const com::Utf8Str &aPassword)
16905{
16906#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16907 RT_NOREF(aPassword);
16908 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16909#else
16910 AutoLimitedCaller autoCaller(this);
16911 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16912
16913 PCVBOXCRYPTOIF pCryptoIf = NULL;
16914 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16915 if (FAILED(hrc)) return hrc; /* Error is set */
16916
16917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16918
16919 if (mData->mstrKeyStore.isNotEmpty())
16920 {
16921 char *pszCipher = NULL;
16922 uint8_t *pbDek = NULL;
16923 size_t cbDek = 0;
16924 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), aPassword.c_str(),
16925 &pbDek, &cbDek, &pszCipher);
16926 if (RT_SUCCESS(vrc))
16927 {
16928 RTStrFree(pszCipher);
16929 RTMemSaferFree(pbDek, cbDek);
16930 }
16931 else
16932 hrc = setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc,
16933 tr("The password supplied for the encrypted machine is incorrect"));
16934 }
16935 else
16936 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16937
16938 mParent->i_releaseCryptoIf(pCryptoIf);
16939
16940 return hrc;
16941#endif
16942}
16943
16944HRESULT Machine::addEncryptionPassword(const com::Utf8Str &aId,
16945 const com::Utf8Str &aPassword)
16946{
16947#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16948 RT_NOREF(aId, aPassword);
16949 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16950#else
16951 AutoLimitedCaller autoCaller(this);
16952 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16953
16954 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16955
16956 size_t cbPassword = aPassword.length() + 1;
16957 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
16958
16959 mData->mpKeyStore->addSecretKey(aId, pbPassword, cbPassword);
16960
16961 if ( mData->mAccessible
16962 && mData->mSession.mState == SessionState_Locked
16963 && mData->mSession.mLockType == LockType_VM
16964 && mData->mSession.mDirectControl != NULL)
16965 {
16966 /* get the console from the direct session */
16967 ComPtr<IConsole> console;
16968 HRESULT rc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16969 ComAssertComRC(rc);
16970 /* send passsword to console */
16971 console->AddEncryptionPassword(Bstr(aId).raw(),
16972 Bstr(aPassword).raw(),
16973 TRUE);
16974 }
16975
16976 if (mData->mstrKeyId == aId)
16977 {
16978 HRESULT hrc = checkEncryptionPassword(aPassword);
16979 if (FAILED(hrc))
16980 return hrc;
16981
16982 if (SUCCEEDED(hrc))
16983 {
16984 /*
16985 * Encryption is used and password is correct,
16986 * Reinit the machine if required.
16987 */
16988 BOOL fAccessible;
16989 alock.release();
16990 getAccessible(&fAccessible);
16991 alock.acquire();
16992 }
16993 }
16994
16995 /*
16996 * Add the password into the NvramStore only after
16997 * the machine becomes accessible and the NvramStore
16998 * contains key id and key store.
16999 */
17000 if (mNvramStore.isNotNull())
17001 mNvramStore->i_addPassword(aId, aPassword);
17002
17003 return S_OK;
17004#endif
17005}
17006
17007HRESULT Machine::addEncryptionPasswords(const std::vector<com::Utf8Str> &aIds,
17008 const std::vector<com::Utf8Str> &aPasswords)
17009{
17010#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
17011 RT_NOREF(aIds, aPasswords);
17012 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
17013#else
17014 if (aIds.size() != aPasswords.size())
17015 return setError(E_INVALIDARG, tr("Id and passwords arrays must have the same size"));
17016
17017 HRESULT hrc = S_OK;
17018 for (size_t i = 0; i < aIds.size() && SUCCEEDED(hrc); ++i)
17019 hrc = addEncryptionPassword(aIds[i], aPasswords[i]);
17020
17021 return hrc;
17022#endif
17023}
17024
17025HRESULT Machine::removeEncryptionPassword(AutoCaller &autoCaller, const com::Utf8Str &aId)
17026{
17027#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
17028 RT_NOREF(autoCaller, aId);
17029 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
17030#else
17031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
17032
17033 if ( mData->mAccessible
17034 && mData->mSession.mState == SessionState_Locked
17035 && mData->mSession.mLockType == LockType_VM
17036 && mData->mSession.mDirectControl != NULL)
17037 {
17038 /* get the console from the direct session */
17039 ComPtr<IConsole> console;
17040 HRESULT rc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
17041 ComAssertComRC(rc);
17042 /* send passsword to console */
17043 console->RemoveEncryptionPassword(Bstr(aId).raw());
17044 }
17045
17046 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty() && mData->mstrKeyId == aId)
17047 {
17048 if (Global::IsOnlineOrTransient(mData->mMachineState))
17049 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
17050 alock.release();
17051 autoCaller.release();
17052 /* return because all passwords are purged when machine becomes inaccessible; */
17053 return i_setInaccessible();
17054 }
17055
17056 if (mNvramStore.isNotNull())
17057 mNvramStore->i_removePassword(aId);
17058 mData->mpKeyStore->deleteSecretKey(aId);
17059 return S_OK;
17060#endif
17061}
17062
17063HRESULT Machine::clearAllEncryptionPasswords(AutoCaller &autoCaller)
17064{
17065#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
17066 RT_NOREF(autoCaller);
17067 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
17068#else
17069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
17070
17071 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty())
17072 {
17073 if (Global::IsOnlineOrTransient(mData->mMachineState))
17074 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
17075 alock.release();
17076 autoCaller.release();
17077 /* return because all passwords are purged when machine becomes inaccessible; */
17078 return i_setInaccessible();
17079 }
17080
17081 mNvramStore->i_removeAllPasswords();
17082 mData->mpKeyStore->deleteAllSecretKeys(false, true);
17083 return S_OK;
17084#endif
17085}
17086
17087#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
17088HRESULT Machine::i_setInaccessible()
17089{
17090 if (!mData->mAccessible)
17091 return S_OK;
17092
17093 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
17094 VirtualBox *pParent = mParent;
17095 com::Utf8Str strConfigFile = mData->m_strConfigFile;
17096 Guid id(i_getId());
17097
17098 alock.release();
17099
17100 uninit();
17101 HRESULT rc = initFromSettings(pParent, strConfigFile, &id, com::Utf8Str());
17102
17103 alock.acquire();
17104 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
17105 return rc;
17106}
17107#endif
17108
17109/* This isn't handled entirely by the wrapper generator yet. */
17110#ifdef VBOX_WITH_XPCOM
17111NS_DECL_CLASSINFO(SessionMachine)
17112NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
17113
17114NS_DECL_CLASSINFO(SnapshotMachine)
17115NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
17116#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