VirtualBox

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

Last change on this file since 97698 was 97523, checked in by vboxsync, 2 years ago

Main: Add new OnMachineGroupsChanged event which fires when the groups of a machine change, bugref:10276

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 601.0 KB
Line 
1/* $Id: MachineImpl.cpp 97523 2022-11-14 08:58:36Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
29
30/* Make sure all the stdint.h macros are included - must come first! */
31#ifndef __STDC_LIMIT_MACROS
32# define __STDC_LIMIT_MACROS
33#endif
34#ifndef __STDC_CONSTANT_MACROS
35# define __STDC_CONSTANT_MACROS
36#endif
37
38#include "LoggingNew.h"
39#include "VirtualBoxImpl.h"
40#include "MachineImpl.h"
41#include "SnapshotImpl.h"
42#include "ClientToken.h"
43#include "ProgressImpl.h"
44#include "ProgressProxyImpl.h"
45#include "MediumAttachmentImpl.h"
46#include "MediumImpl.h"
47#include "MediumLock.h"
48#include "USBControllerImpl.h"
49#include "USBDeviceFiltersImpl.h"
50#include "HostImpl.h"
51#include "SharedFolderImpl.h"
52#include "GuestOSTypeImpl.h"
53#include "VirtualBoxErrorInfoImpl.h"
54#include "StorageControllerImpl.h"
55#include "DisplayImpl.h"
56#include "DisplayUtils.h"
57#include "MachineImplCloneVM.h"
58#include "AutostartDb.h"
59#include "SystemPropertiesImpl.h"
60#include "MachineImplMoveVM.h"
61#include "ExtPackManagerImpl.h"
62#include "MachineLaunchVMCommonWorker.h"
63#include "CryptoUtils.h"
64
65// generated header
66#include "VBoxEvents.h"
67
68#ifdef VBOX_WITH_USB
69# include "USBProxyService.h"
70#endif
71
72#include "AutoCaller.h"
73#include "HashedPw.h"
74#include "Performance.h"
75#include "StringifyEnums.h"
76
77#include <iprt/asm.h>
78#include <iprt/path.h>
79#include <iprt/dir.h>
80#include <iprt/env.h>
81#include <iprt/lockvalidator.h>
82#include <iprt/memsafer.h>
83#include <iprt/process.h>
84#include <iprt/cpp/utils.h>
85#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
86#include <iprt/sha.h>
87#include <iprt/string.h>
88#include <iprt/ctype.h>
89
90#include <VBox/com/array.h>
91#include <VBox/com/list.h>
92#include <VBox/VBoxCryptoIf.h>
93
94#include <VBox/err.h>
95#include <VBox/param.h>
96#include <VBox/settings.h>
97#include <VBox/VMMDev.h>
98#include <VBox/vmm/ssm.h>
99
100#ifdef VBOX_WITH_GUEST_PROPS
101# include <VBox/HostServices/GuestPropertySvc.h>
102# include <VBox/com/array.h>
103#endif
104
105#ifdef VBOX_WITH_SHARED_CLIPBOARD
106# include <VBox/HostServices/VBoxClipboardSvc.h>
107#endif
108
109#include "VBox/com/MultiResult.h"
110
111#include <algorithm>
112
113#ifdef VBOX_WITH_DTRACE_R3_MAIN
114# include "dtrace/VBoxAPI.h"
115#endif
116
117#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
118# define HOSTSUFF_EXE ".exe"
119#else /* !RT_OS_WINDOWS */
120# define HOSTSUFF_EXE ""
121#endif /* !RT_OS_WINDOWS */
122
123// defines / prototypes
124/////////////////////////////////////////////////////////////////////////////
125
126#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
127# define BUF_DATA_SIZE _64K
128
129enum CipherMode
130{
131 CipherModeGcm = 0,
132 CipherModeCtr,
133 CipherModeXts,
134 CipherModeMax
135};
136
137enum AesSize
138{
139 Aes128 = 0,
140 Aes256,
141 AesMax
142};
143
144const char *g_apszCipher[AesMax][CipherModeMax] =
145{
146 {"AES-GCM128", "AES-CTR128", "AES-XTS128-PLAIN64"},
147 {"AES-GCM256", "AES-CTR256", "AES-XTS256-PLAIN64"}
148};
149const char *g_apszCipherAlgo[AesMax] = {"AES-128", "AES-256"};
150
151static const char *getCipherString(const char *pszAlgo, const int iMode)
152{
153 if (iMode >= CipherModeMax)
154 return pszAlgo;
155
156 for (int i = 0; i < AesMax; i++)
157 {
158 if (strcmp(pszAlgo, g_apszCipherAlgo[i]) == 0)
159 return g_apszCipher[i][iMode];
160 }
161 return pszAlgo;
162}
163
164static const char *getCipherStringWithoutMode(const char *pszAlgo)
165{
166 for (int i = 0; i < AesMax; i++)
167 {
168 for (int j = 0; j < CipherModeMax; j++)
169 {
170 if (strcmp(pszAlgo, g_apszCipher[i][j]) == 0)
171 return g_apszCipherAlgo[i];
172 }
173 }
174 return pszAlgo;
175}
176#endif
177
178/////////////////////////////////////////////////////////////////////////////
179// Machine::Data structure
180/////////////////////////////////////////////////////////////////////////////
181
182Machine::Data::Data()
183{
184 mRegistered = FALSE;
185 pMachineConfigFile = NULL;
186 /* Contains hints on what has changed when the user is using the VM (config
187 * changes, running the VM, ...). This is used to decide if a config needs
188 * to be written to disk. */
189 flModifications = 0;
190 /* VM modification usually also trigger setting the current state to
191 * "Modified". Although this is not always the case. An e.g. is the VM
192 * initialization phase or when snapshot related data is changed. The
193 * actually behavior is controlled by the following flag. */
194 m_fAllowStateModification = false;
195 mAccessible = FALSE;
196 /* mUuid is initialized in Machine::init() */
197
198 mMachineState = MachineState_PoweredOff;
199 RTTimeNow(&mLastStateChange);
200
201 mMachineStateDeps = 0;
202 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
203 mMachineStateChangePending = 0;
204
205 mCurrentStateModified = TRUE;
206 mGuestPropertiesModified = FALSE;
207
208 mSession.mPID = NIL_RTPROCESS;
209 mSession.mLockType = LockType_Null;
210 mSession.mState = SessionState_Unlocked;
211
212#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
213 mpKeyStore = NULL;
214#endif
215}
216
217Machine::Data::~Data()
218{
219 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
220 {
221 RTSemEventMultiDestroy(mMachineStateDepsSem);
222 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
223 }
224 if (pMachineConfigFile)
225 {
226 delete pMachineConfigFile;
227 pMachineConfigFile = NULL;
228 }
229}
230
231/////////////////////////////////////////////////////////////////////////////
232// Machine::HWData structure
233/////////////////////////////////////////////////////////////////////////////
234
235Machine::HWData::HWData()
236{
237 /* default values for a newly created machine */
238 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
239 mMemorySize = 128;
240 mCPUCount = 1;
241 mCPUHotPlugEnabled = false;
242 mMemoryBalloonSize = 0;
243 mPageFusionEnabled = false;
244 mHWVirtExEnabled = true;
245 mHWVirtExNestedPagingEnabled = true;
246 mHWVirtExLargePagesEnabled = HC_ARCH_BITS == 64; /* Not supported on 32 bits hosts. */
247 mHWVirtExVPIDEnabled = true;
248 mHWVirtExUXEnabled = true;
249 mHWVirtExForceEnabled = false;
250 mHWVirtExUseNativeApi = false;
251 mHWVirtExVirtVmsaveVmload = true;
252#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
253 mPAEEnabled = true;
254#else
255 mPAEEnabled = false;
256#endif
257 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
258 mTripleFaultReset = false;
259 mAPIC = true;
260 mX2APIC = false;
261 mIBPBOnVMExit = false;
262 mIBPBOnVMEntry = false;
263 mSpecCtrl = false;
264 mSpecCtrlByHost = false;
265 mL1DFlushOnSched = true;
266 mL1DFlushOnVMEntry = false;
267 mMDSClearOnSched = true;
268 mMDSClearOnVMEntry = false;
269 mNestedHWVirt = false;
270 mHPETEnabled = false;
271 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
272 mCpuIdPortabilityLevel = 0;
273 mCpuProfile = "host";
274
275 /* default boot order: floppy - DVD - HDD */
276 mBootOrder[0] = DeviceType_Floppy;
277 mBootOrder[1] = DeviceType_DVD;
278 mBootOrder[2] = DeviceType_HardDisk;
279 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
280 mBootOrder[i] = DeviceType_Null;
281
282 mClipboardMode = ClipboardMode_Disabled;
283 mClipboardFileTransfersEnabled = FALSE;
284
285 mDnDMode = DnDMode_Disabled;
286
287 mFirmwareType = FirmwareType_BIOS;
288 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
289 mPointingHIDType = PointingHIDType_PS2Mouse;
290 mChipsetType = ChipsetType_PIIX3;
291 mIommuType = IommuType_None;
292 mParavirtProvider = ParavirtProvider_Default;
293 mEmulatedUSBCardReaderEnabled = FALSE;
294
295 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
296 mCPUAttached[i] = false;
297
298 mIOCacheEnabled = true;
299 mIOCacheSize = 5; /* 5MB */
300}
301
302Machine::HWData::~HWData()
303{
304}
305
306/////////////////////////////////////////////////////////////////////////////
307// Machine class
308/////////////////////////////////////////////////////////////////////////////
309
310// constructor / destructor
311/////////////////////////////////////////////////////////////////////////////
312
313Machine::Machine() :
314#ifdef VBOX_WITH_RESOURCE_USAGE_API
315 mCollectorGuest(NULL),
316#endif
317 mPeer(NULL),
318 mParent(NULL),
319 mSerialPorts(),
320 mParallelPorts(),
321 uRegistryNeedsSaving(0)
322{}
323
324Machine::~Machine()
325{}
326
327HRESULT Machine::FinalConstruct()
328{
329 LogFlowThisFunc(("\n"));
330 return BaseFinalConstruct();
331}
332
333void Machine::FinalRelease()
334{
335 LogFlowThisFunc(("\n"));
336 uninit();
337 BaseFinalRelease();
338}
339
340/**
341 * Initializes a new machine instance; this init() variant creates a new, empty machine.
342 * This gets called from VirtualBox::CreateMachine().
343 *
344 * @param aParent Associated parent object
345 * @param strConfigFile Local file system path to the VM settings file (can
346 * be relative to the VirtualBox config directory).
347 * @param strName name for the machine
348 * @param llGroups list of groups for the machine
349 * @param strOsType OS Type string (stored as is if aOsType is NULL).
350 * @param aOsType OS Type of this machine or NULL.
351 * @param aId UUID for the new machine.
352 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
353 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
354 * scheme (includes the UUID).
355 * @param aCipher The cipher to encrypt the VM with.
356 * @param aPasswordId The password ID, empty if the VM should not be encrypted.
357 * @param aPassword The password to encrypt the VM with.
358 *
359 * @return Success indicator. if not S_OK, the machine object is invalid
360 */
361HRESULT Machine::init(VirtualBox *aParent,
362 const Utf8Str &strConfigFile,
363 const Utf8Str &strName,
364 const StringsList &llGroups,
365 const Utf8Str &strOsType,
366 GuestOSType *aOsType,
367 const Guid &aId,
368 bool fForceOverwrite,
369 bool fDirectoryIncludesUUID,
370 const com::Utf8Str &aCipher,
371 const com::Utf8Str &aPasswordId,
372 const com::Utf8Str &aPassword)
373{
374 LogFlowThisFuncEnter();
375 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
376
377#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
378 RT_NOREF(aCipher);
379 if (aPassword.isNotEmpty() || aPasswordId.isNotEmpty())
380 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
381#endif
382
383 /* Enclose the state transition NotReady->InInit->Ready */
384 AutoInitSpan autoInitSpan(this);
385 AssertReturn(autoInitSpan.isOk(), E_FAIL);
386
387 HRESULT rc = initImpl(aParent, strConfigFile);
388 if (FAILED(rc)) return rc;
389
390#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
391 com::Utf8Str strSsmKeyId;
392 com::Utf8Str strSsmKeyStore;
393 com::Utf8Str strNVRAMKeyId;
394 com::Utf8Str strNVRAMKeyStore;
395
396 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
397 {
398 /* Resolve the cryptographic interface. */
399 PCVBOXCRYPTOIF pCryptoIf = NULL;
400 HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
401 if (SUCCEEDED(hrc))
402 {
403 CipherMode aenmMode[] = {CipherModeGcm, CipherModeGcm, CipherModeGcm, CipherModeCtr};
404 com::Utf8Str *astrKeyId[] = {&mData->mstrKeyId, &strSsmKeyId, &strNVRAMKeyId, &mData->mstrLogKeyId};
405 com::Utf8Str *astrKeyStore[] = {&mData->mstrKeyStore, &strSsmKeyStore, &strNVRAMKeyStore, &mData->mstrLogKeyStore};
406
407 for (uint32_t i = 0; i < RT_ELEMENTS(astrKeyId); i++)
408 {
409 const char *pszCipher = getCipherString(aCipher.c_str(), aenmMode[i]);
410 if (!pszCipher)
411 {
412 hrc = setError(VBOX_E_NOT_SUPPORTED,
413 tr("The cipher '%s' is not supported"), aCipher.c_str());
414 break;
415 }
416
417 VBOXCRYPTOCTX hCryptoCtx;
418 int vrc = pCryptoIf->pfnCryptoCtxCreate(pszCipher, aPassword.c_str(), &hCryptoCtx);
419 if (RT_FAILURE(vrc))
420 {
421 hrc = setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
422 break;
423 }
424
425 char *pszKeyStore;
426 vrc = pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
427 int vrc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
428 AssertRC(vrc2);
429
430 if (RT_FAILURE(vrc))
431 {
432 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
433 break;
434 }
435
436 *(astrKeyStore[i]) = pszKeyStore;
437 RTMemFree(pszKeyStore);
438 *(astrKeyId[i]) = aPasswordId;
439 }
440
441 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
442 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
443
444 if (FAILED(hrc))
445 return hrc; /* Error is set. */
446 }
447 else
448 return hrc; /* Error is set. */
449 }
450#endif
451
452 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
453 if (FAILED(rc)) return rc;
454
455 if (SUCCEEDED(rc))
456 {
457 // create an empty machine config
458 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
459
460 rc = initDataAndChildObjects();
461 }
462
463 if (SUCCEEDED(rc))
464 {
465#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
466 mSSData->strStateKeyId = strSsmKeyId;
467 mSSData->strStateKeyStore = strSsmKeyStore;
468#endif
469
470 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
471 mData->mAccessible = TRUE;
472
473 unconst(mData->mUuid) = aId;
474
475 mUserData->s.strName = strName;
476
477 if (llGroups.size())
478 mUserData->s.llGroups = llGroups;
479
480 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
481 // the "name sync" flag determines whether the machine directory gets renamed along
482 // with the machine file; say so if the settings file name is the same as the
483 // settings file parent directory (machine directory)
484 mUserData->s.fNameSync = i_isInOwnDir();
485
486 // initialize the default snapshots folder
487 rc = COMSETTER(SnapshotFolder)(NULL);
488 AssertComRC(rc);
489
490 if (aOsType)
491 {
492 /* Store OS type */
493 mUserData->s.strOsType = aOsType->i_id();
494
495 /* Let the OS type select 64-bit ness. */
496 mHWData->mLongMode = aOsType->i_is64Bit()
497 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
498
499 /* Let the OS type enable the X2APIC */
500 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
501
502 rc = aOsType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
503 AssertComRC(rc);
504 }
505 else if (!strOsType.isEmpty())
506 {
507 /* Store OS type */
508 mUserData->s.strOsType = strOsType;
509
510 /* No guest OS type object. Pick some plausible defaults which the
511 * host can handle. There's no way to know or validate anything. */
512 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
513 mHWData->mX2APIC = false;
514 }
515
516 /* Apply BIOS defaults. */
517 mBIOSSettings->i_applyDefaults(aOsType);
518
519 /* Apply TPM defaults. */
520 mTrustedPlatformModule->i_applyDefaults(aOsType);
521
522 /* Apply recording defaults. */
523 mRecordingSettings->i_applyDefaults();
524
525 /* Apply network adapters defaults */
526 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
527 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
528
529 /* Apply serial port defaults */
530 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
531 mSerialPorts[slot]->i_applyDefaults(aOsType);
532
533 /* Apply parallel port defaults */
534 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
535 mParallelPorts[slot]->i_applyDefaults();
536
537 /* Enable the VMMDev testing feature for bootsector VMs: */
538 if (aOsType && aOsType->i_id() == "VBoxBS_64")
539 mData->pMachineConfigFile->mapExtraDataItems["VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled"] = "1";
540
541#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
542 rc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
543#endif
544 if (SUCCEEDED(rc))
545 {
546 /* At this point the changing of the current state modification
547 * flag is allowed. */
548 i_allowStateModification();
549
550 /* commit all changes made during the initialization */
551 i_commit();
552 }
553 }
554
555 /* Confirm a successful initialization when it's the case */
556 if (SUCCEEDED(rc))
557 {
558#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
559 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
560 {
561 size_t cbPassword = aPassword.length() + 1;
562 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
563 mData->mpKeyStore->addSecretKey(aPasswordId, pbPassword, cbPassword);
564 }
565#endif
566
567 if (mData->mAccessible)
568 autoInitSpan.setSucceeded();
569 else
570 autoInitSpan.setLimited();
571 }
572
573 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
574 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
575 mData->mRegistered,
576 mData->mAccessible,
577 rc));
578
579 LogFlowThisFuncLeave();
580
581 return rc;
582}
583
584/**
585 * Initializes a new instance with data from machine XML (formerly Init_Registered).
586 * Gets called in two modes:
587 *
588 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
589 * UUID is specified and we mark the machine as "registered";
590 *
591 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
592 * and the machine remains unregistered until RegisterMachine() is called.
593 *
594 * @param aParent Associated parent object
595 * @param strConfigFile Local file system path to the VM settings file (can
596 * be relative to the VirtualBox config directory).
597 * @param aId UUID of the machine or NULL (see above).
598 * @param strPassword Password for decrypting the config
599 *
600 * @return Success indicator. if not S_OK, the machine object is invalid
601 */
602HRESULT Machine::initFromSettings(VirtualBox *aParent,
603 const Utf8Str &strConfigFile,
604 const Guid *aId,
605 const com::Utf8Str &strPassword)
606{
607 LogFlowThisFuncEnter();
608 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
609
610 PCVBOXCRYPTOIF pCryptoIf = NULL;
611#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
612 if (strPassword.isNotEmpty())
613 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
614#else
615 if (strPassword.isNotEmpty())
616 {
617 /* Get at the crpytographic interface. */
618 HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
619 if (FAILED(hrc))
620 return hrc; /* Error is set. */
621 }
622#endif
623
624 /* Enclose the state transition NotReady->InInit->Ready */
625 AutoInitSpan autoInitSpan(this);
626 AssertReturn(autoInitSpan.isOk(), E_FAIL);
627
628 HRESULT rc = initImpl(aParent, strConfigFile);
629 if (FAILED(rc)) return rc;
630
631 if (aId)
632 {
633 // loading a registered VM:
634 unconst(mData->mUuid) = *aId;
635 mData->mRegistered = TRUE;
636 // now load the settings from XML:
637 rc = i_registeredInit();
638 // this calls initDataAndChildObjects() and loadSettings()
639 }
640 else
641 {
642 // opening an unregistered VM (VirtualBox::OpenMachine()):
643 rc = initDataAndChildObjects();
644
645 if (SUCCEEDED(rc))
646 {
647 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
648 mData->mAccessible = TRUE;
649
650 try
651 {
652 // load and parse machine XML; this will throw on XML or logic errors
653 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
654 pCryptoIf,
655 strPassword.c_str());
656
657 // reject VM UUID duplicates, they can happen if someone
658 // tries to register an already known VM config again
659 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
660 true /* fPermitInaccessible */,
661 false /* aDoSetError */,
662 NULL) != VBOX_E_OBJECT_NOT_FOUND)
663 {
664 throw setError(E_FAIL,
665 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
666 mData->m_strConfigFile.c_str());
667 }
668
669 // use UUID from machine config
670 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
671
672#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
673 // No exception is thrown if config is encrypted, allowing us to get the uuid and the encryption fields.
674 // We fill in the encryptions fields, and the rest will be filled in if all data parsed.
675 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
676 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
677#endif
678
679 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
680 {
681 // We just set the inaccessible state and fill the error info allowing the caller
682 // to register the machine with encrypted config even if the password is incorrect
683 mData->mAccessible = FALSE;
684
685 /* fetch the current error info */
686 mData->mAccessError = com::ErrorInfo();
687
688 setError(VBOX_E_PASSWORD_INCORRECT,
689 tr("Decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
690 mData->pMachineConfigFile->uuid.raw());
691 }
692 else
693 {
694#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
695 if (strPassword.isNotEmpty())
696 {
697 size_t cbKey = strPassword.length() + 1; /* Include terminator */
698 const uint8_t *pbKey = (const uint8_t *)strPassword.c_str();
699 mData->mpKeyStore->addSecretKey(mData->mstrKeyId, pbKey, cbKey);
700 }
701#endif
702
703 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
704 NULL /* puuidRegistry */);
705 if (FAILED(rc)) throw rc;
706
707 /* At this point the changing of the current state modification
708 * flag is allowed. */
709 i_allowStateModification();
710
711 i_commit();
712 }
713 }
714 catch (HRESULT err)
715 {
716 /* we assume that error info is set by the thrower */
717 rc = err;
718 }
719 catch (...)
720 {
721 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
722 }
723 }
724 }
725
726 /* Confirm a successful initialization when it's the case */
727 if (SUCCEEDED(rc))
728 {
729 if (mData->mAccessible)
730 autoInitSpan.setSucceeded();
731 else
732 {
733 autoInitSpan.setLimited();
734
735 // uninit media from this machine's media registry, or else
736 // reloading the settings will fail
737 mParent->i_unregisterMachineMedia(i_getId());
738 }
739 }
740
741#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
742 if (pCryptoIf)
743 {
744 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
745 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
746 }
747#endif
748
749 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
750 "rc=%08X\n",
751 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
752 mData->mRegistered, mData->mAccessible, rc));
753
754 LogFlowThisFuncLeave();
755
756 return rc;
757}
758
759/**
760 * Initializes a new instance from a machine config that is already in memory
761 * (import OVF case). Since we are importing, the UUID in the machine
762 * config is ignored and we always generate a fresh one.
763 *
764 * @param aParent Associated parent object.
765 * @param strName Name for the new machine; this overrides what is specified in config.
766 * @param strSettingsFilename File name of .vbox file.
767 * @param config Machine configuration loaded and parsed from XML.
768 *
769 * @return Success indicator. if not S_OK, the machine object is invalid
770 */
771HRESULT Machine::init(VirtualBox *aParent,
772 const Utf8Str &strName,
773 const Utf8Str &strSettingsFilename,
774 const settings::MachineConfigFile &config)
775{
776 LogFlowThisFuncEnter();
777
778 /* Enclose the state transition NotReady->InInit->Ready */
779 AutoInitSpan autoInitSpan(this);
780 AssertReturn(autoInitSpan.isOk(), E_FAIL);
781
782 HRESULT rc = initImpl(aParent, strSettingsFilename);
783 if (FAILED(rc)) return rc;
784
785 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
786 if (FAILED(rc)) return rc;
787
788 rc = initDataAndChildObjects();
789
790 if (SUCCEEDED(rc))
791 {
792 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
793 mData->mAccessible = TRUE;
794
795 // create empty machine config for instance data
796 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
797
798 // generate fresh UUID, ignore machine config
799 unconst(mData->mUuid).create();
800
801 rc = i_loadMachineDataFromSettings(config,
802 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
803
804 // override VM name as well, it may be different
805 mUserData->s.strName = strName;
806
807 if (SUCCEEDED(rc))
808 {
809 /* At this point the changing of the current state modification
810 * flag is allowed. */
811 i_allowStateModification();
812
813 /* commit all changes made during the initialization */
814 i_commit();
815 }
816 }
817
818 /* Confirm a successful initialization when it's the case */
819 if (SUCCEEDED(rc))
820 {
821 if (mData->mAccessible)
822 autoInitSpan.setSucceeded();
823 else
824 {
825 /* Ignore all errors from unregistering, they would destroy
826- * the more interesting error information we already have,
827- * pinpointing the issue with the VM config. */
828 ErrorInfoKeeper eik;
829
830 autoInitSpan.setLimited();
831
832 // uninit media from this machine's media registry, or else
833 // reloading the settings will fail
834 mParent->i_unregisterMachineMedia(i_getId());
835 }
836 }
837
838 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
839 "rc=%08X\n",
840 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
841 mData->mRegistered, mData->mAccessible, rc));
842
843 LogFlowThisFuncLeave();
844
845 return rc;
846}
847
848/**
849 * Shared code between the various init() implementations.
850 * @param aParent The VirtualBox object.
851 * @param strConfigFile Settings file.
852 * @return
853 */
854HRESULT Machine::initImpl(VirtualBox *aParent,
855 const Utf8Str &strConfigFile)
856{
857 LogFlowThisFuncEnter();
858
859 AssertReturn(aParent, E_INVALIDARG);
860 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
861
862 HRESULT rc = S_OK;
863
864 /* share the parent weakly */
865 unconst(mParent) = aParent;
866
867 /* allocate the essential machine data structure (the rest will be
868 * allocated later by initDataAndChildObjects() */
869 mData.allocate();
870
871 /* memorize the config file name (as provided) */
872 mData->m_strConfigFile = strConfigFile;
873
874 /* get the full file name */
875 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
876 if (RT_FAILURE(vrc1))
877 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
878 tr("Invalid machine settings file name '%s' (%Rrc)"),
879 strConfigFile.c_str(),
880 vrc1);
881
882#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
883 /** @todo Only create when the machine is going to be encrypted. */
884 /* Non-pageable memory is not accessible for non-VM process */
885 mData->mpKeyStore = new SecretKeyStore(false /* fKeyBufNonPageable */);
886 AssertReturn(mData->mpKeyStore, E_OUTOFMEMORY);
887#endif
888
889 LogFlowThisFuncLeave();
890
891 return rc;
892}
893
894/**
895 * Tries to create a machine settings file in the path stored in the machine
896 * instance data. Used when a new machine is created to fail gracefully if
897 * the settings file could not be written (e.g. because machine dir is read-only).
898 * @return
899 */
900HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
901{
902 HRESULT rc = S_OK;
903
904 // when we create a new machine, we must be able to create the settings file
905 RTFILE f = NIL_RTFILE;
906 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
907 if ( RT_SUCCESS(vrc)
908 || vrc == VERR_SHARING_VIOLATION
909 )
910 {
911 if (RT_SUCCESS(vrc))
912 RTFileClose(f);
913 if (!fForceOverwrite)
914 rc = setError(VBOX_E_FILE_ERROR,
915 tr("Machine settings file '%s' already exists"),
916 mData->m_strConfigFileFull.c_str());
917 else
918 {
919 /* try to delete the config file, as otherwise the creation
920 * of a new settings file will fail. */
921 i_deleteFile(mData->m_strConfigFileFull.c_str(), false /* fIgnoreFailures */, tr("existing settings file"));
922 }
923 }
924 else if ( vrc != VERR_FILE_NOT_FOUND
925 && vrc != VERR_PATH_NOT_FOUND
926 )
927 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
928 tr("Invalid machine settings file name '%s' (%Rrc)"),
929 mData->m_strConfigFileFull.c_str(),
930 vrc);
931 return rc;
932}
933
934/**
935 * Initializes the registered machine by loading the settings file.
936 * This method is separated from #init() in order to make it possible to
937 * retry the operation after VirtualBox startup instead of refusing to
938 * startup the whole VirtualBox server in case if the settings file of some
939 * registered VM is invalid or inaccessible.
940 *
941 * @note Must be always called from this object's write lock
942 * (unless called from #init() that doesn't need any locking).
943 * @note Locks the mUSBController method for writing.
944 * @note Subclasses must not call this method.
945 */
946HRESULT Machine::i_registeredInit()
947{
948 AssertReturn(!i_isSessionMachine(), E_FAIL);
949 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
950 AssertReturn(mData->mUuid.isValid(), E_FAIL);
951 AssertReturn(!mData->mAccessible, E_FAIL);
952
953 HRESULT rc = initDataAndChildObjects();
954
955 if (SUCCEEDED(rc))
956 {
957 /* Temporarily reset the registered flag in order to let setters
958 * potentially called from loadSettings() succeed (isMutable() used in
959 * all setters will return FALSE for a Machine instance if mRegistered
960 * is TRUE). */
961 mData->mRegistered = FALSE;
962
963 PCVBOXCRYPTOIF pCryptoIf = NULL;
964 SecretKey *pKey = NULL;
965 const char *pszPassword = NULL;
966#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
967 /* Resolve password and cryptographic support interface if machine is encrypted. */
968 if (mData->mstrKeyId.isNotEmpty())
969 {
970 /* Get at the crpytographic interface. */
971 rc = mParent->i_retainCryptoIf(&pCryptoIf);
972 if (SUCCEEDED(rc))
973 {
974 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
975 if (RT_SUCCESS(vrc))
976 pszPassword = (const char *)pKey->getKeyBuffer();
977 else
978 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to retain key for key ID '%s' with %Rrc"),
979 mData->mstrKeyId.c_str(), vrc);
980 }
981 }
982#else
983 RT_NOREF(pKey);
984#endif
985
986 if (SUCCEEDED(rc))
987 {
988 try
989 {
990 // load and parse machine XML; this will throw on XML or logic errors
991 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
992 pCryptoIf, pszPassword);
993
994 if (mData->mUuid != mData->pMachineConfigFile->uuid)
995 throw setError(E_FAIL,
996 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
997 mData->pMachineConfigFile->uuid.raw(),
998 mData->m_strConfigFileFull.c_str(),
999 mData->mUuid.toString().c_str(),
1000 mParent->i_settingsFilePath().c_str());
1001
1002#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1003 // If config is encrypted, no exception is thrown allowing us to get the uuid and the encryption fields.
1004 // We fill in the encryptions fields, and the rest will be filled in if all data parsed
1005 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
1006 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
1007
1008 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
1009 rc = setError(VBOX_E_PASSWORD_INCORRECT,
1010 tr("Config decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
1011 mData->pMachineConfigFile->uuid.raw());
1012 else
1013#endif
1014 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
1015 NULL /* const Guid *puuidRegistry */);
1016 if (FAILED(rc)) throw rc;
1017 }
1018 catch (HRESULT err)
1019 {
1020 /* we assume that error info is set by the thrower */
1021 rc = err;
1022 }
1023 catch (...)
1024 {
1025 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
1026 }
1027
1028 /* Restore the registered flag (even on failure) */
1029 mData->mRegistered = TRUE;
1030 }
1031
1032#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1033 if (pCryptoIf)
1034 mParent->i_releaseCryptoIf(pCryptoIf);
1035 if (pKey)
1036 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
1037#endif
1038 }
1039
1040 if (SUCCEEDED(rc))
1041 {
1042 /* Set mAccessible to TRUE only if we successfully locked and loaded
1043 * the settings file */
1044 mData->mAccessible = TRUE;
1045
1046 /* commit all changes made during loading the settings file */
1047 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
1048 /// @todo r=klaus for some reason the settings loading logic backs up
1049 // the settings, and therefore a commit is needed. Should probably be changed.
1050 }
1051 else
1052 {
1053 /* If the machine is registered, then, instead of returning a
1054 * failure, we mark it as inaccessible and set the result to
1055 * success to give it a try later */
1056
1057 /* fetch the current error info */
1058 mData->mAccessError = com::ErrorInfo();
1059 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
1060
1061 /* rollback all changes */
1062 i_rollback(false /* aNotify */);
1063
1064 // uninit media from this machine's media registry, or else
1065 // reloading the settings will fail
1066 mParent->i_unregisterMachineMedia(i_getId());
1067
1068 /* uninitialize the common part to make sure all data is reset to
1069 * default (null) values */
1070 uninitDataAndChildObjects();
1071
1072 rc = S_OK;
1073 }
1074
1075 return rc;
1076}
1077
1078/**
1079 * Uninitializes the instance.
1080 * Called either from FinalRelease() or by the parent when it gets destroyed.
1081 *
1082 * @note The caller of this method must make sure that this object
1083 * a) doesn't have active callers on the current thread and b) is not locked
1084 * by the current thread; otherwise uninit() will hang either a) due to
1085 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
1086 * a dead-lock caused by this thread waiting for all callers on the other
1087 * threads are done but preventing them from doing so by holding a lock.
1088 */
1089void Machine::uninit()
1090{
1091 LogFlowThisFuncEnter();
1092
1093 Assert(!isWriteLockOnCurrentThread());
1094
1095 Assert(!uRegistryNeedsSaving);
1096 if (uRegistryNeedsSaving)
1097 {
1098 AutoCaller autoCaller(this);
1099 if (SUCCEEDED(autoCaller.rc()))
1100 {
1101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1102 i_saveSettings(NULL, alock, Machine::SaveS_Force);
1103 }
1104 }
1105
1106 /* Enclose the state transition Ready->InUninit->NotReady */
1107 AutoUninitSpan autoUninitSpan(this);
1108 if (autoUninitSpan.uninitDone())
1109 return;
1110
1111 Assert(!i_isSnapshotMachine());
1112 Assert(!i_isSessionMachine());
1113 Assert(!!mData);
1114
1115 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
1116 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
1117
1118 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1119
1120 if (!mData->mSession.mMachine.isNull())
1121 {
1122 /* Theoretically, this can only happen if the VirtualBox server has been
1123 * terminated while there were clients running that owned open direct
1124 * sessions. Since in this case we are definitely called by
1125 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
1126 * won't happen on the client watcher thread (because it has a
1127 * VirtualBox caller for the duration of the
1128 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
1129 * cannot happen until the VirtualBox caller is released). This is
1130 * important, because SessionMachine::uninit() cannot correctly operate
1131 * after we return from this method (it expects the Machine instance is
1132 * still valid). We'll call it ourselves below.
1133 */
1134 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
1135 (SessionMachine*)mData->mSession.mMachine));
1136
1137 if (Global::IsOnlineOrTransient(mData->mMachineState))
1138 {
1139 Log1WarningThisFunc(("Setting state to Aborted!\n"));
1140 /* set machine state using SessionMachine reimplementation */
1141 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
1142 }
1143
1144 /*
1145 * Uninitialize SessionMachine using public uninit() to indicate
1146 * an unexpected uninitialization.
1147 */
1148 mData->mSession.mMachine->uninit();
1149 /* SessionMachine::uninit() must set mSession.mMachine to null */
1150 Assert(mData->mSession.mMachine.isNull());
1151 }
1152
1153 // uninit media from this machine's media registry, if they're still there
1154 Guid uuidMachine(i_getId());
1155
1156 /* the lock is no more necessary (SessionMachine is uninitialized) */
1157 alock.release();
1158
1159 /* XXX This will fail with
1160 * "cannot be closed because it is still attached to 1 virtual machines"
1161 * because at this point we did not call uninitDataAndChildObjects() yet
1162 * and therefore also removeBackReference() for all these mediums was not called! */
1163
1164 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
1165 mParent->i_unregisterMachineMedia(uuidMachine);
1166
1167 // has machine been modified?
1168 if (mData->flModifications)
1169 {
1170 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
1171 i_rollback(false /* aNotify */);
1172 }
1173
1174 if (mData->mAccessible)
1175 uninitDataAndChildObjects();
1176
1177#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1178 if (mData->mpKeyStore != NULL)
1179 delete mData->mpKeyStore;
1180#endif
1181
1182 /* free the essential data structure last */
1183 mData.free();
1184
1185 LogFlowThisFuncLeave();
1186}
1187
1188// Wrapped IMachine properties
1189/////////////////////////////////////////////////////////////////////////////
1190HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
1191{
1192 /* mParent is constant during life time, no need to lock */
1193 ComObjPtr<VirtualBox> pVirtualBox(mParent);
1194 aParent = pVirtualBox;
1195
1196 return S_OK;
1197}
1198
1199
1200HRESULT Machine::getAccessible(BOOL *aAccessible)
1201{
1202 /* In some cases (medium registry related), it is necessary to be able to
1203 * go through the list of all machines. Happens when an inaccessible VM
1204 * has a sensible medium registry. */
1205 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
1206 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1207
1208 HRESULT rc = S_OK;
1209
1210 if (!mData->mAccessible)
1211 {
1212 /* try to initialize the VM once more if not accessible */
1213
1214 AutoReinitSpan autoReinitSpan(this);
1215 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
1216
1217#ifdef DEBUG
1218 LogFlowThisFunc(("Dumping media backreferences\n"));
1219 mParent->i_dumpAllBackRefs();
1220#endif
1221
1222 if (mData->pMachineConfigFile)
1223 {
1224 // reset the XML file to force loadSettings() (called from i_registeredInit())
1225 // to parse it again; the file might have changed
1226 delete mData->pMachineConfigFile;
1227 mData->pMachineConfigFile = NULL;
1228 }
1229
1230 rc = i_registeredInit();
1231
1232 if (SUCCEEDED(rc) && mData->mAccessible)
1233 {
1234 autoReinitSpan.setSucceeded();
1235
1236 /* make sure interesting parties will notice the accessibility
1237 * state change */
1238 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
1239 mParent->i_onMachineDataChanged(mData->mUuid);
1240 }
1241 }
1242
1243 if (SUCCEEDED(rc))
1244 *aAccessible = mData->mAccessible;
1245
1246 LogFlowThisFuncLeave();
1247
1248 return rc;
1249}
1250
1251HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
1252{
1253 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1254
1255 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
1256 {
1257 /* return shortly */
1258 aAccessError = NULL;
1259 return S_OK;
1260 }
1261
1262 HRESULT rc = S_OK;
1263
1264 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1265 rc = errorInfo.createObject();
1266 if (SUCCEEDED(rc))
1267 {
1268 errorInfo->init(mData->mAccessError.getResultCode(),
1269 mData->mAccessError.getInterfaceID().ref(),
1270 Utf8Str(mData->mAccessError.getComponent()).c_str(),
1271 Utf8Str(mData->mAccessError.getText()));
1272 aAccessError = errorInfo;
1273 }
1274
1275 return rc;
1276}
1277
1278HRESULT Machine::getName(com::Utf8Str &aName)
1279{
1280 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1281
1282 aName = mUserData->s.strName;
1283
1284 return S_OK;
1285}
1286
1287HRESULT Machine::setName(const com::Utf8Str &aName)
1288{
1289 // prohibit setting a UUID only as the machine name, or else it can
1290 // never be found by findMachine()
1291 Guid test(aName);
1292
1293 if (test.isValid())
1294 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1295
1296 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1297
1298 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1299 if (FAILED(rc)) return rc;
1300
1301 i_setModified(IsModified_MachineData);
1302 mUserData.backup();
1303 mUserData->s.strName = aName;
1304
1305 return S_OK;
1306}
1307
1308HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1309{
1310 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1311
1312 aDescription = mUserData->s.strDescription;
1313
1314 return S_OK;
1315}
1316
1317HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1318{
1319 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1320
1321 // this can be done in principle in any state as it doesn't affect the VM
1322 // significantly, but play safe by not messing around while complex
1323 // activities are going on
1324 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1325 if (FAILED(rc)) return rc;
1326
1327 i_setModified(IsModified_MachineData);
1328 mUserData.backup();
1329 mUserData->s.strDescription = aDescription;
1330
1331 return S_OK;
1332}
1333
1334HRESULT Machine::getId(com::Guid &aId)
1335{
1336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1337
1338 aId = mData->mUuid;
1339
1340 return S_OK;
1341}
1342
1343HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1344{
1345 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1346 aGroups.resize(mUserData->s.llGroups.size());
1347 size_t i = 0;
1348 for (StringsList::const_iterator
1349 it = mUserData->s.llGroups.begin();
1350 it != mUserData->s.llGroups.end();
1351 ++it, ++i)
1352 aGroups[i] = (*it);
1353
1354 return S_OK;
1355}
1356
1357HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1358{
1359 StringsList llGroups;
1360 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1361 if (FAILED(rc))
1362 return rc;
1363
1364 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1365
1366 rc = i_checkStateDependency(MutableOrSavedStateDep);
1367 if (FAILED(rc)) return rc;
1368
1369 i_setModified(IsModified_MachineData);
1370 mUserData.backup();
1371 mUserData->s.llGroups = llGroups;
1372
1373 mParent->i_onMachineGroupsChanged(mData->mUuid);
1374 return S_OK;
1375}
1376
1377HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1378{
1379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1380
1381 aOSTypeId = mUserData->s.strOsType;
1382
1383 return S_OK;
1384}
1385
1386HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1387{
1388 /* look up the object by Id to check it is valid */
1389 ComObjPtr<GuestOSType> pGuestOSType;
1390 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1391
1392 /* when setting, always use the "etalon" value for consistency -- lookup
1393 * by ID is case-insensitive and the input value may have different case */
1394 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1395
1396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1397
1398 HRESULT rc = i_checkStateDependency(MutableStateDep);
1399 if (FAILED(rc)) return rc;
1400
1401 i_setModified(IsModified_MachineData);
1402 mUserData.backup();
1403 mUserData->s.strOsType = osTypeId;
1404
1405 return S_OK;
1406}
1407
1408HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1409{
1410 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1411
1412 *aFirmwareType = mHWData->mFirmwareType;
1413
1414 return S_OK;
1415}
1416
1417HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1418{
1419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1420
1421 HRESULT rc = i_checkStateDependency(MutableStateDep);
1422 if (FAILED(rc)) return rc;
1423
1424 i_setModified(IsModified_MachineData);
1425 mHWData.backup();
1426 mHWData->mFirmwareType = aFirmwareType;
1427 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1428 alock.release();
1429
1430 mNvramStore->i_updateNonVolatileStorageFile(strNVRAM);
1431
1432 return S_OK;
1433}
1434
1435HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1436{
1437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1438
1439 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1440
1441 return S_OK;
1442}
1443
1444HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1445{
1446 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1447
1448 HRESULT rc = i_checkStateDependency(MutableStateDep);
1449 if (FAILED(rc)) return rc;
1450
1451 i_setModified(IsModified_MachineData);
1452 mHWData.backup();
1453 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1454
1455 return S_OK;
1456}
1457
1458HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1459{
1460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1461
1462 *aPointingHIDType = mHWData->mPointingHIDType;
1463
1464 return S_OK;
1465}
1466
1467HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1468{
1469 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1470
1471 HRESULT rc = i_checkStateDependency(MutableStateDep);
1472 if (FAILED(rc)) return rc;
1473
1474 i_setModified(IsModified_MachineData);
1475 mHWData.backup();
1476 mHWData->mPointingHIDType = aPointingHIDType;
1477
1478 return S_OK;
1479}
1480
1481HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1482{
1483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1484
1485 *aChipsetType = mHWData->mChipsetType;
1486
1487 return S_OK;
1488}
1489
1490HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1491{
1492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1493
1494 HRESULT rc = i_checkStateDependency(MutableStateDep);
1495 if (FAILED(rc)) return rc;
1496
1497 if (aChipsetType != mHWData->mChipsetType)
1498 {
1499 i_setModified(IsModified_MachineData);
1500 mHWData.backup();
1501 mHWData->mChipsetType = aChipsetType;
1502
1503 // Resize network adapter array, to be finalized on commit/rollback.
1504 // We must not throw away entries yet, otherwise settings are lost
1505 // without a way to roll back.
1506 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1507 size_t oldCount = mNetworkAdapters.size();
1508 if (newCount > oldCount)
1509 {
1510 mNetworkAdapters.resize(newCount);
1511 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1512 {
1513 unconst(mNetworkAdapters[slot]).createObject();
1514 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1515 }
1516 }
1517 }
1518
1519 return S_OK;
1520}
1521
1522HRESULT Machine::getIommuType(IommuType_T *aIommuType)
1523{
1524 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1525
1526 *aIommuType = mHWData->mIommuType;
1527
1528 return S_OK;
1529}
1530
1531HRESULT Machine::setIommuType(IommuType_T aIommuType)
1532{
1533 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1534
1535 HRESULT rc = i_checkStateDependency(MutableStateDep);
1536 if (FAILED(rc)) return rc;
1537
1538 if (aIommuType != mHWData->mIommuType)
1539 {
1540 if (aIommuType == IommuType_Intel)
1541 {
1542#ifndef VBOX_WITH_IOMMU_INTEL
1543 LogRelFunc(("Setting Intel IOMMU when Intel IOMMU support not available!\n"));
1544 return E_UNEXPECTED;
1545#endif
1546 }
1547
1548 i_setModified(IsModified_MachineData);
1549 mHWData.backup();
1550 mHWData->mIommuType = aIommuType;
1551 }
1552
1553 return S_OK;
1554}
1555
1556HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1557{
1558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1559
1560 aParavirtDebug = mHWData->mParavirtDebug;
1561 return S_OK;
1562}
1563
1564HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1565{
1566 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1567
1568 HRESULT rc = i_checkStateDependency(MutableStateDep);
1569 if (FAILED(rc)) return rc;
1570
1571 /** @todo Parse/validate options? */
1572 if (aParavirtDebug != mHWData->mParavirtDebug)
1573 {
1574 i_setModified(IsModified_MachineData);
1575 mHWData.backup();
1576 mHWData->mParavirtDebug = aParavirtDebug;
1577 }
1578
1579 return S_OK;
1580}
1581
1582HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1583{
1584 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1585
1586 *aParavirtProvider = mHWData->mParavirtProvider;
1587
1588 return S_OK;
1589}
1590
1591HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1592{
1593 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1594
1595 HRESULT rc = i_checkStateDependency(MutableStateDep);
1596 if (FAILED(rc)) return rc;
1597
1598 if (aParavirtProvider != mHWData->mParavirtProvider)
1599 {
1600 i_setModified(IsModified_MachineData);
1601 mHWData.backup();
1602 mHWData->mParavirtProvider = aParavirtProvider;
1603 }
1604
1605 return S_OK;
1606}
1607
1608HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1609{
1610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1611
1612 *aParavirtProvider = mHWData->mParavirtProvider;
1613 switch (mHWData->mParavirtProvider)
1614 {
1615 case ParavirtProvider_None:
1616 case ParavirtProvider_HyperV:
1617 case ParavirtProvider_KVM:
1618 case ParavirtProvider_Minimal:
1619 break;
1620
1621 /* Resolve dynamic provider types to the effective types. */
1622 default:
1623 {
1624 ComObjPtr<GuestOSType> pGuestOSType;
1625 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1626 pGuestOSType);
1627 if (FAILED(hrc2) || pGuestOSType.isNull())
1628 {
1629 *aParavirtProvider = ParavirtProvider_None;
1630 break;
1631 }
1632
1633 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1634 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1635
1636 switch (mHWData->mParavirtProvider)
1637 {
1638 case ParavirtProvider_Legacy:
1639 {
1640 if (fOsXGuest)
1641 *aParavirtProvider = ParavirtProvider_Minimal;
1642 else
1643 *aParavirtProvider = ParavirtProvider_None;
1644 break;
1645 }
1646
1647 case ParavirtProvider_Default:
1648 {
1649 if (fOsXGuest)
1650 *aParavirtProvider = ParavirtProvider_Minimal;
1651 else if ( mUserData->s.strOsType == "Windows11_64"
1652 || mUserData->s.strOsType == "Windows10"
1653 || mUserData->s.strOsType == "Windows10_64"
1654 || mUserData->s.strOsType == "Windows81"
1655 || mUserData->s.strOsType == "Windows81_64"
1656 || mUserData->s.strOsType == "Windows8"
1657 || mUserData->s.strOsType == "Windows8_64"
1658 || mUserData->s.strOsType == "Windows7"
1659 || mUserData->s.strOsType == "Windows7_64"
1660 || mUserData->s.strOsType == "WindowsVista"
1661 || mUserData->s.strOsType == "WindowsVista_64"
1662 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1663 || mUserData->s.strOsType.startsWith("Windows201"))
1664 && mUserData->s.strOsType.endsWith("_64"))
1665 || mUserData->s.strOsType == "Windows2012"
1666 || mUserData->s.strOsType == "Windows2012_64"
1667 || mUserData->s.strOsType == "Windows2008"
1668 || mUserData->s.strOsType == "Windows2008_64")
1669 {
1670 *aParavirtProvider = ParavirtProvider_HyperV;
1671 }
1672 else if (guestTypeFamilyId == "Linux" &&
1673 mUserData->s.strOsType != "Linux22" && // Linux22 and Linux24{_64} excluded as they're too old
1674 mUserData->s.strOsType != "Linux24" && // to have any KVM paravirtualization support.
1675 mUserData->s.strOsType != "Linux24_64")
1676 {
1677 *aParavirtProvider = ParavirtProvider_KVM;
1678 }
1679 else
1680 *aParavirtProvider = ParavirtProvider_None;
1681 break;
1682 }
1683
1684 default: AssertFailedBreak(); /* Shut up MSC. */
1685 }
1686 break;
1687 }
1688 }
1689
1690 Assert( *aParavirtProvider == ParavirtProvider_None
1691 || *aParavirtProvider == ParavirtProvider_Minimal
1692 || *aParavirtProvider == ParavirtProvider_HyperV
1693 || *aParavirtProvider == ParavirtProvider_KVM);
1694 return S_OK;
1695}
1696
1697HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1698{
1699 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1700
1701 aHardwareVersion = mHWData->mHWVersion;
1702
1703 return S_OK;
1704}
1705
1706HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1707{
1708 /* check known version */
1709 Utf8Str hwVersion = aHardwareVersion;
1710 if ( hwVersion.compare("1") != 0
1711 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1712 return setError(E_INVALIDARG,
1713 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1714
1715 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1716
1717 HRESULT rc = i_checkStateDependency(MutableStateDep);
1718 if (FAILED(rc)) return rc;
1719
1720 i_setModified(IsModified_MachineData);
1721 mHWData.backup();
1722 mHWData->mHWVersion = aHardwareVersion;
1723
1724 return S_OK;
1725}
1726
1727HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1728{
1729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1730
1731 if (!mHWData->mHardwareUUID.isZero())
1732 aHardwareUUID = mHWData->mHardwareUUID;
1733 else
1734 aHardwareUUID = mData->mUuid;
1735
1736 return S_OK;
1737}
1738
1739HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1740{
1741 if (!aHardwareUUID.isValid())
1742 return E_INVALIDARG;
1743
1744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1745
1746 HRESULT rc = i_checkStateDependency(MutableStateDep);
1747 if (FAILED(rc)) return rc;
1748
1749 i_setModified(IsModified_MachineData);
1750 mHWData.backup();
1751 if (aHardwareUUID == mData->mUuid)
1752 mHWData->mHardwareUUID.clear();
1753 else
1754 mHWData->mHardwareUUID = aHardwareUUID;
1755
1756 return S_OK;
1757}
1758
1759HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1760{
1761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1762
1763 *aMemorySize = mHWData->mMemorySize;
1764
1765 return S_OK;
1766}
1767
1768HRESULT Machine::setMemorySize(ULONG aMemorySize)
1769{
1770 /* check RAM limits */
1771 if ( aMemorySize < MM_RAM_MIN_IN_MB
1772 || aMemorySize > MM_RAM_MAX_IN_MB
1773 )
1774 return setError(E_INVALIDARG,
1775 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1776 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1777
1778 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1779
1780 HRESULT rc = i_checkStateDependency(MutableStateDep);
1781 if (FAILED(rc)) return rc;
1782
1783 i_setModified(IsModified_MachineData);
1784 mHWData.backup();
1785 mHWData->mMemorySize = aMemorySize;
1786
1787 return S_OK;
1788}
1789
1790HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1791{
1792 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1793
1794 *aCPUCount = mHWData->mCPUCount;
1795
1796 return S_OK;
1797}
1798
1799HRESULT Machine::setCPUCount(ULONG aCPUCount)
1800{
1801 /* check CPU limits */
1802 if ( aCPUCount < SchemaDefs::MinCPUCount
1803 || aCPUCount > SchemaDefs::MaxCPUCount
1804 )
1805 return setError(E_INVALIDARG,
1806 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1807 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1808
1809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1810
1811 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1812 if (mHWData->mCPUHotPlugEnabled)
1813 {
1814 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1815 {
1816 if (mHWData->mCPUAttached[idx])
1817 return setError(E_INVALIDARG,
1818 tr("There is still a CPU attached to socket %lu."
1819 "Detach the CPU before removing the socket"),
1820 aCPUCount, idx+1);
1821 }
1822 }
1823
1824 HRESULT rc = i_checkStateDependency(MutableStateDep);
1825 if (FAILED(rc)) return rc;
1826
1827 i_setModified(IsModified_MachineData);
1828 mHWData.backup();
1829 mHWData->mCPUCount = aCPUCount;
1830
1831 return S_OK;
1832}
1833
1834HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1835{
1836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1837
1838 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1839
1840 return S_OK;
1841}
1842
1843HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1844{
1845 HRESULT rc = S_OK;
1846
1847 /* check throttle limits */
1848 if ( aCPUExecutionCap < 1
1849 || aCPUExecutionCap > 100
1850 )
1851 return setError(E_INVALIDARG,
1852 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1853 aCPUExecutionCap, 1, 100);
1854
1855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1856
1857 rc = i_checkStateDependency(MutableOrRunningStateDep);
1858 if (FAILED(rc)) return rc;
1859
1860 alock.release();
1861 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1862 alock.acquire();
1863 if (FAILED(rc)) return rc;
1864
1865 i_setModified(IsModified_MachineData);
1866 mHWData.backup();
1867 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1868
1869 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1870 if (Global::IsOnline(mData->mMachineState))
1871 i_saveSettings(NULL, alock);
1872
1873 return S_OK;
1874}
1875
1876HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1877{
1878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1879
1880 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1881
1882 return S_OK;
1883}
1884
1885HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1886{
1887 HRESULT rc = S_OK;
1888
1889 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1890
1891 rc = i_checkStateDependency(MutableStateDep);
1892 if (FAILED(rc)) return rc;
1893
1894 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1895 {
1896 if (aCPUHotPlugEnabled)
1897 {
1898 i_setModified(IsModified_MachineData);
1899 mHWData.backup();
1900
1901 /* Add the amount of CPUs currently attached */
1902 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1903 mHWData->mCPUAttached[i] = true;
1904 }
1905 else
1906 {
1907 /*
1908 * We can disable hotplug only if the amount of maximum CPUs is equal
1909 * to the amount of attached CPUs
1910 */
1911 unsigned cCpusAttached = 0;
1912 unsigned iHighestId = 0;
1913
1914 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1915 {
1916 if (mHWData->mCPUAttached[i])
1917 {
1918 cCpusAttached++;
1919 iHighestId = i;
1920 }
1921 }
1922
1923 if ( (cCpusAttached != mHWData->mCPUCount)
1924 || (iHighestId >= mHWData->mCPUCount))
1925 return setError(E_INVALIDARG,
1926 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1927
1928 i_setModified(IsModified_MachineData);
1929 mHWData.backup();
1930 }
1931 }
1932
1933 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1934
1935 return rc;
1936}
1937
1938HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1939{
1940 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1941
1942 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1943
1944 return S_OK;
1945}
1946
1947HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1948{
1949 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1950
1951 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1952 if (SUCCEEDED(hrc))
1953 {
1954 i_setModified(IsModified_MachineData);
1955 mHWData.backup();
1956 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1957 }
1958 return hrc;
1959}
1960
1961HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1962{
1963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1964 aCPUProfile = mHWData->mCpuProfile;
1965 return S_OK;
1966}
1967
1968HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1969{
1970 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1971 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1972 if (SUCCEEDED(hrc))
1973 {
1974 i_setModified(IsModified_MachineData);
1975 mHWData.backup();
1976 /* Empty equals 'host'. */
1977 if (aCPUProfile.isNotEmpty())
1978 mHWData->mCpuProfile = aCPUProfile;
1979 else
1980 mHWData->mCpuProfile = "host";
1981 }
1982 return hrc;
1983}
1984
1985HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1986{
1987#ifdef VBOX_WITH_USB_CARDREADER
1988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1989
1990 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1991
1992 return S_OK;
1993#else
1994 NOREF(aEmulatedUSBCardReaderEnabled);
1995 return E_NOTIMPL;
1996#endif
1997}
1998
1999HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
2000{
2001#ifdef VBOX_WITH_USB_CARDREADER
2002 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2003
2004 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2005 if (FAILED(rc)) return rc;
2006
2007 i_setModified(IsModified_MachineData);
2008 mHWData.backup();
2009 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
2010
2011 return S_OK;
2012#else
2013 NOREF(aEmulatedUSBCardReaderEnabled);
2014 return E_NOTIMPL;
2015#endif
2016}
2017
2018HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
2019{
2020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2021
2022 *aHPETEnabled = mHWData->mHPETEnabled;
2023
2024 return S_OK;
2025}
2026
2027HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
2028{
2029 HRESULT rc = S_OK;
2030
2031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2032
2033 rc = i_checkStateDependency(MutableStateDep);
2034 if (FAILED(rc)) return rc;
2035
2036 i_setModified(IsModified_MachineData);
2037 mHWData.backup();
2038
2039 mHWData->mHPETEnabled = aHPETEnabled;
2040
2041 return rc;
2042}
2043
2044/** @todo this method should not be public */
2045HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2046{
2047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2048
2049 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2050
2051 return S_OK;
2052}
2053
2054/**
2055 * Set the memory balloon size.
2056 *
2057 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2058 * we have to make sure that we never call IGuest from here.
2059 */
2060HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2061{
2062 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2063#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2064 /* check limits */
2065 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2066 return setError(E_INVALIDARG,
2067 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2068 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2069
2070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2071
2072 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
2073 if (FAILED(rc)) return rc;
2074
2075 i_setModified(IsModified_MachineData);
2076 mHWData.backup();
2077 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2078
2079 return S_OK;
2080#else
2081 NOREF(aMemoryBalloonSize);
2082 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2083#endif
2084}
2085
2086HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2087{
2088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2089
2090 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2091 return S_OK;
2092}
2093
2094HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2095{
2096#ifdef VBOX_WITH_PAGE_SHARING
2097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2098
2099 HRESULT rc = i_checkStateDependency(MutableStateDep);
2100 if (FAILED(rc)) return rc;
2101
2102 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2103 i_setModified(IsModified_MachineData);
2104 mHWData.backup();
2105 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2106 return S_OK;
2107#else
2108 NOREF(aPageFusionEnabled);
2109 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2110#endif
2111}
2112
2113HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2114{
2115 /* mBIOSSettings is constant during life time, no need to lock */
2116 aBIOSSettings = mBIOSSettings;
2117
2118 return S_OK;
2119}
2120
2121HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
2122{
2123 /* mTrustedPlatformModule is constant during life time, no need to lock */
2124 aTrustedPlatformModule = mTrustedPlatformModule;
2125
2126 return S_OK;
2127}
2128
2129HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
2130{
2131 /* mNvramStore is constant during life time, no need to lock */
2132 aNvramStore = mNvramStore;
2133
2134 return S_OK;
2135}
2136
2137HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
2138{
2139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2140
2141 aRecordingSettings = mRecordingSettings;
2142
2143 return S_OK;
2144}
2145
2146HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
2147{
2148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2149
2150 aGraphicsAdapter = mGraphicsAdapter;
2151
2152 return S_OK;
2153}
2154
2155HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2156{
2157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2158
2159 switch (aProperty)
2160 {
2161 case CPUPropertyType_PAE:
2162 *aValue = mHWData->mPAEEnabled;
2163 break;
2164
2165 case CPUPropertyType_LongMode:
2166 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2167 *aValue = TRUE;
2168 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2169 *aValue = FALSE;
2170#if HC_ARCH_BITS == 64
2171 else
2172 *aValue = TRUE;
2173#else
2174 else
2175 {
2176 *aValue = FALSE;
2177
2178 ComObjPtr<GuestOSType> pGuestOSType;
2179 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2180 pGuestOSType);
2181 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
2182 {
2183 if (pGuestOSType->i_is64Bit())
2184 {
2185 ComObjPtr<Host> pHost = mParent->i_host();
2186 alock.release();
2187
2188 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2189 if (FAILED(hrc2))
2190 *aValue = FALSE;
2191 }
2192 }
2193 }
2194#endif
2195 break;
2196
2197 case CPUPropertyType_TripleFaultReset:
2198 *aValue = mHWData->mTripleFaultReset;
2199 break;
2200
2201 case CPUPropertyType_APIC:
2202 *aValue = mHWData->mAPIC;
2203 break;
2204
2205 case CPUPropertyType_X2APIC:
2206 *aValue = mHWData->mX2APIC;
2207 break;
2208
2209 case CPUPropertyType_IBPBOnVMExit:
2210 *aValue = mHWData->mIBPBOnVMExit;
2211 break;
2212
2213 case CPUPropertyType_IBPBOnVMEntry:
2214 *aValue = mHWData->mIBPBOnVMEntry;
2215 break;
2216
2217 case CPUPropertyType_SpecCtrl:
2218 *aValue = mHWData->mSpecCtrl;
2219 break;
2220
2221 case CPUPropertyType_SpecCtrlByHost:
2222 *aValue = mHWData->mSpecCtrlByHost;
2223 break;
2224
2225 case CPUPropertyType_HWVirt:
2226 *aValue = mHWData->mNestedHWVirt;
2227 break;
2228
2229 case CPUPropertyType_L1DFlushOnEMTScheduling:
2230 *aValue = mHWData->mL1DFlushOnSched;
2231 break;
2232
2233 case CPUPropertyType_L1DFlushOnVMEntry:
2234 *aValue = mHWData->mL1DFlushOnVMEntry;
2235 break;
2236
2237 case CPUPropertyType_MDSClearOnEMTScheduling:
2238 *aValue = mHWData->mMDSClearOnSched;
2239 break;
2240
2241 case CPUPropertyType_MDSClearOnVMEntry:
2242 *aValue = mHWData->mMDSClearOnVMEntry;
2243 break;
2244
2245 default:
2246 return E_INVALIDARG;
2247 }
2248 return S_OK;
2249}
2250
2251HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2252{
2253 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2254
2255 HRESULT rc = i_checkStateDependency(MutableStateDep);
2256 if (FAILED(rc)) return rc;
2257
2258 switch (aProperty)
2259 {
2260 case CPUPropertyType_PAE:
2261 i_setModified(IsModified_MachineData);
2262 mHWData.backup();
2263 mHWData->mPAEEnabled = !!aValue;
2264 break;
2265
2266 case CPUPropertyType_LongMode:
2267 i_setModified(IsModified_MachineData);
2268 mHWData.backup();
2269 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2270 break;
2271
2272 case CPUPropertyType_TripleFaultReset:
2273 i_setModified(IsModified_MachineData);
2274 mHWData.backup();
2275 mHWData->mTripleFaultReset = !!aValue;
2276 break;
2277
2278 case CPUPropertyType_APIC:
2279 if (mHWData->mX2APIC)
2280 aValue = TRUE;
2281 i_setModified(IsModified_MachineData);
2282 mHWData.backup();
2283 mHWData->mAPIC = !!aValue;
2284 break;
2285
2286 case CPUPropertyType_X2APIC:
2287 i_setModified(IsModified_MachineData);
2288 mHWData.backup();
2289 mHWData->mX2APIC = !!aValue;
2290 if (aValue)
2291 mHWData->mAPIC = !!aValue;
2292 break;
2293
2294 case CPUPropertyType_IBPBOnVMExit:
2295 i_setModified(IsModified_MachineData);
2296 mHWData.backup();
2297 mHWData->mIBPBOnVMExit = !!aValue;
2298 break;
2299
2300 case CPUPropertyType_IBPBOnVMEntry:
2301 i_setModified(IsModified_MachineData);
2302 mHWData.backup();
2303 mHWData->mIBPBOnVMEntry = !!aValue;
2304 break;
2305
2306 case CPUPropertyType_SpecCtrl:
2307 i_setModified(IsModified_MachineData);
2308 mHWData.backup();
2309 mHWData->mSpecCtrl = !!aValue;
2310 break;
2311
2312 case CPUPropertyType_SpecCtrlByHost:
2313 i_setModified(IsModified_MachineData);
2314 mHWData.backup();
2315 mHWData->mSpecCtrlByHost = !!aValue;
2316 break;
2317
2318 case CPUPropertyType_HWVirt:
2319 i_setModified(IsModified_MachineData);
2320 mHWData.backup();
2321 mHWData->mNestedHWVirt = !!aValue;
2322 break;
2323
2324 case CPUPropertyType_L1DFlushOnEMTScheduling:
2325 i_setModified(IsModified_MachineData);
2326 mHWData.backup();
2327 mHWData->mL1DFlushOnSched = !!aValue;
2328 break;
2329
2330 case CPUPropertyType_L1DFlushOnVMEntry:
2331 i_setModified(IsModified_MachineData);
2332 mHWData.backup();
2333 mHWData->mL1DFlushOnVMEntry = !!aValue;
2334 break;
2335
2336 case CPUPropertyType_MDSClearOnEMTScheduling:
2337 i_setModified(IsModified_MachineData);
2338 mHWData.backup();
2339 mHWData->mMDSClearOnSched = !!aValue;
2340 break;
2341
2342 case CPUPropertyType_MDSClearOnVMEntry:
2343 i_setModified(IsModified_MachineData);
2344 mHWData.backup();
2345 mHWData->mMDSClearOnVMEntry = !!aValue;
2346 break;
2347
2348 default:
2349 return E_INVALIDARG;
2350 }
2351 return S_OK;
2352}
2353
2354HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2355 ULONG *aValEcx, ULONG *aValEdx)
2356{
2357 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2358 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2359 {
2360 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2361 it != mHWData->mCpuIdLeafList.end();
2362 ++it)
2363 {
2364 if (aOrdinal == 0)
2365 {
2366 const settings::CpuIdLeaf &rLeaf= *it;
2367 *aIdx = rLeaf.idx;
2368 *aSubIdx = rLeaf.idxSub;
2369 *aValEax = rLeaf.uEax;
2370 *aValEbx = rLeaf.uEbx;
2371 *aValEcx = rLeaf.uEcx;
2372 *aValEdx = rLeaf.uEdx;
2373 return S_OK;
2374 }
2375 aOrdinal--;
2376 }
2377 }
2378 return E_INVALIDARG;
2379}
2380
2381HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2382{
2383 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2384
2385 /*
2386 * Search the list.
2387 */
2388 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2389 {
2390 const settings::CpuIdLeaf &rLeaf= *it;
2391 if ( rLeaf.idx == aIdx
2392 && ( aSubIdx == UINT32_MAX
2393 || rLeaf.idxSub == aSubIdx) )
2394 {
2395 *aValEax = rLeaf.uEax;
2396 *aValEbx = rLeaf.uEbx;
2397 *aValEcx = rLeaf.uEcx;
2398 *aValEdx = rLeaf.uEdx;
2399 return S_OK;
2400 }
2401 }
2402
2403 return E_INVALIDARG;
2404}
2405
2406
2407HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2408{
2409 /*
2410 * Validate input before taking locks and checking state.
2411 */
2412 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2413 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2414 if ( aIdx >= UINT32_C(0x20)
2415 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2416 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2417 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2418
2419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2420 HRESULT rc = i_checkStateDependency(MutableStateDep);
2421 if (FAILED(rc)) return rc;
2422
2423 /*
2424 * Impose a maximum number of leaves.
2425 */
2426 if (mHWData->mCpuIdLeafList.size() > 256)
2427 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2428
2429 /*
2430 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2431 */
2432 i_setModified(IsModified_MachineData);
2433 mHWData.backup();
2434
2435 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2436 {
2437 settings::CpuIdLeaf &rLeaf= *it;
2438 if ( rLeaf.idx == aIdx
2439 && ( aSubIdx == UINT32_MAX
2440 || rLeaf.idxSub == aSubIdx) )
2441 it = mHWData->mCpuIdLeafList.erase(it);
2442 else
2443 ++it;
2444 }
2445
2446 settings::CpuIdLeaf NewLeaf;
2447 NewLeaf.idx = aIdx;
2448 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2449 NewLeaf.uEax = aValEax;
2450 NewLeaf.uEbx = aValEbx;
2451 NewLeaf.uEcx = aValEcx;
2452 NewLeaf.uEdx = aValEdx;
2453 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2454 return S_OK;
2455}
2456
2457HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2458{
2459 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2460
2461 HRESULT rc = i_checkStateDependency(MutableStateDep);
2462 if (FAILED(rc)) return rc;
2463
2464 /*
2465 * Do the removal.
2466 */
2467 bool fModified = mHWData.isBackedUp();
2468 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2469 {
2470 settings::CpuIdLeaf &rLeaf= *it;
2471 if ( rLeaf.idx == aIdx
2472 && ( aSubIdx == UINT32_MAX
2473 || rLeaf.idxSub == aSubIdx) )
2474 {
2475 if (!fModified)
2476 {
2477 fModified = true;
2478 i_setModified(IsModified_MachineData);
2479 mHWData.backup();
2480 // Start from the beginning, since mHWData.backup() creates
2481 // a new list, causing iterator mixup. This makes sure that
2482 // the settings are not unnecessarily marked as modified,
2483 // at the price of extra list walking.
2484 it = mHWData->mCpuIdLeafList.begin();
2485 }
2486 else
2487 it = mHWData->mCpuIdLeafList.erase(it);
2488 }
2489 else
2490 ++it;
2491 }
2492
2493 return S_OK;
2494}
2495
2496HRESULT Machine::removeAllCPUIDLeaves()
2497{
2498 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2499
2500 HRESULT rc = i_checkStateDependency(MutableStateDep);
2501 if (FAILED(rc)) return rc;
2502
2503 if (mHWData->mCpuIdLeafList.size() > 0)
2504 {
2505 i_setModified(IsModified_MachineData);
2506 mHWData.backup();
2507
2508 mHWData->mCpuIdLeafList.clear();
2509 }
2510
2511 return S_OK;
2512}
2513HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2514{
2515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2516
2517 switch(aProperty)
2518 {
2519 case HWVirtExPropertyType_Enabled:
2520 *aValue = mHWData->mHWVirtExEnabled;
2521 break;
2522
2523 case HWVirtExPropertyType_VPID:
2524 *aValue = mHWData->mHWVirtExVPIDEnabled;
2525 break;
2526
2527 case HWVirtExPropertyType_NestedPaging:
2528 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2529 break;
2530
2531 case HWVirtExPropertyType_UnrestrictedExecution:
2532 *aValue = mHWData->mHWVirtExUXEnabled;
2533 break;
2534
2535 case HWVirtExPropertyType_LargePages:
2536 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2537 break;
2538
2539 case HWVirtExPropertyType_Force:
2540 *aValue = mHWData->mHWVirtExForceEnabled;
2541 break;
2542
2543 case HWVirtExPropertyType_UseNativeApi:
2544 *aValue = mHWData->mHWVirtExUseNativeApi;
2545 break;
2546
2547 case HWVirtExPropertyType_VirtVmsaveVmload:
2548 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2549 break;
2550
2551 default:
2552 return E_INVALIDARG;
2553 }
2554 return S_OK;
2555}
2556
2557HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2558{
2559 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2560
2561 HRESULT rc = i_checkStateDependency(MutableStateDep);
2562 if (FAILED(rc)) return rc;
2563
2564 switch (aProperty)
2565 {
2566 case HWVirtExPropertyType_Enabled:
2567 i_setModified(IsModified_MachineData);
2568 mHWData.backup();
2569 mHWData->mHWVirtExEnabled = !!aValue;
2570 break;
2571
2572 case HWVirtExPropertyType_VPID:
2573 i_setModified(IsModified_MachineData);
2574 mHWData.backup();
2575 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2576 break;
2577
2578 case HWVirtExPropertyType_NestedPaging:
2579 i_setModified(IsModified_MachineData);
2580 mHWData.backup();
2581 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2582 break;
2583
2584 case HWVirtExPropertyType_UnrestrictedExecution:
2585 i_setModified(IsModified_MachineData);
2586 mHWData.backup();
2587 mHWData->mHWVirtExUXEnabled = !!aValue;
2588 break;
2589
2590 case HWVirtExPropertyType_LargePages:
2591 i_setModified(IsModified_MachineData);
2592 mHWData.backup();
2593 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2594 break;
2595
2596 case HWVirtExPropertyType_Force:
2597 i_setModified(IsModified_MachineData);
2598 mHWData.backup();
2599 mHWData->mHWVirtExForceEnabled = !!aValue;
2600 break;
2601
2602 case HWVirtExPropertyType_UseNativeApi:
2603 i_setModified(IsModified_MachineData);
2604 mHWData.backup();
2605 mHWData->mHWVirtExUseNativeApi = !!aValue;
2606 break;
2607
2608 case HWVirtExPropertyType_VirtVmsaveVmload:
2609 i_setModified(IsModified_MachineData);
2610 mHWData.backup();
2611 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2612 break;
2613
2614 default:
2615 return E_INVALIDARG;
2616 }
2617
2618 return S_OK;
2619}
2620
2621HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2622{
2623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2624
2625 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2626
2627 return S_OK;
2628}
2629
2630HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2631{
2632 /** @todo (r=dmik):
2633 * 1. Allow to change the name of the snapshot folder containing snapshots
2634 * 2. Rename the folder on disk instead of just changing the property
2635 * value (to be smart and not to leave garbage). Note that it cannot be
2636 * done here because the change may be rolled back. Thus, the right
2637 * place is #saveSettings().
2638 */
2639
2640 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2641
2642 HRESULT rc = i_checkStateDependency(MutableStateDep);
2643 if (FAILED(rc)) return rc;
2644
2645 if (!mData->mCurrentSnapshot.isNull())
2646 return setError(E_FAIL,
2647 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2648
2649 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2650
2651 if (strSnapshotFolder.isEmpty())
2652 strSnapshotFolder = "Snapshots";
2653 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2654 if (RT_FAILURE(vrc))
2655 return setErrorBoth(E_FAIL, vrc,
2656 tr("Invalid snapshot folder '%s' (%Rrc)"),
2657 strSnapshotFolder.c_str(), vrc);
2658
2659 i_setModified(IsModified_MachineData);
2660 mUserData.backup();
2661
2662 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2663
2664 return S_OK;
2665}
2666
2667HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2668{
2669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2670
2671 aMediumAttachments.resize(mMediumAttachments->size());
2672 size_t i = 0;
2673 for (MediumAttachmentList::const_iterator
2674 it = mMediumAttachments->begin();
2675 it != mMediumAttachments->end();
2676 ++it, ++i)
2677 aMediumAttachments[i] = *it;
2678
2679 return S_OK;
2680}
2681
2682HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2683{
2684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2685
2686 Assert(!!mVRDEServer);
2687
2688 aVRDEServer = mVRDEServer;
2689
2690 return S_OK;
2691}
2692
2693HRESULT Machine::getAudioSettings(ComPtr<IAudioSettings> &aAudioSettings)
2694{
2695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2696
2697 aAudioSettings = mAudioSettings;
2698
2699 return S_OK;
2700}
2701
2702HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2703{
2704#ifdef VBOX_WITH_VUSB
2705 clearError();
2706 MultiResult rc(S_OK);
2707
2708# ifdef VBOX_WITH_USB
2709 rc = mParent->i_host()->i_checkUSBProxyService();
2710 if (FAILED(rc)) return rc;
2711# endif
2712
2713 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2714
2715 aUSBControllers.resize(mUSBControllers->size());
2716 size_t i = 0;
2717 for (USBControllerList::const_iterator
2718 it = mUSBControllers->begin();
2719 it != mUSBControllers->end();
2720 ++it, ++i)
2721 aUSBControllers[i] = *it;
2722
2723 return S_OK;
2724#else
2725 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2726 * extended error info to indicate that USB is simply not available
2727 * (w/o treating it as a failure), for example, as in OSE */
2728 NOREF(aUSBControllers);
2729 ReturnComNotImplemented();
2730#endif /* VBOX_WITH_VUSB */
2731}
2732
2733HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2734{
2735#ifdef VBOX_WITH_VUSB
2736 clearError();
2737 MultiResult rc(S_OK);
2738
2739# ifdef VBOX_WITH_USB
2740 rc = mParent->i_host()->i_checkUSBProxyService();
2741 if (FAILED(rc)) return rc;
2742# endif
2743
2744 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2745
2746 aUSBDeviceFilters = mUSBDeviceFilters;
2747 return rc;
2748#else
2749 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2750 * extended error info to indicate that USB is simply not available
2751 * (w/o treating it as a failure), for example, as in OSE */
2752 NOREF(aUSBDeviceFilters);
2753 ReturnComNotImplemented();
2754#endif /* VBOX_WITH_VUSB */
2755}
2756
2757HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2758{
2759 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2760
2761 aSettingsFilePath = mData->m_strConfigFileFull;
2762
2763 return S_OK;
2764}
2765
2766HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2767{
2768 RT_NOREF(aSettingsFilePath);
2769 ReturnComNotImplemented();
2770}
2771
2772HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2773{
2774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2775
2776 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2777 if (FAILED(rc)) return rc;
2778
2779 if (!mData->pMachineConfigFile->fileExists())
2780 // this is a new machine, and no config file exists yet:
2781 *aSettingsModified = TRUE;
2782 else
2783 *aSettingsModified = (mData->flModifications != 0);
2784
2785 return S_OK;
2786}
2787
2788HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2789{
2790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2791
2792 *aSessionState = mData->mSession.mState;
2793
2794 return S_OK;
2795}
2796
2797HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2798{
2799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2800
2801 aSessionName = mData->mSession.mName;
2802
2803 return S_OK;
2804}
2805
2806HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2807{
2808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2809
2810 *aSessionPID = mData->mSession.mPID;
2811
2812 return S_OK;
2813}
2814
2815HRESULT Machine::getState(MachineState_T *aState)
2816{
2817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2818
2819 *aState = mData->mMachineState;
2820 Assert(mData->mMachineState != MachineState_Null);
2821
2822 return S_OK;
2823}
2824
2825HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2826{
2827 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2828
2829 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2830
2831 return S_OK;
2832}
2833
2834HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2835{
2836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2837
2838 aStateFilePath = mSSData->strStateFilePath;
2839
2840 return S_OK;
2841}
2842
2843HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2844{
2845 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2846
2847 i_getLogFolder(aLogFolder);
2848
2849 return S_OK;
2850}
2851
2852HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2853{
2854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2855
2856 aCurrentSnapshot = mData->mCurrentSnapshot;
2857
2858 return S_OK;
2859}
2860
2861HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2862{
2863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2864
2865 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2866 ? 0
2867 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2868
2869 return S_OK;
2870}
2871
2872HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2873{
2874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2875
2876 /* Note: for machines with no snapshots, we always return FALSE
2877 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2878 * reasons :) */
2879
2880 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2881 ? FALSE
2882 : mData->mCurrentStateModified;
2883
2884 return S_OK;
2885}
2886
2887HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2888{
2889 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2890
2891 aSharedFolders.resize(mHWData->mSharedFolders.size());
2892 size_t i = 0;
2893 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2894 it = mHWData->mSharedFolders.begin();
2895 it != mHWData->mSharedFolders.end();
2896 ++it, ++i)
2897 aSharedFolders[i] = *it;
2898
2899 return S_OK;
2900}
2901
2902HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2903{
2904 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2905
2906 *aClipboardMode = mHWData->mClipboardMode;
2907
2908 return S_OK;
2909}
2910
2911HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2912{
2913 HRESULT rc = S_OK;
2914
2915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2916
2917 rc = i_checkStateDependency(MutableOrRunningStateDep);
2918 if (FAILED(rc)) return rc;
2919
2920 alock.release();
2921 rc = i_onClipboardModeChange(aClipboardMode);
2922 alock.acquire();
2923 if (FAILED(rc)) return rc;
2924
2925 i_setModified(IsModified_MachineData);
2926 mHWData.backup();
2927 mHWData->mClipboardMode = aClipboardMode;
2928
2929 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2930 if (Global::IsOnline(mData->mMachineState))
2931 i_saveSettings(NULL, alock);
2932
2933 return S_OK;
2934}
2935
2936HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2937{
2938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2939
2940 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2941
2942 return S_OK;
2943}
2944
2945HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2946{
2947 HRESULT rc = S_OK;
2948
2949 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2950
2951 rc = i_checkStateDependency(MutableOrRunningStateDep);
2952 if (FAILED(rc)) return rc;
2953
2954 alock.release();
2955 rc = i_onClipboardFileTransferModeChange(aEnabled);
2956 alock.acquire();
2957 if (FAILED(rc)) return rc;
2958
2959 i_setModified(IsModified_MachineData);
2960 mHWData.backup();
2961 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2962
2963 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2964 if (Global::IsOnline(mData->mMachineState))
2965 i_saveSettings(NULL, alock);
2966
2967 return S_OK;
2968}
2969
2970HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2971{
2972 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2973
2974 *aDnDMode = mHWData->mDnDMode;
2975
2976 return S_OK;
2977}
2978
2979HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2980{
2981 HRESULT rc = S_OK;
2982
2983 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2984
2985 rc = i_checkStateDependency(MutableOrRunningStateDep);
2986 if (FAILED(rc)) return rc;
2987
2988 alock.release();
2989 rc = i_onDnDModeChange(aDnDMode);
2990
2991 alock.acquire();
2992 if (FAILED(rc)) return rc;
2993
2994 i_setModified(IsModified_MachineData);
2995 mHWData.backup();
2996 mHWData->mDnDMode = aDnDMode;
2997
2998 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2999 if (Global::IsOnline(mData->mMachineState))
3000 i_saveSettings(NULL, alock);
3001
3002 return S_OK;
3003}
3004
3005HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
3006{
3007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3008
3009 aStorageControllers.resize(mStorageControllers->size());
3010 size_t i = 0;
3011 for (StorageControllerList::const_iterator
3012 it = mStorageControllers->begin();
3013 it != mStorageControllers->end();
3014 ++it, ++i)
3015 aStorageControllers[i] = *it;
3016
3017 return S_OK;
3018}
3019
3020HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
3021{
3022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3023
3024 *aEnabled = mUserData->s.fTeleporterEnabled;
3025
3026 return S_OK;
3027}
3028
3029HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
3030{
3031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3032
3033 /* Only allow it to be set to true when PoweredOff or Aborted.
3034 (Clearing it is always permitted.) */
3035 if ( aTeleporterEnabled
3036 && mData->mRegistered
3037 && ( !i_isSessionMachine()
3038 || ( mData->mMachineState != MachineState_PoweredOff
3039 && mData->mMachineState != MachineState_Teleported
3040 && mData->mMachineState != MachineState_Aborted
3041 )
3042 )
3043 )
3044 return setError(VBOX_E_INVALID_VM_STATE,
3045 tr("The machine is not powered off (state is %s)"),
3046 Global::stringifyMachineState(mData->mMachineState));
3047
3048 i_setModified(IsModified_MachineData);
3049 mUserData.backup();
3050 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
3051
3052 return S_OK;
3053}
3054
3055HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
3056{
3057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3058
3059 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
3060
3061 return S_OK;
3062}
3063
3064HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
3065{
3066 if (aTeleporterPort >= _64K)
3067 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
3068
3069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3070
3071 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3072 if (FAILED(rc)) return rc;
3073
3074 i_setModified(IsModified_MachineData);
3075 mUserData.backup();
3076 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3077
3078 return S_OK;
3079}
3080
3081HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3082{
3083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3084
3085 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3086
3087 return S_OK;
3088}
3089
3090HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3091{
3092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3093
3094 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3095 if (FAILED(rc)) return rc;
3096
3097 i_setModified(IsModified_MachineData);
3098 mUserData.backup();
3099 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3100
3101 return S_OK;
3102}
3103
3104HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3105{
3106 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3107 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3108
3109 return S_OK;
3110}
3111
3112HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3113{
3114 /*
3115 * Hash the password first.
3116 */
3117 com::Utf8Str aT = aTeleporterPassword;
3118
3119 if (!aT.isEmpty())
3120 {
3121 if (VBoxIsPasswordHashed(&aT))
3122 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3123 VBoxHashPassword(&aT);
3124 }
3125
3126 /*
3127 * Do the update.
3128 */
3129 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3130 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3131 if (SUCCEEDED(hrc))
3132 {
3133 i_setModified(IsModified_MachineData);
3134 mUserData.backup();
3135 mUserData->s.strTeleporterPassword = aT;
3136 }
3137
3138 return hrc;
3139}
3140
3141HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3142{
3143 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3144
3145 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3146
3147 return S_OK;
3148}
3149
3150HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3151{
3152 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3153
3154 /* Only allow it to be set to true when PoweredOff or Aborted.
3155 (Clearing it is always permitted.) */
3156 if ( aRTCUseUTC
3157 && mData->mRegistered
3158 && ( !i_isSessionMachine()
3159 || ( mData->mMachineState != MachineState_PoweredOff
3160 && mData->mMachineState != MachineState_Teleported
3161 && mData->mMachineState != MachineState_Aborted
3162 )
3163 )
3164 )
3165 return setError(VBOX_E_INVALID_VM_STATE,
3166 tr("The machine is not powered off (state is %s)"),
3167 Global::stringifyMachineState(mData->mMachineState));
3168
3169 i_setModified(IsModified_MachineData);
3170 mUserData.backup();
3171 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3172
3173 return S_OK;
3174}
3175
3176HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3177{
3178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3179
3180 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3181
3182 return S_OK;
3183}
3184
3185HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3186{
3187 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3188
3189 HRESULT rc = i_checkStateDependency(MutableStateDep);
3190 if (FAILED(rc)) return rc;
3191
3192 i_setModified(IsModified_MachineData);
3193 mHWData.backup();
3194 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3195
3196 return S_OK;
3197}
3198
3199HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3200{
3201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3202
3203 *aIOCacheSize = mHWData->mIOCacheSize;
3204
3205 return S_OK;
3206}
3207
3208HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3209{
3210 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3211
3212 HRESULT rc = i_checkStateDependency(MutableStateDep);
3213 if (FAILED(rc)) return rc;
3214
3215 i_setModified(IsModified_MachineData);
3216 mHWData.backup();
3217 mHWData->mIOCacheSize = aIOCacheSize;
3218
3219 return S_OK;
3220}
3221
3222HRESULT Machine::getStateKeyId(com::Utf8Str &aKeyId)
3223{
3224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3225
3226#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3227 aKeyId = mSSData->strStateKeyId;
3228#else
3229 aKeyId = com::Utf8Str::Empty;
3230#endif
3231
3232 return S_OK;
3233}
3234
3235HRESULT Machine::getStateKeyStore(com::Utf8Str &aKeyStore)
3236{
3237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3238
3239#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3240 aKeyStore = mSSData->strStateKeyStore;
3241#else
3242 aKeyStore = com::Utf8Str::Empty;
3243#endif
3244
3245 return S_OK;
3246}
3247
3248HRESULT Machine::getLogKeyId(com::Utf8Str &aKeyId)
3249{
3250 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3251
3252#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3253 aKeyId = mData->mstrLogKeyId;
3254#else
3255 aKeyId = com::Utf8Str::Empty;
3256#endif
3257
3258 return S_OK;
3259}
3260
3261HRESULT Machine::getLogKeyStore(com::Utf8Str &aKeyStore)
3262{
3263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3264
3265#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3266 aKeyStore = mData->mstrLogKeyStore;
3267#else
3268 aKeyStore = com::Utf8Str::Empty;
3269#endif
3270
3271 return S_OK;
3272}
3273
3274HRESULT Machine::getGuestDebugControl(ComPtr<IGuestDebugControl> &aGuestDebugControl)
3275{
3276 mGuestDebugControl.queryInterfaceTo(aGuestDebugControl.asOutParam());
3277
3278 return S_OK;
3279}
3280
3281
3282/**
3283 * @note Locks objects!
3284 */
3285HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3286 LockType_T aLockType)
3287{
3288 /* check the session state */
3289 SessionState_T state;
3290 HRESULT rc = aSession->COMGETTER(State)(&state);
3291 if (FAILED(rc)) return rc;
3292
3293 if (state != SessionState_Unlocked)
3294 return setError(VBOX_E_INVALID_OBJECT_STATE,
3295 tr("The given session is busy"));
3296
3297 // get the client's IInternalSessionControl interface
3298 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3299 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
3300 E_INVALIDARG);
3301
3302 // session name (only used in some code paths)
3303 Utf8Str strSessionName;
3304
3305 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3306
3307 if (!mData->mRegistered)
3308 return setError(E_UNEXPECTED,
3309 tr("The machine '%s' is not registered"),
3310 mUserData->s.strName.c_str());
3311
3312 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3313
3314 SessionState_T oldState = mData->mSession.mState;
3315 /* Hack: in case the session is closing and there is a progress object
3316 * which allows waiting for the session to be closed, take the opportunity
3317 * and do a limited wait (max. 1 second). This helps a lot when the system
3318 * is busy and thus session closing can take a little while. */
3319 if ( mData->mSession.mState == SessionState_Unlocking
3320 && mData->mSession.mProgress)
3321 {
3322 alock.release();
3323 mData->mSession.mProgress->WaitForCompletion(1000);
3324 alock.acquire();
3325 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3326 }
3327
3328 // try again now
3329 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3330 // (i.e. session machine exists)
3331 && (aLockType == LockType_Shared) // caller wants a shared link to the
3332 // existing session that holds the write lock:
3333 )
3334 {
3335 // OK, share the session... we are now dealing with three processes:
3336 // 1) VBoxSVC (where this code runs);
3337 // 2) process C: the caller's client process (who wants a shared session);
3338 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3339
3340 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3341 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3342 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3343 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3344 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3345
3346 /*
3347 * Release the lock before calling the client process. It's safe here
3348 * since the only thing to do after we get the lock again is to add
3349 * the remote control to the list (which doesn't directly influence
3350 * anything).
3351 */
3352 alock.release();
3353
3354 // get the console of the session holding the write lock (this is a remote call)
3355 ComPtr<IConsole> pConsoleW;
3356 if (mData->mSession.mLockType == LockType_VM)
3357 {
3358 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3359 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3360 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3361 if (FAILED(rc))
3362 // the failure may occur w/o any error info (from RPC), so provide one
3363 return setError(VBOX_E_VM_ERROR,
3364 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3365 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3366 }
3367
3368 // share the session machine and W's console with the caller's session
3369 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3370 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3371 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3372
3373 if (FAILED(rc))
3374 // the failure may occur w/o any error info (from RPC), so provide one
3375 return setError(VBOX_E_VM_ERROR,
3376 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3377 alock.acquire();
3378
3379 // need to revalidate the state after acquiring the lock again
3380 if (mData->mSession.mState != SessionState_Locked)
3381 {
3382 pSessionControl->Uninitialize();
3383 return setError(VBOX_E_INVALID_SESSION_STATE,
3384 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3385 mUserData->s.strName.c_str());
3386 }
3387
3388 // add the caller's session to the list
3389 mData->mSession.mRemoteControls.push_back(pSessionControl);
3390 }
3391 else if ( mData->mSession.mState == SessionState_Locked
3392 || mData->mSession.mState == SessionState_Unlocking
3393 )
3394 {
3395 // sharing not permitted, or machine still unlocking:
3396 return setError(VBOX_E_INVALID_OBJECT_STATE,
3397 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3398 mUserData->s.strName.c_str());
3399 }
3400 else
3401 {
3402 // machine is not locked: then write-lock the machine (create the session machine)
3403
3404 // must not be busy
3405 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3406
3407 // get the caller's session PID
3408 RTPROCESS pid = NIL_RTPROCESS;
3409 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3410 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3411 Assert(pid != NIL_RTPROCESS);
3412
3413 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3414
3415 if (fLaunchingVMProcess)
3416 {
3417 if (mData->mSession.mPID == NIL_RTPROCESS)
3418 {
3419 // two or more clients racing for a lock, the one which set the
3420 // session state to Spawning will win, the others will get an
3421 // error as we can't decide here if waiting a little would help
3422 // (only for shared locks this would avoid an error)
3423 return setError(VBOX_E_INVALID_OBJECT_STATE,
3424 tr("The machine '%s' already has a lock request pending"),
3425 mUserData->s.strName.c_str());
3426 }
3427
3428 // this machine is awaiting for a spawning session to be opened:
3429 // then the calling process must be the one that got started by
3430 // LaunchVMProcess()
3431
3432 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3433 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3434
3435#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3436 /* Hardened windows builds spawns three processes when a VM is
3437 launched, the 3rd one is the one that will end up here. */
3438 RTPROCESS pidParent;
3439 int vrc = RTProcQueryParent(pid, &pidParent);
3440 if (RT_SUCCESS(vrc))
3441 vrc = RTProcQueryParent(pidParent, &pidParent);
3442 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3443 || vrc == VERR_ACCESS_DENIED)
3444 {
3445 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3446 mData->mSession.mPID = pid;
3447 }
3448#endif
3449
3450 if (mData->mSession.mPID != pid)
3451 return setError(E_ACCESSDENIED,
3452 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3453 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3454 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3455 }
3456
3457 // create the mutable SessionMachine from the current machine
3458 ComObjPtr<SessionMachine> sessionMachine;
3459 sessionMachine.createObject();
3460 rc = sessionMachine->init(this);
3461 AssertComRC(rc);
3462
3463 /* NOTE: doing return from this function after this point but
3464 * before the end is forbidden since it may call SessionMachine::uninit()
3465 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3466 * lock while still holding the Machine lock in alock so that a deadlock
3467 * is possible due to the wrong lock order. */
3468
3469 if (SUCCEEDED(rc))
3470 {
3471 /*
3472 * Set the session state to Spawning to protect against subsequent
3473 * attempts to open a session and to unregister the machine after
3474 * we release the lock.
3475 */
3476 SessionState_T origState = mData->mSession.mState;
3477 mData->mSession.mState = SessionState_Spawning;
3478
3479#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3480 /* Get the client token ID to be passed to the client process */
3481 Utf8Str strTokenId;
3482 sessionMachine->i_getTokenId(strTokenId);
3483 Assert(!strTokenId.isEmpty());
3484#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3485 /* Get the client token to be passed to the client process */
3486 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3487 /* The token is now "owned" by pToken, fix refcount */
3488 if (!pToken.isNull())
3489 pToken->Release();
3490#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3491
3492 /*
3493 * Release the lock before calling the client process -- it will call
3494 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3495 * because the state is Spawning, so that LaunchVMProcess() and
3496 * LockMachine() calls will fail. This method, called before we
3497 * acquire the lock again, will fail because of the wrong PID.
3498 *
3499 * Note that mData->mSession.mRemoteControls accessed outside
3500 * the lock may not be modified when state is Spawning, so it's safe.
3501 */
3502 alock.release();
3503
3504 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3505#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3506 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3507#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3508 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3509 /* Now the token is owned by the client process. */
3510 pToken.setNull();
3511#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3512 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3513
3514 /* The failure may occur w/o any error info (from RPC), so provide one */
3515 if (FAILED(rc))
3516 setError(VBOX_E_VM_ERROR,
3517 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3518
3519 // get session name, either to remember or to compare against
3520 // the already known session name.
3521 {
3522 Bstr bstrSessionName;
3523 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3524 if (SUCCEEDED(rc2))
3525 strSessionName = bstrSessionName;
3526 }
3527
3528 if ( SUCCEEDED(rc)
3529 && fLaunchingVMProcess
3530 )
3531 {
3532 /* complete the remote session initialization */
3533
3534 /* get the console from the direct session */
3535 ComPtr<IConsole> console;
3536 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3537 ComAssertComRC(rc);
3538
3539 if (SUCCEEDED(rc) && !console)
3540 {
3541 ComAssert(!!console);
3542 rc = E_FAIL;
3543 }
3544
3545 /* assign machine & console to the remote session */
3546 if (SUCCEEDED(rc))
3547 {
3548 /*
3549 * after LaunchVMProcess(), the first and the only
3550 * entry in remoteControls is that remote session
3551 */
3552 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3553 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3554 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3555
3556 /* The failure may occur w/o any error info (from RPC), so provide one */
3557 if (FAILED(rc))
3558 setError(VBOX_E_VM_ERROR,
3559 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3560 }
3561
3562 if (FAILED(rc))
3563 pSessionControl->Uninitialize();
3564 }
3565
3566 /* acquire the lock again */
3567 alock.acquire();
3568
3569 /* Restore the session state */
3570 mData->mSession.mState = origState;
3571 }
3572
3573 // finalize spawning anyway (this is why we don't return on errors above)
3574 if (fLaunchingVMProcess)
3575 {
3576 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3577 /* Note that the progress object is finalized later */
3578 /** @todo Consider checking mData->mSession.mProgress for cancellation
3579 * around here. */
3580
3581 /* We don't reset mSession.mPID here because it is necessary for
3582 * SessionMachine::uninit() to reap the child process later. */
3583
3584 if (FAILED(rc))
3585 {
3586 /* Close the remote session, remove the remote control from the list
3587 * and reset session state to Closed (@note keep the code in sync
3588 * with the relevant part in checkForSpawnFailure()). */
3589
3590 Assert(mData->mSession.mRemoteControls.size() == 1);
3591 if (mData->mSession.mRemoteControls.size() == 1)
3592 {
3593 ErrorInfoKeeper eik;
3594 mData->mSession.mRemoteControls.front()->Uninitialize();
3595 }
3596
3597 mData->mSession.mRemoteControls.clear();
3598 mData->mSession.mState = SessionState_Unlocked;
3599 }
3600 }
3601 else
3602 {
3603 /* memorize PID of the directly opened session */
3604 if (SUCCEEDED(rc))
3605 mData->mSession.mPID = pid;
3606 }
3607
3608 if (SUCCEEDED(rc))
3609 {
3610 mData->mSession.mLockType = aLockType;
3611 /* memorize the direct session control and cache IUnknown for it */
3612 mData->mSession.mDirectControl = pSessionControl;
3613 mData->mSession.mState = SessionState_Locked;
3614 if (!fLaunchingVMProcess)
3615 mData->mSession.mName = strSessionName;
3616 /* associate the SessionMachine with this Machine */
3617 mData->mSession.mMachine = sessionMachine;
3618
3619 /* request an IUnknown pointer early from the remote party for later
3620 * identity checks (it will be internally cached within mDirectControl
3621 * at least on XPCOM) */
3622 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3623 NOREF(unk);
3624
3625#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3626 if (aLockType == LockType_VM)
3627 {
3628 /* get the console from the direct session */
3629 ComPtr<IConsole> console;
3630 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3631 ComAssertComRC(rc);
3632 /* send passswords to console */
3633 for (SecretKeyStore::SecretKeyMap::iterator it = mData->mpKeyStore->begin();
3634 it != mData->mpKeyStore->end();
3635 ++it)
3636 {
3637 SecretKey *pKey = it->second;
3638 pKey->retain();
3639 console->AddEncryptionPassword(Bstr(it->first).raw(),
3640 Bstr((const char*)pKey->getKeyBuffer()).raw(),
3641 TRUE);
3642 pKey->release();
3643 }
3644
3645 }
3646#endif
3647 }
3648
3649 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3650 * would break the lock order */
3651 alock.release();
3652
3653 /* uninitialize the created session machine on failure */
3654 if (FAILED(rc))
3655 sessionMachine->uninit();
3656 }
3657
3658 if (SUCCEEDED(rc))
3659 {
3660 /*
3661 * tell the client watcher thread to update the set of
3662 * machines that have open sessions
3663 */
3664 mParent->i_updateClientWatcher();
3665
3666 if (oldState != SessionState_Locked)
3667 /* fire an event */
3668 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3669 }
3670
3671 return rc;
3672}
3673
3674/**
3675 * @note Locks objects!
3676 */
3677HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3678 const com::Utf8Str &aName,
3679 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3680 ComPtr<IProgress> &aProgress)
3681{
3682 Utf8Str strFrontend(aName);
3683 /* "emergencystop" doesn't need the session, so skip the checks/interface
3684 * retrieval. This code doesn't quite fit in here, but introducing a
3685 * special API method would be even more effort, and would require explicit
3686 * support by every API client. It's better to hide the feature a bit. */
3687 if (strFrontend != "emergencystop")
3688 CheckComArgNotNull(aSession);
3689
3690 HRESULT rc = S_OK;
3691 if (strFrontend.isEmpty())
3692 {
3693 Bstr bstrFrontend;
3694 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3695 if (FAILED(rc))
3696 return rc;
3697 strFrontend = bstrFrontend;
3698 if (strFrontend.isEmpty())
3699 {
3700 ComPtr<ISystemProperties> systemProperties;
3701 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3702 if (FAILED(rc))
3703 return rc;
3704 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3705 if (FAILED(rc))
3706 return rc;
3707 strFrontend = bstrFrontend;
3708 }
3709 /* paranoia - emergencystop is not a valid default */
3710 if (strFrontend == "emergencystop")
3711 strFrontend = Utf8Str::Empty;
3712 }
3713 /* default frontend: Qt GUI */
3714 if (strFrontend.isEmpty())
3715 strFrontend = "GUI/Qt";
3716
3717 if (strFrontend != "emergencystop")
3718 {
3719 /* check the session state */
3720 SessionState_T state;
3721 rc = aSession->COMGETTER(State)(&state);
3722 if (FAILED(rc))
3723 return rc;
3724
3725 if (state != SessionState_Unlocked)
3726 return setError(VBOX_E_INVALID_OBJECT_STATE,
3727 tr("The given session is busy"));
3728
3729 /* get the IInternalSessionControl interface */
3730 ComPtr<IInternalSessionControl> control(aSession);
3731 ComAssertMsgRet(!control.isNull(),
3732 ("No IInternalSessionControl interface"),
3733 E_INVALIDARG);
3734
3735 /* get the teleporter enable state for the progress object init. */
3736 BOOL fTeleporterEnabled;
3737 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3738 if (FAILED(rc))
3739 return rc;
3740
3741 /* create a progress object */
3742 ComObjPtr<ProgressProxy> progress;
3743 progress.createObject();
3744 rc = progress->init(mParent,
3745 static_cast<IMachine*>(this),
3746 Bstr(tr("Starting VM")).raw(),
3747 TRUE /* aCancelable */,
3748 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3749 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3750 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3751 2 /* uFirstOperationWeight */,
3752 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3753
3754 if (SUCCEEDED(rc))
3755 {
3756 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3757 if (SUCCEEDED(rc))
3758 {
3759 aProgress = progress;
3760
3761 /* signal the client watcher thread */
3762 mParent->i_updateClientWatcher();
3763
3764 /* fire an event */
3765 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3766 }
3767 }
3768 }
3769 else
3770 {
3771 /* no progress object - either instant success or failure */
3772 aProgress = NULL;
3773
3774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3775
3776 if (mData->mSession.mState != SessionState_Locked)
3777 return setError(VBOX_E_INVALID_OBJECT_STATE,
3778 tr("The machine '%s' is not locked by a session"),
3779 mUserData->s.strName.c_str());
3780
3781 /* must have a VM process associated - do not kill normal API clients
3782 * with an open session */
3783 if (!Global::IsOnline(mData->mMachineState))
3784 return setError(VBOX_E_INVALID_OBJECT_STATE,
3785 tr("The machine '%s' does not have a VM process"),
3786 mUserData->s.strName.c_str());
3787
3788 /* forcibly terminate the VM process */
3789 if (mData->mSession.mPID != NIL_RTPROCESS)
3790 RTProcTerminate(mData->mSession.mPID);
3791
3792 /* signal the client watcher thread, as most likely the client has
3793 * been terminated */
3794 mParent->i_updateClientWatcher();
3795 }
3796
3797 return rc;
3798}
3799
3800HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3801{
3802 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3803 return setError(E_INVALIDARG,
3804 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3805 aPosition, SchemaDefs::MaxBootPosition);
3806
3807 if (aDevice == DeviceType_USB)
3808 return setError(E_NOTIMPL,
3809 tr("Booting from USB device is currently not supported"));
3810
3811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3812
3813 HRESULT rc = i_checkStateDependency(MutableStateDep);
3814 if (FAILED(rc)) return rc;
3815
3816 i_setModified(IsModified_MachineData);
3817 mHWData.backup();
3818 mHWData->mBootOrder[aPosition - 1] = aDevice;
3819
3820 return S_OK;
3821}
3822
3823HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3824{
3825 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3826 return setError(E_INVALIDARG,
3827 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3828 aPosition, SchemaDefs::MaxBootPosition);
3829
3830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3831
3832 *aDevice = mHWData->mBootOrder[aPosition - 1];
3833
3834 return S_OK;
3835}
3836
3837HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3838 LONG aControllerPort,
3839 LONG aDevice,
3840 DeviceType_T aType,
3841 const ComPtr<IMedium> &aMedium)
3842{
3843 IMedium *aM = aMedium;
3844 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3845 aName.c_str(), aControllerPort, aDevice, aType, aM));
3846
3847 // request the host lock first, since might be calling Host methods for getting host drives;
3848 // next, protect the media tree all the while we're in here, as well as our member variables
3849 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3850 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3851
3852 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3853 if (FAILED(rc)) return rc;
3854
3855 /// @todo NEWMEDIA implicit machine registration
3856 if (!mData->mRegistered)
3857 return setError(VBOX_E_INVALID_OBJECT_STATE,
3858 tr("Cannot attach storage devices to an unregistered machine"));
3859
3860 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3861
3862 /* Check for an existing controller. */
3863 ComObjPtr<StorageController> ctl;
3864 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3865 if (FAILED(rc)) return rc;
3866
3867 StorageControllerType_T ctrlType;
3868 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3869 if (FAILED(rc))
3870 return setError(E_FAIL,
3871 tr("Could not get type of controller '%s'"),
3872 aName.c_str());
3873
3874 bool fSilent = false;
3875 Utf8Str strReconfig;
3876
3877 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3878 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3879 if ( mData->mMachineState == MachineState_Paused
3880 && strReconfig == "1")
3881 fSilent = true;
3882
3883 /* Check that the controller can do hot-plugging if we attach the device while the VM is running. */
3884 bool fHotplug = false;
3885 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3886 fHotplug = true;
3887
3888 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3889 return setError(VBOX_E_INVALID_VM_STATE,
3890 tr("Controller '%s' does not support hot-plugging"),
3891 aName.c_str());
3892
3893 /* Attaching a USB device when a VM is powered off should default to being marked as hot-pluggable */
3894 if (!fHotplug && !Global::IsOnlineOrTransient(mData->mMachineState) && ctrlType == StorageControllerType_USB)
3895 fHotplug = true;
3896
3897 // check that the port and device are not out of range
3898 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3899 if (FAILED(rc)) return rc;
3900
3901 /* check if the device slot is already busy */
3902 MediumAttachment *pAttachTemp;
3903 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3904 aName,
3905 aControllerPort,
3906 aDevice)))
3907 {
3908 Medium *pMedium = pAttachTemp->i_getMedium();
3909 if (pMedium)
3910 {
3911 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3912 return setError(VBOX_E_OBJECT_IN_USE,
3913 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3914 pMedium->i_getLocationFull().c_str(),
3915 aControllerPort,
3916 aDevice,
3917 aName.c_str());
3918 }
3919 else
3920 return setError(VBOX_E_OBJECT_IN_USE,
3921 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3922 aControllerPort, aDevice, aName.c_str());
3923 }
3924
3925 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3926 if (aMedium && medium.isNull())
3927 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3928
3929 AutoCaller mediumCaller(medium);
3930 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3931
3932 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3933
3934 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3935 && !medium.isNull()
3936 && ( medium->i_getType() != MediumType_Readonly
3937 || medium->i_getDeviceType() != DeviceType_DVD)
3938 )
3939 return setError(VBOX_E_OBJECT_IN_USE,
3940 tr("Medium '%s' is already attached to this virtual machine"),
3941 medium->i_getLocationFull().c_str());
3942
3943 if (!medium.isNull())
3944 {
3945 MediumType_T mtype = medium->i_getType();
3946 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3947 // For DVDs it's not written to the config file, so needs no global config
3948 // version bump. For floppies it's a new attribute "type", which is ignored
3949 // by older VirtualBox version, so needs no global config version bump either.
3950 // For hard disks this type is not accepted.
3951 if (mtype == MediumType_MultiAttach)
3952 {
3953 // This type is new with VirtualBox 4.0 and therefore requires settings
3954 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3955 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3956 // two reasons: The medium type is a property of the media registry tree, which
3957 // can reside in the global config file (for pre-4.0 media); we would therefore
3958 // possibly need to bump the global config version. We don't want to do that though
3959 // because that might make downgrading to pre-4.0 impossible.
3960 // As a result, we can only use these two new types if the medium is NOT in the
3961 // global registry:
3962 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3963 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3964 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3965 )
3966 return setError(VBOX_E_INVALID_OBJECT_STATE,
3967 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3968 "to machines that were created with VirtualBox 4.0 or later"),
3969 medium->i_getLocationFull().c_str());
3970 }
3971 }
3972
3973 bool fIndirect = false;
3974 if (!medium.isNull())
3975 fIndirect = medium->i_isReadOnly();
3976 bool associate = true;
3977
3978 do
3979 {
3980 if ( aType == DeviceType_HardDisk
3981 && mMediumAttachments.isBackedUp())
3982 {
3983 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3984
3985 /* check if the medium was attached to the VM before we started
3986 * changing attachments in which case the attachment just needs to
3987 * be restored */
3988 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3989 {
3990 AssertReturn(!fIndirect, E_FAIL);
3991
3992 /* see if it's the same bus/channel/device */
3993 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3994 {
3995 /* the simplest case: restore the whole attachment
3996 * and return, nothing else to do */
3997 mMediumAttachments->push_back(pAttachTemp);
3998
3999 /* Reattach the medium to the VM. */
4000 if (fHotplug || fSilent)
4001 {
4002 mediumLock.release();
4003 treeLock.release();
4004 alock.release();
4005
4006 MediumLockList *pMediumLockList(new MediumLockList());
4007
4008 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4009 medium /* pToLockWrite */,
4010 false /* fMediumLockWriteAll */,
4011 NULL,
4012 *pMediumLockList);
4013 alock.acquire();
4014 if (FAILED(rc))
4015 delete pMediumLockList;
4016 else
4017 {
4018 mData->mSession.mLockedMedia.Unlock();
4019 alock.release();
4020 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4021 mData->mSession.mLockedMedia.Lock();
4022 alock.acquire();
4023 }
4024 alock.release();
4025
4026 if (SUCCEEDED(rc))
4027 {
4028 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4029 /* Remove lock list in case of error. */
4030 if (FAILED(rc))
4031 {
4032 mData->mSession.mLockedMedia.Unlock();
4033 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4034 mData->mSession.mLockedMedia.Lock();
4035 }
4036 }
4037 }
4038
4039 return S_OK;
4040 }
4041
4042 /* bus/channel/device differ; we need a new attachment object,
4043 * but don't try to associate it again */
4044 associate = false;
4045 break;
4046 }
4047 }
4048
4049 /* go further only if the attachment is to be indirect */
4050 if (!fIndirect)
4051 break;
4052
4053 /* perform the so called smart attachment logic for indirect
4054 * attachments. Note that smart attachment is only applicable to base
4055 * hard disks. */
4056
4057 if (medium->i_getParent().isNull())
4058 {
4059 /* first, investigate the backup copy of the current hard disk
4060 * attachments to make it possible to re-attach existing diffs to
4061 * another device slot w/o losing their contents */
4062 if (mMediumAttachments.isBackedUp())
4063 {
4064 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4065
4066 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4067 uint32_t foundLevel = 0;
4068
4069 for (MediumAttachmentList::const_iterator
4070 it = oldAtts.begin();
4071 it != oldAtts.end();
4072 ++it)
4073 {
4074 uint32_t level = 0;
4075 MediumAttachment *pAttach = *it;
4076 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4077 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4078 if (pMedium.isNull())
4079 continue;
4080
4081 if (pMedium->i_getBase(&level) == medium)
4082 {
4083 /* skip the hard disk if its currently attached (we
4084 * cannot attach the same hard disk twice) */
4085 if (i_findAttachment(*mMediumAttachments.data(),
4086 pMedium))
4087 continue;
4088
4089 /* matched device, channel and bus (i.e. attached to the
4090 * same place) will win and immediately stop the search;
4091 * otherwise the attachment that has the youngest
4092 * descendant of medium will be used
4093 */
4094 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4095 {
4096 /* the simplest case: restore the whole attachment
4097 * and return, nothing else to do */
4098 mMediumAttachments->push_back(*it);
4099
4100 /* Reattach the medium to the VM. */
4101 if (fHotplug || fSilent)
4102 {
4103 mediumLock.release();
4104 treeLock.release();
4105 alock.release();
4106
4107 MediumLockList *pMediumLockList(new MediumLockList());
4108
4109 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4110 medium /* pToLockWrite */,
4111 false /* fMediumLockWriteAll */,
4112 NULL,
4113 *pMediumLockList);
4114 alock.acquire();
4115 if (FAILED(rc))
4116 delete pMediumLockList;
4117 else
4118 {
4119 mData->mSession.mLockedMedia.Unlock();
4120 alock.release();
4121 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4122 mData->mSession.mLockedMedia.Lock();
4123 alock.acquire();
4124 }
4125 alock.release();
4126
4127 if (SUCCEEDED(rc))
4128 {
4129 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4130 /* Remove lock list in case of error. */
4131 if (FAILED(rc))
4132 {
4133 mData->mSession.mLockedMedia.Unlock();
4134 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4135 mData->mSession.mLockedMedia.Lock();
4136 }
4137 }
4138 }
4139
4140 return S_OK;
4141 }
4142 else if ( foundIt == oldAtts.end()
4143 || level > foundLevel /* prefer younger */
4144 )
4145 {
4146 foundIt = it;
4147 foundLevel = level;
4148 }
4149 }
4150 }
4151
4152 if (foundIt != oldAtts.end())
4153 {
4154 /* use the previously attached hard disk */
4155 medium = (*foundIt)->i_getMedium();
4156 mediumCaller.attach(medium);
4157 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4158 mediumLock.attach(medium);
4159 /* not implicit, doesn't require association with this VM */
4160 fIndirect = false;
4161 associate = false;
4162 /* go right to the MediumAttachment creation */
4163 break;
4164 }
4165 }
4166
4167 /* must give up the medium lock and medium tree lock as below we
4168 * go over snapshots, which needs a lock with higher lock order. */
4169 mediumLock.release();
4170 treeLock.release();
4171
4172 /* then, search through snapshots for the best diff in the given
4173 * hard disk's chain to base the new diff on */
4174
4175 ComObjPtr<Medium> base;
4176 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4177 while (snap)
4178 {
4179 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4180
4181 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4182
4183 MediumAttachment *pAttachFound = NULL;
4184 uint32_t foundLevel = 0;
4185
4186 for (MediumAttachmentList::const_iterator
4187 it = snapAtts.begin();
4188 it != snapAtts.end();
4189 ++it)
4190 {
4191 MediumAttachment *pAttach = *it;
4192 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4193 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4194 if (pMedium.isNull())
4195 continue;
4196
4197 uint32_t level = 0;
4198 if (pMedium->i_getBase(&level) == medium)
4199 {
4200 /* matched device, channel and bus (i.e. attached to the
4201 * same place) will win and immediately stop the search;
4202 * otherwise the attachment that has the youngest
4203 * descendant of medium will be used
4204 */
4205 if ( pAttach->i_getDevice() == aDevice
4206 && pAttach->i_getPort() == aControllerPort
4207 && pAttach->i_getControllerName() == aName
4208 )
4209 {
4210 pAttachFound = pAttach;
4211 break;
4212 }
4213 else if ( !pAttachFound
4214 || level > foundLevel /* prefer younger */
4215 )
4216 {
4217 pAttachFound = pAttach;
4218 foundLevel = level;
4219 }
4220 }
4221 }
4222
4223 if (pAttachFound)
4224 {
4225 base = pAttachFound->i_getMedium();
4226 break;
4227 }
4228
4229 snap = snap->i_getParent();
4230 }
4231
4232 /* re-lock medium tree and the medium, as we need it below */
4233 treeLock.acquire();
4234 mediumLock.acquire();
4235
4236 /* found a suitable diff, use it as a base */
4237 if (!base.isNull())
4238 {
4239 medium = base;
4240 mediumCaller.attach(medium);
4241 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4242 mediumLock.attach(medium);
4243 }
4244 }
4245
4246 Utf8Str strFullSnapshotFolder;
4247 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4248
4249 ComObjPtr<Medium> diff;
4250 diff.createObject();
4251 // store this diff in the same registry as the parent
4252 Guid uuidRegistryParent;
4253 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4254 {
4255 // parent image has no registry: this can happen if we're attaching a new immutable
4256 // image that has not yet been attached (medium then points to the base and we're
4257 // creating the diff image for the immutable, and the parent is not yet registered);
4258 // put the parent in the machine registry then
4259 mediumLock.release();
4260 treeLock.release();
4261 alock.release();
4262 i_addMediumToRegistry(medium);
4263 alock.acquire();
4264 treeLock.acquire();
4265 mediumLock.acquire();
4266 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4267 }
4268 rc = diff->init(mParent,
4269 medium->i_getPreferredDiffFormat(),
4270 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4271 uuidRegistryParent,
4272 DeviceType_HardDisk);
4273 if (FAILED(rc)) return rc;
4274
4275 /* Apply the normal locking logic to the entire chain. */
4276 MediumLockList *pMediumLockList(new MediumLockList());
4277 mediumLock.release();
4278 treeLock.release();
4279 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4280 diff /* pToLockWrite */,
4281 false /* fMediumLockWriteAll */,
4282 medium,
4283 *pMediumLockList);
4284 treeLock.acquire();
4285 mediumLock.acquire();
4286 if (SUCCEEDED(rc))
4287 {
4288 mediumLock.release();
4289 treeLock.release();
4290 rc = pMediumLockList->Lock();
4291 treeLock.acquire();
4292 mediumLock.acquire();
4293 if (FAILED(rc))
4294 setError(rc,
4295 tr("Could not lock medium when creating diff '%s'"),
4296 diff->i_getLocationFull().c_str());
4297 else
4298 {
4299 /* will release the lock before the potentially lengthy
4300 * operation, so protect with the special state */
4301 MachineState_T oldState = mData->mMachineState;
4302 i_setMachineState(MachineState_SettingUp);
4303
4304 mediumLock.release();
4305 treeLock.release();
4306 alock.release();
4307
4308 rc = medium->i_createDiffStorage(diff,
4309 medium->i_getPreferredDiffVariant(),
4310 pMediumLockList,
4311 NULL /* aProgress */,
4312 true /* aWait */,
4313 false /* aNotify */);
4314
4315 alock.acquire();
4316 treeLock.acquire();
4317 mediumLock.acquire();
4318
4319 i_setMachineState(oldState);
4320 }
4321 }
4322
4323 /* Unlock the media and free the associated memory. */
4324 delete pMediumLockList;
4325
4326 if (FAILED(rc)) return rc;
4327
4328 /* use the created diff for the actual attachment */
4329 medium = diff;
4330 mediumCaller.attach(medium);
4331 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4332 mediumLock.attach(medium);
4333 }
4334 while (0);
4335
4336 ComObjPtr<MediumAttachment> attachment;
4337 attachment.createObject();
4338 rc = attachment->init(this,
4339 medium,
4340 aName,
4341 aControllerPort,
4342 aDevice,
4343 aType,
4344 fIndirect,
4345 false /* fPassthrough */,
4346 false /* fTempEject */,
4347 false /* fNonRotational */,
4348 false /* fDiscard */,
4349 fHotplug /* fHotPluggable */,
4350 Utf8Str::Empty);
4351 if (FAILED(rc)) return rc;
4352
4353 if (associate && !medium.isNull())
4354 {
4355 // as the last step, associate the medium to the VM
4356 rc = medium->i_addBackReference(mData->mUuid);
4357 // here we can fail because of Deleting, or being in process of creating a Diff
4358 if (FAILED(rc)) return rc;
4359
4360 mediumLock.release();
4361 treeLock.release();
4362 alock.release();
4363 i_addMediumToRegistry(medium);
4364 alock.acquire();
4365 treeLock.acquire();
4366 mediumLock.acquire();
4367 }
4368
4369 /* success: finally remember the attachment */
4370 i_setModified(IsModified_Storage);
4371 mMediumAttachments.backup();
4372 mMediumAttachments->push_back(attachment);
4373
4374 mediumLock.release();
4375 treeLock.release();
4376 alock.release();
4377
4378 if (fHotplug || fSilent)
4379 {
4380 if (!medium.isNull())
4381 {
4382 MediumLockList *pMediumLockList(new MediumLockList());
4383
4384 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4385 medium /* pToLockWrite */,
4386 false /* fMediumLockWriteAll */,
4387 NULL,
4388 *pMediumLockList);
4389 alock.acquire();
4390 if (FAILED(rc))
4391 delete pMediumLockList;
4392 else
4393 {
4394 mData->mSession.mLockedMedia.Unlock();
4395 alock.release();
4396 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4397 mData->mSession.mLockedMedia.Lock();
4398 alock.acquire();
4399 }
4400 alock.release();
4401 }
4402
4403 if (SUCCEEDED(rc))
4404 {
4405 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4406 /* Remove lock list in case of error. */
4407 if (FAILED(rc))
4408 {
4409 mData->mSession.mLockedMedia.Unlock();
4410 mData->mSession.mLockedMedia.Remove(attachment);
4411 mData->mSession.mLockedMedia.Lock();
4412 }
4413 }
4414 }
4415
4416 /* Save modified registries, but skip this machine as it's the caller's
4417 * job to save its settings like all other settings changes. */
4418 mParent->i_unmarkRegistryModified(i_getId());
4419 mParent->i_saveModifiedRegistries();
4420
4421 if (SUCCEEDED(rc))
4422 {
4423 if (fIndirect && medium != aM)
4424 mParent->i_onMediumConfigChanged(medium);
4425 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4426 }
4427
4428 return rc;
4429}
4430
4431HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4432 LONG aDevice)
4433{
4434 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4435 aName.c_str(), aControllerPort, aDevice));
4436
4437 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4438
4439 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4440 if (FAILED(rc)) return rc;
4441
4442 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4443
4444 /* Check for an existing controller. */
4445 ComObjPtr<StorageController> ctl;
4446 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4447 if (FAILED(rc)) return rc;
4448
4449 StorageControllerType_T ctrlType;
4450 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4451 if (FAILED(rc))
4452 return setError(E_FAIL,
4453 tr("Could not get type of controller '%s'"),
4454 aName.c_str());
4455
4456 bool fSilent = false;
4457 Utf8Str strReconfig;
4458
4459 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4460 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4461 if ( mData->mMachineState == MachineState_Paused
4462 && strReconfig == "1")
4463 fSilent = true;
4464
4465 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4466 bool fHotplug = false;
4467 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4468 fHotplug = true;
4469
4470 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4471 return setError(VBOX_E_INVALID_VM_STATE,
4472 tr("Controller '%s' does not support hot-plugging"),
4473 aName.c_str());
4474
4475 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4476 aName,
4477 aControllerPort,
4478 aDevice);
4479 if (!pAttach)
4480 return setError(VBOX_E_OBJECT_NOT_FOUND,
4481 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4482 aDevice, aControllerPort, aName.c_str());
4483
4484 if (fHotplug && !pAttach->i_getHotPluggable())
4485 return setError(VBOX_E_NOT_SUPPORTED,
4486 tr("The device slot %d on port %d of controller '%s' does not support hot-plugging"),
4487 aDevice, aControllerPort, aName.c_str());
4488
4489 /*
4490 * The VM has to detach the device before we delete any implicit diffs.
4491 * If this fails we can roll back without loosing data.
4492 */
4493 if (fHotplug || fSilent)
4494 {
4495 alock.release();
4496 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4497 alock.acquire();
4498 }
4499 if (FAILED(rc)) return rc;
4500
4501 /* If we are here everything went well and we can delete the implicit now. */
4502 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4503
4504 alock.release();
4505
4506 /* Save modified registries, but skip this machine as it's the caller's
4507 * job to save its settings like all other settings changes. */
4508 mParent->i_unmarkRegistryModified(i_getId());
4509 mParent->i_saveModifiedRegistries();
4510
4511 if (SUCCEEDED(rc))
4512 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4513
4514 return rc;
4515}
4516
4517HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4518 LONG aDevice, BOOL aPassthrough)
4519{
4520 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4521 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4522
4523 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4524
4525 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4526 if (FAILED(rc)) return rc;
4527
4528 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4529
4530 /* Check for an existing controller. */
4531 ComObjPtr<StorageController> ctl;
4532 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4533 if (FAILED(rc)) return rc;
4534
4535 StorageControllerType_T ctrlType;
4536 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4537 if (FAILED(rc))
4538 return setError(E_FAIL,
4539 tr("Could not get type of controller '%s'"),
4540 aName.c_str());
4541
4542 bool fSilent = false;
4543 Utf8Str strReconfig;
4544
4545 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4546 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4547 if ( mData->mMachineState == MachineState_Paused
4548 && strReconfig == "1")
4549 fSilent = true;
4550
4551 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4552 bool fHotplug = false;
4553 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4554 fHotplug = true;
4555
4556 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4557 return setError(VBOX_E_INVALID_VM_STATE,
4558 tr("Controller '%s' does not support hot-plugging which is required to change the passthrough setting while the VM is running"),
4559 aName.c_str());
4560
4561 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4562 aName,
4563 aControllerPort,
4564 aDevice);
4565 if (!pAttach)
4566 return setError(VBOX_E_OBJECT_NOT_FOUND,
4567 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4568 aDevice, aControllerPort, aName.c_str());
4569
4570
4571 i_setModified(IsModified_Storage);
4572 mMediumAttachments.backup();
4573
4574 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4575
4576 if (pAttach->i_getType() != DeviceType_DVD)
4577 return setError(E_INVALIDARG,
4578 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4579 aDevice, aControllerPort, aName.c_str());
4580
4581 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4582
4583 pAttach->i_updatePassthrough(!!aPassthrough);
4584
4585 attLock.release();
4586 alock.release();
4587 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4588 if (SUCCEEDED(rc) && fValueChanged)
4589 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4590
4591 return rc;
4592}
4593
4594HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4595 LONG aDevice, BOOL aTemporaryEject)
4596{
4597
4598 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4599 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4600
4601 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4602
4603 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4604 if (FAILED(rc)) return rc;
4605
4606 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4607 aName,
4608 aControllerPort,
4609 aDevice);
4610 if (!pAttach)
4611 return setError(VBOX_E_OBJECT_NOT_FOUND,
4612 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4613 aDevice, aControllerPort, aName.c_str());
4614
4615
4616 i_setModified(IsModified_Storage);
4617 mMediumAttachments.backup();
4618
4619 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4620
4621 if (pAttach->i_getType() != DeviceType_DVD)
4622 return setError(E_INVALIDARG,
4623 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4624 aDevice, aControllerPort, aName.c_str());
4625 pAttach->i_updateTempEject(!!aTemporaryEject);
4626
4627 return S_OK;
4628}
4629
4630HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4631 LONG aDevice, BOOL aNonRotational)
4632{
4633
4634 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4635 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4636
4637 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4638
4639 HRESULT rc = i_checkStateDependency(MutableStateDep);
4640 if (FAILED(rc)) return rc;
4641
4642 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4643
4644 if (Global::IsOnlineOrTransient(mData->mMachineState))
4645 return setError(VBOX_E_INVALID_VM_STATE,
4646 tr("Invalid machine state: %s"),
4647 Global::stringifyMachineState(mData->mMachineState));
4648
4649 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4650 aName,
4651 aControllerPort,
4652 aDevice);
4653 if (!pAttach)
4654 return setError(VBOX_E_OBJECT_NOT_FOUND,
4655 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4656 aDevice, aControllerPort, aName.c_str());
4657
4658
4659 i_setModified(IsModified_Storage);
4660 mMediumAttachments.backup();
4661
4662 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4663
4664 if (pAttach->i_getType() != DeviceType_HardDisk)
4665 return setError(E_INVALIDARG,
4666 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4667 aDevice, aControllerPort, aName.c_str());
4668 pAttach->i_updateNonRotational(!!aNonRotational);
4669
4670 return S_OK;
4671}
4672
4673HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4674 LONG aDevice, BOOL aDiscard)
4675{
4676
4677 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4678 aName.c_str(), aControllerPort, aDevice, aDiscard));
4679
4680 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4681
4682 HRESULT rc = i_checkStateDependency(MutableStateDep);
4683 if (FAILED(rc)) return rc;
4684
4685 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4686
4687 if (Global::IsOnlineOrTransient(mData->mMachineState))
4688 return setError(VBOX_E_INVALID_VM_STATE,
4689 tr("Invalid machine state: %s"),
4690 Global::stringifyMachineState(mData->mMachineState));
4691
4692 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4693 aName,
4694 aControllerPort,
4695 aDevice);
4696 if (!pAttach)
4697 return setError(VBOX_E_OBJECT_NOT_FOUND,
4698 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4699 aDevice, aControllerPort, aName.c_str());
4700
4701
4702 i_setModified(IsModified_Storage);
4703 mMediumAttachments.backup();
4704
4705 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4706
4707 if (pAttach->i_getType() != DeviceType_HardDisk)
4708 return setError(E_INVALIDARG,
4709 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4710 aDevice, aControllerPort, aName.c_str());
4711 pAttach->i_updateDiscard(!!aDiscard);
4712
4713 return S_OK;
4714}
4715
4716HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4717 LONG aDevice, BOOL aHotPluggable)
4718{
4719 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4720 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4721
4722 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4723
4724 HRESULT rc = i_checkStateDependency(MutableStateDep);
4725 if (FAILED(rc)) return rc;
4726
4727 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4728
4729 if (Global::IsOnlineOrTransient(mData->mMachineState))
4730 return setError(VBOX_E_INVALID_VM_STATE,
4731 tr("Invalid machine state: %s"),
4732 Global::stringifyMachineState(mData->mMachineState));
4733
4734 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4735 aName,
4736 aControllerPort,
4737 aDevice);
4738 if (!pAttach)
4739 return setError(VBOX_E_OBJECT_NOT_FOUND,
4740 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4741 aDevice, aControllerPort, aName.c_str());
4742
4743 /* Check for an existing controller. */
4744 ComObjPtr<StorageController> ctl;
4745 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4746 if (FAILED(rc)) return rc;
4747
4748 StorageControllerType_T ctrlType;
4749 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4750 if (FAILED(rc))
4751 return setError(E_FAIL,
4752 tr("Could not get type of controller '%s'"),
4753 aName.c_str());
4754
4755 if (!i_isControllerHotplugCapable(ctrlType))
4756 return setError(VBOX_E_NOT_SUPPORTED,
4757 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4758 aName.c_str());
4759
4760 /* silently ignore attempts to modify the hot-plug status of USB devices */
4761 if (ctrlType == StorageControllerType_USB)
4762 return S_OK;
4763
4764 i_setModified(IsModified_Storage);
4765 mMediumAttachments.backup();
4766
4767 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4768
4769 if (pAttach->i_getType() == DeviceType_Floppy)
4770 return setError(E_INVALIDARG,
4771 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%s' is a floppy drive"),
4772 aDevice, aControllerPort, aName.c_str());
4773 pAttach->i_updateHotPluggable(!!aHotPluggable);
4774
4775 return S_OK;
4776}
4777
4778HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4779 LONG aDevice)
4780{
4781 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4782 aName.c_str(), aControllerPort, aDevice));
4783
4784 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4785}
4786
4787HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4788 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4789{
4790 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4791 aName.c_str(), aControllerPort, aDevice));
4792
4793 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4794
4795 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4796 if (FAILED(rc)) return rc;
4797
4798 if (Global::IsOnlineOrTransient(mData->mMachineState))
4799 return setError(VBOX_E_INVALID_VM_STATE,
4800 tr("Invalid machine state: %s"),
4801 Global::stringifyMachineState(mData->mMachineState));
4802
4803 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4804 aName,
4805 aControllerPort,
4806 aDevice);
4807 if (!pAttach)
4808 return setError(VBOX_E_OBJECT_NOT_FOUND,
4809 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4810 aDevice, aControllerPort, aName.c_str());
4811
4812
4813 i_setModified(IsModified_Storage);
4814 mMediumAttachments.backup();
4815
4816 IBandwidthGroup *iB = aBandwidthGroup;
4817 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4818 if (aBandwidthGroup && group.isNull())
4819 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4820
4821 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4822
4823 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4824 if (strBandwidthGroupOld.isNotEmpty())
4825 {
4826 /* Get the bandwidth group object and release it - this must not fail. */
4827 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4828 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4829 Assert(SUCCEEDED(rc));
4830
4831 pBandwidthGroupOld->i_release();
4832 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4833 }
4834
4835 if (!group.isNull())
4836 {
4837 group->i_reference();
4838 pAttach->i_updateBandwidthGroup(group->i_getName());
4839 }
4840
4841 return S_OK;
4842}
4843
4844HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4845 LONG aControllerPort,
4846 LONG aDevice,
4847 DeviceType_T aType)
4848{
4849 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4850 aName.c_str(), aControllerPort, aDevice, aType));
4851
4852 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4853}
4854
4855
4856HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4857 LONG aControllerPort,
4858 LONG aDevice,
4859 BOOL aForce)
4860{
4861 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4862 aName.c_str(), aControllerPort, aForce));
4863
4864 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4865}
4866
4867HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4868 LONG aControllerPort,
4869 LONG aDevice,
4870 const ComPtr<IMedium> &aMedium,
4871 BOOL aForce)
4872{
4873 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4874 aName.c_str(), aControllerPort, aDevice, aForce));
4875
4876 // request the host lock first, since might be calling Host methods for getting host drives;
4877 // next, protect the media tree all the while we're in here, as well as our member variables
4878 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4879 this->lockHandle(),
4880 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4881
4882 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4883 if (FAILED(hrc)) return hrc;
4884
4885 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4886 aName,
4887 aControllerPort,
4888 aDevice);
4889 if (pAttach.isNull())
4890 return setError(VBOX_E_OBJECT_NOT_FOUND,
4891 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4892 aDevice, aControllerPort, aName.c_str());
4893
4894 /* Remember previously mounted medium. The medium before taking the
4895 * backup is not necessarily the same thing. */
4896 ComObjPtr<Medium> oldmedium;
4897 oldmedium = pAttach->i_getMedium();
4898
4899 IMedium *iM = aMedium;
4900 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4901 if (aMedium && pMedium.isNull())
4902 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4903
4904 /* Check if potential medium is already mounted */
4905 if (pMedium == oldmedium)
4906 return S_OK;
4907
4908 AutoCaller mediumCaller(pMedium);
4909 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4910
4911 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4912 if (pMedium)
4913 {
4914 DeviceType_T mediumType = pAttach->i_getType();
4915 switch (mediumType)
4916 {
4917 case DeviceType_DVD:
4918 case DeviceType_Floppy:
4919 break;
4920
4921 default:
4922 return setError(VBOX_E_INVALID_OBJECT_STATE,
4923 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4924 aControllerPort,
4925 aDevice,
4926 aName.c_str());
4927 }
4928 }
4929
4930 i_setModified(IsModified_Storage);
4931 mMediumAttachments.backup();
4932
4933 {
4934 // The backup operation makes the pAttach reference point to the
4935 // old settings. Re-get the correct reference.
4936 pAttach = i_findAttachment(*mMediumAttachments.data(),
4937 aName,
4938 aControllerPort,
4939 aDevice);
4940 if (!oldmedium.isNull())
4941 oldmedium->i_removeBackReference(mData->mUuid);
4942 if (!pMedium.isNull())
4943 {
4944 pMedium->i_addBackReference(mData->mUuid);
4945
4946 mediumLock.release();
4947 multiLock.release();
4948 i_addMediumToRegistry(pMedium);
4949 multiLock.acquire();
4950 mediumLock.acquire();
4951 }
4952
4953 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4954 pAttach->i_updateMedium(pMedium);
4955 }
4956
4957 i_setModified(IsModified_Storage);
4958
4959 mediumLock.release();
4960 multiLock.release();
4961 HRESULT rc = i_onMediumChange(pAttach, aForce);
4962 multiLock.acquire();
4963 mediumLock.acquire();
4964
4965 /* On error roll back this change only. */
4966 if (FAILED(rc))
4967 {
4968 if (!pMedium.isNull())
4969 pMedium->i_removeBackReference(mData->mUuid);
4970 pAttach = i_findAttachment(*mMediumAttachments.data(),
4971 aName,
4972 aControllerPort,
4973 aDevice);
4974 /* If the attachment is gone in the meantime, bail out. */
4975 if (pAttach.isNull())
4976 return rc;
4977 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4978 if (!oldmedium.isNull())
4979 oldmedium->i_addBackReference(mData->mUuid);
4980 pAttach->i_updateMedium(oldmedium);
4981 }
4982
4983 mediumLock.release();
4984 multiLock.release();
4985
4986 /* Save modified registries, but skip this machine as it's the caller's
4987 * job to save its settings like all other settings changes. */
4988 mParent->i_unmarkRegistryModified(i_getId());
4989 mParent->i_saveModifiedRegistries();
4990
4991 return rc;
4992}
4993HRESULT Machine::getMedium(const com::Utf8Str &aName,
4994 LONG aControllerPort,
4995 LONG aDevice,
4996 ComPtr<IMedium> &aMedium)
4997{
4998 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4999 aName.c_str(), aControllerPort, aDevice));
5000
5001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5002
5003 aMedium = NULL;
5004
5005 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5006 aName,
5007 aControllerPort,
5008 aDevice);
5009 if (pAttach.isNull())
5010 return setError(VBOX_E_OBJECT_NOT_FOUND,
5011 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5012 aDevice, aControllerPort, aName.c_str());
5013
5014 aMedium = pAttach->i_getMedium();
5015
5016 return S_OK;
5017}
5018
5019HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
5020{
5021 if (aSlot < RT_ELEMENTS(mSerialPorts))
5022 {
5023 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5024 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5025 return S_OK;
5026 }
5027 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
5028}
5029
5030HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
5031{
5032 if (aSlot < RT_ELEMENTS(mParallelPorts))
5033 {
5034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5035 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5036 return S_OK;
5037 }
5038 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
5039}
5040
5041
5042HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
5043{
5044 /* Do not assert if slot is out of range, just return the advertised
5045 status. testdriver/vbox.py triggers this in logVmInfo. */
5046 if (aSlot >= mNetworkAdapters.size())
5047 return setError(E_INVALIDARG,
5048 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
5049 aSlot, mNetworkAdapters.size());
5050
5051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5052
5053 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
5054
5055 return S_OK;
5056}
5057
5058HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
5059{
5060 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5061
5062 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
5063 size_t i = 0;
5064 for (settings::StringsMap::const_iterator
5065 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5066 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5067 ++it, ++i)
5068 aKeys[i] = it->first;
5069
5070 return S_OK;
5071}
5072
5073 /**
5074 * @note Locks this object for reading.
5075 */
5076HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
5077 com::Utf8Str &aValue)
5078{
5079 /* start with nothing found */
5080 aValue = "";
5081
5082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5083
5084 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5085 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5086 // found:
5087 aValue = it->second; // source is a Utf8Str
5088
5089 /* return the result to caller (may be empty) */
5090 return S_OK;
5091}
5092
5093 /**
5094 * @note Locks mParent for writing + this object for writing.
5095 */
5096HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5097{
5098 /* Because control characters in aKey have caused problems in the settings
5099 * they are rejected unless the key should be deleted. */
5100 if (!aValue.isEmpty())
5101 {
5102 for (size_t i = 0; i < aKey.length(); ++i)
5103 {
5104 char ch = aKey[i];
5105 if (RTLocCIsCntrl(ch))
5106 return E_INVALIDARG;
5107 }
5108 }
5109
5110 Utf8Str strOldValue; // empty
5111
5112 // locking note: we only hold the read lock briefly to look up the old value,
5113 // then release it and call the onExtraCanChange callbacks. There is a small
5114 // chance of a race insofar as the callback might be called twice if two callers
5115 // change the same key at the same time, but that's a much better solution
5116 // than the deadlock we had here before. The actual changing of the extradata
5117 // is then performed under the write lock and race-free.
5118
5119 // look up the old value first; if nothing has changed then we need not do anything
5120 {
5121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5122
5123 // For snapshots don't even think about allowing changes, extradata
5124 // is global for a machine, so there is nothing snapshot specific.
5125 if (i_isSnapshotMachine())
5126 return setError(VBOX_E_INVALID_VM_STATE,
5127 tr("Cannot set extradata for a snapshot"));
5128
5129 // check if the right IMachine instance is used
5130 if (mData->mRegistered && !i_isSessionMachine())
5131 return setError(VBOX_E_INVALID_VM_STATE,
5132 tr("Cannot set extradata for an immutable machine"));
5133
5134 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5135 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5136 strOldValue = it->second;
5137 }
5138
5139 bool fChanged;
5140 if ((fChanged = (strOldValue != aValue)))
5141 {
5142 // ask for permission from all listeners outside the locks;
5143 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5144 // lock to copy the list of callbacks to invoke
5145 Bstr bstrError;
5146 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
5147 {
5148 const char *sep = bstrError.isEmpty() ? "" : ": ";
5149 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
5150 return setError(E_ACCESSDENIED,
5151 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5152 aKey.c_str(),
5153 aValue.c_str(),
5154 sep,
5155 bstrError.raw());
5156 }
5157
5158 // data is changing and change not vetoed: then write it out under the lock
5159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5160
5161 if (aValue.isEmpty())
5162 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5163 else
5164 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5165 // creates a new key if needed
5166
5167 bool fNeedsGlobalSaveSettings = false;
5168 // This saving of settings is tricky: there is no "old state" for the
5169 // extradata items at all (unlike all other settings), so the old/new
5170 // settings comparison would give a wrong result!
5171 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
5172
5173 if (fNeedsGlobalSaveSettings)
5174 {
5175 // save the global settings; for that we should hold only the VirtualBox lock
5176 alock.release();
5177 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5178 mParent->i_saveSettings();
5179 }
5180 }
5181
5182 // fire notification outside the lock
5183 if (fChanged)
5184 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
5185
5186 return S_OK;
5187}
5188
5189HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5190{
5191 aProgress = NULL;
5192 NOREF(aSettingsFilePath);
5193 ReturnComNotImplemented();
5194}
5195
5196HRESULT Machine::saveSettings()
5197{
5198 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5199
5200 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5201 if (FAILED(rc)) return rc;
5202
5203 /* the settings file path may never be null */
5204 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5205
5206 /* save all VM data excluding snapshots */
5207 bool fNeedsGlobalSaveSettings = false;
5208 rc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
5209 mlock.release();
5210
5211 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5212 {
5213 // save the global settings; for that we should hold only the VirtualBox lock
5214 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5215 rc = mParent->i_saveSettings();
5216 }
5217
5218 return rc;
5219}
5220
5221
5222HRESULT Machine::discardSettings()
5223{
5224 /*
5225 * We need to take the machine list lock here as well as the machine one
5226 * or we'll get into trouble should any media stuff require rolling back.
5227 *
5228 * Details:
5229 *
5230 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5231 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5232 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other lock: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x]
5233 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: class=0000000000d5eb10 4-LISTOFMACHINES created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
5234 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5235 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5236 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5237 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: class=0000000000d5ecd0 5-MACHINEOBJECT created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
5238 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5239 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5240 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5241 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5242 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5243 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #00: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=2 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(11705) Machine::i_rollback 00007ff6853f6ce4} [x/r]
5244 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #01: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x] (*)
5245 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5246 * 0:005> k
5247 * # Child-SP RetAddr Call Site
5248 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5249 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5250 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5251 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5252 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5253 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5254 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5255 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5256 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5257 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5258 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5259 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5260 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5261 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5262 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5263 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5264 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5265 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5266 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5267 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5268 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5269 *
5270 */
5271 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5273
5274 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5275 if (FAILED(rc)) return rc;
5276
5277 /*
5278 * during this rollback, the session will be notified if data has
5279 * been actually changed
5280 */
5281 i_rollback(true /* aNotify */);
5282
5283 return S_OK;
5284}
5285
5286/** @note Locks objects! */
5287HRESULT Machine::unregister(AutoCaller &autoCaller,
5288 CleanupMode_T aCleanupMode,
5289 std::vector<ComPtr<IMedium> > &aMedia)
5290{
5291 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5292
5293 Guid id(i_getId());
5294
5295 if (mData->mSession.mState != SessionState_Unlocked)
5296 return setError(VBOX_E_INVALID_OBJECT_STATE,
5297 tr("Cannot unregister the machine '%s' while it is locked"),
5298 mUserData->s.strName.c_str());
5299
5300 // wait for state dependents to drop to zero
5301 i_ensureNoStateDependencies(alock);
5302
5303 if (!mData->mAccessible)
5304 {
5305 // inaccessible machines can only be unregistered; uninitialize ourselves
5306 // here because currently there may be no unregistered that are inaccessible
5307 // (this state combination is not supported). Note releasing the caller and
5308 // leaving the lock before calling uninit()
5309 alock.release();
5310 autoCaller.release();
5311
5312 uninit();
5313
5314 mParent->i_unregisterMachine(this, CleanupMode_UnregisterOnly, id);
5315 // calls VirtualBox::i_saveSettings()
5316
5317 return S_OK;
5318 }
5319
5320 HRESULT rc = S_OK;
5321 mData->llFilesToDelete.clear();
5322
5323 if (!mSSData->strStateFilePath.isEmpty())
5324 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5325
5326 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
5327 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
5328 mData->llFilesToDelete.push_back(strNVRAMFile);
5329
5330 // This list collects the medium objects from all medium attachments
5331 // which we will detach from the machine and its snapshots, in a specific
5332 // order which allows for closing all media without getting "media in use"
5333 // errors, simply by going through the list from the front to the back:
5334 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5335 // and must be closed before the parent media from the snapshots, or closing the parents
5336 // will fail because they still have children);
5337 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5338 // the root ("first") snapshot of the machine.
5339 MediaList llMedia;
5340
5341 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5342 && mMediumAttachments->size()
5343 )
5344 {
5345 // we have media attachments: detach them all and add the Medium objects to our list
5346 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5347 }
5348
5349 if (mData->mFirstSnapshot)
5350 {
5351 // add the media from the medium attachments of the snapshots to
5352 // llMedia as well, after the "main" machine media;
5353 // Snapshot::uninitAll() calls Machine::detachAllMedia() for each
5354 // snapshot machine, depth first.
5355
5356 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5357 MachineState_T oldState = mData->mMachineState;
5358 mData->mMachineState = MachineState_DeletingSnapshot;
5359
5360 // make a copy of the first snapshot reference so the refcount does not
5361 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
5362 // (would hang due to the AutoCaller voodoo)
5363 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5364
5365 // GO!
5366 pFirstSnapshot->i_uninitAll(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5367
5368 mData->mMachineState = oldState;
5369 }
5370
5371 if (FAILED(rc))
5372 {
5373 i_rollbackMedia();
5374 return rc;
5375 }
5376
5377 // commit all the media changes made above
5378 i_commitMedia();
5379
5380 mData->mRegistered = false;
5381
5382 // machine lock no longer needed
5383 alock.release();
5384
5385 /* Make sure that the settings of the current VM are not saved, because
5386 * they are rather crippled at this point to meet the cleanup expectations
5387 * and there's no point destroying the VM config on disk just because. */
5388 mParent->i_unmarkRegistryModified(id);
5389
5390 // return media to caller
5391 aMedia.resize(llMedia.size());
5392 size_t i = 0;
5393 for (MediaList::const_iterator
5394 it = llMedia.begin();
5395 it != llMedia.end();
5396 ++it, ++i)
5397 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5398
5399 mParent->i_unregisterMachine(this, aCleanupMode, id);
5400 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5401
5402 return S_OK;
5403}
5404
5405/**
5406 * Task record for deleting a machine config.
5407 */
5408class Machine::DeleteConfigTask
5409 : public Machine::Task
5410{
5411public:
5412 DeleteConfigTask(Machine *m,
5413 Progress *p,
5414 const Utf8Str &t,
5415 const RTCList<ComPtr<IMedium> > &llMediums,
5416 const StringsList &llFilesToDelete)
5417 : Task(m, p, t),
5418 m_llMediums(llMediums),
5419 m_llFilesToDelete(llFilesToDelete)
5420 {}
5421
5422private:
5423 void handler()
5424 {
5425 try
5426 {
5427 m_pMachine->i_deleteConfigHandler(*this);
5428 }
5429 catch (...)
5430 {
5431 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5432 }
5433 }
5434
5435 RTCList<ComPtr<IMedium> > m_llMediums;
5436 StringsList m_llFilesToDelete;
5437
5438 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5439};
5440
5441/**
5442 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5443 * SessionMachine::taskHandler().
5444 *
5445 * @note Locks this object for writing.
5446 *
5447 * @param task
5448 * @return
5449 */
5450void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5451{
5452 LogFlowThisFuncEnter();
5453
5454 AutoCaller autoCaller(this);
5455 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5456 if (FAILED(autoCaller.rc()))
5457 {
5458 /* we might have been uninitialized because the session was accidentally
5459 * closed by the client, so don't assert */
5460 HRESULT rc = setError(E_FAIL,
5461 tr("The session has been accidentally closed"));
5462 task.m_pProgress->i_notifyComplete(rc);
5463 LogFlowThisFuncLeave();
5464 return;
5465 }
5466
5467 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5468
5469 HRESULT rc = S_OK;
5470
5471 try
5472 {
5473 ULONG uLogHistoryCount = 3;
5474 ComPtr<ISystemProperties> systemProperties;
5475 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5476 if (FAILED(rc)) throw rc;
5477
5478 if (!systemProperties.isNull())
5479 {
5480 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5481 if (FAILED(rc)) throw rc;
5482 }
5483
5484 MachineState_T oldState = mData->mMachineState;
5485 i_setMachineState(MachineState_SettingUp);
5486 alock.release();
5487 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5488 {
5489 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5490 {
5491 AutoCaller mac(pMedium);
5492 if (FAILED(mac.rc())) throw mac.rc();
5493 Utf8Str strLocation = pMedium->i_getLocationFull();
5494 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5495 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5496 if (FAILED(rc)) throw rc;
5497 }
5498 if (pMedium->i_isMediumFormatFile())
5499 {
5500 ComPtr<IProgress> pProgress2;
5501 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5502 if (FAILED(rc)) throw rc;
5503 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5504 if (FAILED(rc)) throw rc;
5505 }
5506
5507 /* Close the medium, deliberately without checking the return
5508 * code, and without leaving any trace in the error info, as
5509 * a failure here is a very minor issue, which shouldn't happen
5510 * as above we even managed to delete the medium. */
5511 {
5512 ErrorInfoKeeper eik;
5513 pMedium->Close();
5514 }
5515 }
5516 i_setMachineState(oldState);
5517 alock.acquire();
5518
5519 // delete the files pushed on the task list by Machine::Delete()
5520 // (this includes saved states of the machine and snapshots and
5521 // medium storage files from the IMedium list passed in, and the
5522 // machine XML file)
5523 for (StringsList::const_iterator
5524 it = task.m_llFilesToDelete.begin();
5525 it != task.m_llFilesToDelete.end();
5526 ++it)
5527 {
5528 const Utf8Str &strFile = *it;
5529 LogFunc(("Deleting file %s\n", strFile.c_str()));
5530 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5531 if (FAILED(rc)) throw rc;
5532 i_deleteFile(strFile);
5533 }
5534
5535 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5536 if (FAILED(rc)) throw rc;
5537
5538 /* delete the settings only when the file actually exists */
5539 if (mData->pMachineConfigFile->fileExists())
5540 {
5541 /* Delete any backup or uncommitted XML files. Ignore failures.
5542 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5543 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5544 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5545 i_deleteFile(otherXml, true /* fIgnoreFailures */);
5546 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5547 i_deleteFile(otherXml, true /* fIgnoreFailures */);
5548
5549 /* delete the Logs folder, nothing important should be left
5550 * there (we don't check for errors because the user might have
5551 * some private files there that we don't want to delete) */
5552 Utf8Str logFolder;
5553 getLogFolder(logFolder);
5554 Assert(logFolder.length());
5555 if (RTDirExists(logFolder.c_str()))
5556 {
5557 /* Delete all VBox.log[.N] files from the Logs folder
5558 * (this must be in sync with the rotation logic in
5559 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5560 * files that may have been created by the GUI. */
5561 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5562 i_deleteFile(log, true /* fIgnoreFailures */);
5563 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5564 i_deleteFile(log, true /* fIgnoreFailures */);
5565 for (ULONG i = uLogHistoryCount; i > 0; i--)
5566 {
5567 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5568 i_deleteFile(log, true /* fIgnoreFailures */);
5569 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5570 i_deleteFile(log, true /* fIgnoreFailures */);
5571 }
5572 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5573 i_deleteFile(log, true /* fIgnoreFailures */);
5574#if defined(RT_OS_WINDOWS)
5575 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5576 i_deleteFile(log, true /* fIgnoreFailures */);
5577 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5578 i_deleteFile(log, true /* fIgnoreFailures */);
5579#endif
5580
5581 RTDirRemove(logFolder.c_str());
5582 }
5583
5584 /* delete the Snapshots folder, nothing important should be left
5585 * there (we don't check for errors because the user might have
5586 * some private files there that we don't want to delete) */
5587 Utf8Str strFullSnapshotFolder;
5588 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5589 Assert(!strFullSnapshotFolder.isEmpty());
5590 if (RTDirExists(strFullSnapshotFolder.c_str()))
5591 RTDirRemove(strFullSnapshotFolder.c_str());
5592
5593 // delete the directory that contains the settings file, but only
5594 // if it matches the VM name
5595 Utf8Str settingsDir;
5596 if (i_isInOwnDir(&settingsDir))
5597 RTDirRemove(settingsDir.c_str());
5598 }
5599
5600 alock.release();
5601
5602 mParent->i_saveModifiedRegistries();
5603 }
5604 catch (HRESULT aRC) { rc = aRC; }
5605
5606 task.m_pProgress->i_notifyComplete(rc);
5607
5608 LogFlowThisFuncLeave();
5609}
5610
5611HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5612{
5613 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5614
5615 HRESULT rc = i_checkStateDependency(MutableStateDep);
5616 if (FAILED(rc)) return rc;
5617
5618 if (mData->mRegistered)
5619 return setError(VBOX_E_INVALID_VM_STATE,
5620 tr("Cannot delete settings of a registered machine"));
5621
5622 // collect files to delete
5623 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5624 // machine config file
5625 if (mData->pMachineConfigFile->fileExists())
5626 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5627 // backup of machine config file
5628 Utf8Str strTmp(mData->m_strConfigFileFull);
5629 strTmp.append("-prev");
5630 if (RTFileExists(strTmp.c_str()))
5631 llFilesToDelete.push_back(strTmp);
5632
5633 RTCList<ComPtr<IMedium> > llMediums;
5634 for (size_t i = 0; i < aMedia.size(); ++i)
5635 {
5636 IMedium *pIMedium(aMedia[i]);
5637 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5638 if (pMedium.isNull())
5639 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
5640 SafeArray<BSTR> ids;
5641 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5642 if (FAILED(rc)) return rc;
5643 /* At this point the medium should not have any back references
5644 * anymore. If it has it is attached to another VM and *must* not
5645 * deleted. */
5646 if (ids.size() < 1)
5647 llMediums.append(pMedium);
5648 }
5649
5650 ComObjPtr<Progress> pProgress;
5651 pProgress.createObject();
5652 rc = pProgress->init(i_getVirtualBox(),
5653 static_cast<IMachine*>(this) /* aInitiator */,
5654 tr("Deleting files"),
5655 true /* fCancellable */,
5656 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5657 tr("Collecting file inventory"));
5658 if (FAILED(rc))
5659 return rc;
5660
5661 /* create and start the task on a separate thread (note that it will not
5662 * start working until we release alock) */
5663 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5664 rc = pTask->createThread();
5665 pTask = NULL;
5666 if (FAILED(rc))
5667 return rc;
5668
5669 pProgress.queryInterfaceTo(aProgress.asOutParam());
5670
5671 LogFlowFuncLeave();
5672
5673 return S_OK;
5674}
5675
5676HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5677{
5678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5679
5680 ComObjPtr<Snapshot> pSnapshot;
5681 HRESULT rc;
5682
5683 if (aNameOrId.isEmpty())
5684 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5685 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5686 else
5687 {
5688 Guid uuid(aNameOrId);
5689 if (uuid.isValid())
5690 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5691 else
5692 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5693 }
5694 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5695
5696 return rc;
5697}
5698
5699HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5700 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5701{
5702 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5703
5704 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5705 if (FAILED(rc)) return rc;
5706
5707 ComObjPtr<SharedFolder> sharedFolder;
5708 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5709 if (SUCCEEDED(rc))
5710 return setError(VBOX_E_OBJECT_IN_USE,
5711 tr("Shared folder named '%s' already exists"),
5712 aName.c_str());
5713
5714 sharedFolder.createObject();
5715 rc = sharedFolder->init(i_getMachine(),
5716 aName,
5717 aHostPath,
5718 !!aWritable,
5719 !!aAutomount,
5720 aAutoMountPoint,
5721 true /* fFailOnError */);
5722 if (FAILED(rc)) return rc;
5723
5724 i_setModified(IsModified_SharedFolders);
5725 mHWData.backup();
5726 mHWData->mSharedFolders.push_back(sharedFolder);
5727
5728 /* inform the direct session if any */
5729 alock.release();
5730 i_onSharedFolderChange();
5731
5732 return S_OK;
5733}
5734
5735HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5736{
5737 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5738
5739 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5740 if (FAILED(rc)) return rc;
5741
5742 ComObjPtr<SharedFolder> sharedFolder;
5743 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5744 if (FAILED(rc)) return rc;
5745
5746 i_setModified(IsModified_SharedFolders);
5747 mHWData.backup();
5748 mHWData->mSharedFolders.remove(sharedFolder);
5749
5750 /* inform the direct session if any */
5751 alock.release();
5752 i_onSharedFolderChange();
5753
5754 return S_OK;
5755}
5756
5757HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5758{
5759 /* start with No */
5760 *aCanShow = FALSE;
5761
5762 ComPtr<IInternalSessionControl> directControl;
5763 {
5764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5765
5766 if (mData->mSession.mState != SessionState_Locked)
5767 return setError(VBOX_E_INVALID_VM_STATE,
5768 tr("Machine is not locked for session (session state: %s)"),
5769 Global::stringifySessionState(mData->mSession.mState));
5770
5771 if (mData->mSession.mLockType == LockType_VM)
5772 directControl = mData->mSession.mDirectControl;
5773 }
5774
5775 /* ignore calls made after #OnSessionEnd() is called */
5776 if (!directControl)
5777 return S_OK;
5778
5779 LONG64 dummy;
5780 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5781}
5782
5783HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5784{
5785 ComPtr<IInternalSessionControl> directControl;
5786 {
5787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5788
5789 if (mData->mSession.mState != SessionState_Locked)
5790 return setError(E_FAIL,
5791 tr("Machine is not locked for session (session state: %s)"),
5792 Global::stringifySessionState(mData->mSession.mState));
5793
5794 if (mData->mSession.mLockType == LockType_VM)
5795 directControl = mData->mSession.mDirectControl;
5796 }
5797
5798 /* ignore calls made after #OnSessionEnd() is called */
5799 if (!directControl)
5800 return S_OK;
5801
5802 BOOL dummy;
5803 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5804}
5805
5806#ifdef VBOX_WITH_GUEST_PROPS
5807/**
5808 * Look up a guest property in VBoxSVC's internal structures.
5809 */
5810HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5811 com::Utf8Str &aValue,
5812 LONG64 *aTimestamp,
5813 com::Utf8Str &aFlags) const
5814{
5815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5816
5817 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5818 if (it != mHWData->mGuestProperties.end())
5819 {
5820 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5821 aValue = it->second.strValue;
5822 *aTimestamp = it->second.mTimestamp;
5823 GuestPropWriteFlags(it->second.mFlags, szFlags);
5824 aFlags = Utf8Str(szFlags);
5825 }
5826
5827 return S_OK;
5828}
5829
5830/**
5831 * Query the VM that a guest property belongs to for the property.
5832 * @returns E_ACCESSDENIED if the VM process is not available or not
5833 * currently handling queries and the lookup should then be done in
5834 * VBoxSVC.
5835 */
5836HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5837 com::Utf8Str &aValue,
5838 LONG64 *aTimestamp,
5839 com::Utf8Str &aFlags) const
5840{
5841 HRESULT rc = S_OK;
5842 Bstr bstrValue;
5843 Bstr bstrFlags;
5844
5845 ComPtr<IInternalSessionControl> directControl;
5846 {
5847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5848 if (mData->mSession.mLockType == LockType_VM)
5849 directControl = mData->mSession.mDirectControl;
5850 }
5851
5852 /* ignore calls made after #OnSessionEnd() is called */
5853 if (!directControl)
5854 rc = E_ACCESSDENIED;
5855 else
5856 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5857 0 /* accessMode */,
5858 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5859
5860 aValue = bstrValue;
5861 aFlags = bstrFlags;
5862
5863 return rc;
5864}
5865#endif // VBOX_WITH_GUEST_PROPS
5866
5867HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5868 com::Utf8Str &aValue,
5869 LONG64 *aTimestamp,
5870 com::Utf8Str &aFlags)
5871{
5872#ifndef VBOX_WITH_GUEST_PROPS
5873 ReturnComNotImplemented();
5874#else // VBOX_WITH_GUEST_PROPS
5875
5876 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5877
5878 if (rc == E_ACCESSDENIED)
5879 /* The VM is not running or the service is not (yet) accessible */
5880 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5881 return rc;
5882#endif // VBOX_WITH_GUEST_PROPS
5883}
5884
5885HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5886{
5887 LONG64 dummyTimestamp;
5888 com::Utf8Str dummyFlags;
5889 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5890 return rc;
5891
5892}
5893HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5894{
5895 com::Utf8Str dummyFlags;
5896 com::Utf8Str dummyValue;
5897 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5898 return rc;
5899}
5900
5901#ifdef VBOX_WITH_GUEST_PROPS
5902/**
5903 * Set a guest property in VBoxSVC's internal structures.
5904 */
5905HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5906 const com::Utf8Str &aFlags, bool fDelete)
5907{
5908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5909 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5910 if (FAILED(rc)) return rc;
5911
5912 try
5913 {
5914 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5915 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5916 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5917
5918 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
5919 return setError(E_INVALIDARG, tr("Properties with TRANSIENT or TRANSRESET flag cannot be set or modified if VM is not running"));
5920
5921 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5922 if (it == mHWData->mGuestProperties.end())
5923 {
5924 if (!fDelete)
5925 {
5926 i_setModified(IsModified_MachineData);
5927 mHWData.backupEx();
5928
5929 RTTIMESPEC time;
5930 HWData::GuestProperty prop;
5931 prop.strValue = aValue;
5932 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5933 prop.mFlags = fFlags;
5934 mHWData->mGuestProperties[aName] = prop;
5935 }
5936 }
5937 else
5938 {
5939 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5940 {
5941 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5942 }
5943 else
5944 {
5945 i_setModified(IsModified_MachineData);
5946 mHWData.backupEx();
5947
5948 /* The backupEx() operation invalidates our iterator,
5949 * so get a new one. */
5950 it = mHWData->mGuestProperties.find(aName);
5951 Assert(it != mHWData->mGuestProperties.end());
5952
5953 if (!fDelete)
5954 {
5955 RTTIMESPEC time;
5956 it->second.strValue = aValue;
5957 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5958 it->second.mFlags = fFlags;
5959 }
5960 else
5961 mHWData->mGuestProperties.erase(it);
5962 }
5963 }
5964
5965 if (SUCCEEDED(rc))
5966 {
5967 alock.release();
5968
5969 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fDelete);
5970 }
5971 }
5972 catch (std::bad_alloc &)
5973 {
5974 rc = E_OUTOFMEMORY;
5975 }
5976
5977 return rc;
5978}
5979
5980/**
5981 * Set a property on the VM that that property belongs to.
5982 * @returns E_ACCESSDENIED if the VM process is not available or not
5983 * currently handling queries and the setting should then be done in
5984 * VBoxSVC.
5985 */
5986HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5987 const com::Utf8Str &aFlags, bool fDelete)
5988{
5989 HRESULT rc;
5990
5991 try
5992 {
5993 ComPtr<IInternalSessionControl> directControl;
5994 {
5995 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5996 if (mData->mSession.mLockType == LockType_VM)
5997 directControl = mData->mSession.mDirectControl;
5998 }
5999
6000 Bstr dummy1; /* will not be changed (setter) */
6001 Bstr dummy2; /* will not be changed (setter) */
6002 LONG64 dummy64;
6003 if (!directControl)
6004 rc = E_ACCESSDENIED;
6005 else
6006 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6007 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
6008 fDelete ? 2 : 1 /* accessMode */,
6009 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
6010 }
6011 catch (std::bad_alloc &)
6012 {
6013 rc = E_OUTOFMEMORY;
6014 }
6015
6016 return rc;
6017}
6018#endif // VBOX_WITH_GUEST_PROPS
6019
6020HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
6021 const com::Utf8Str &aFlags)
6022{
6023#ifndef VBOX_WITH_GUEST_PROPS
6024 ReturnComNotImplemented();
6025#else // VBOX_WITH_GUEST_PROPS
6026
6027 int vrc = GuestPropValidateName(aProperty.c_str(), aProperty.length() + 1 /* '\0' */);
6028 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
6029
6030 vrc = GuestPropValidateValue(aValue.c_str(), aValue.length() + 1 /* '\0' */);
6031 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
6032
6033 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
6034 if (rc == E_ACCESSDENIED)
6035 /* The VM is not running or the service is not (yet) accessible */
6036 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
6037 return rc;
6038#endif // VBOX_WITH_GUEST_PROPS
6039}
6040
6041HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
6042{
6043 return setGuestProperty(aProperty, aValue, "");
6044}
6045
6046HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
6047{
6048#ifndef VBOX_WITH_GUEST_PROPS
6049 ReturnComNotImplemented();
6050#else // VBOX_WITH_GUEST_PROPS
6051 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
6052 if (rc == E_ACCESSDENIED)
6053 /* The VM is not running or the service is not (yet) accessible */
6054 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
6055 return rc;
6056#endif // VBOX_WITH_GUEST_PROPS
6057}
6058
6059#ifdef VBOX_WITH_GUEST_PROPS
6060/**
6061 * Enumerate the guest properties in VBoxSVC's internal structures.
6062 */
6063HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
6064 std::vector<com::Utf8Str> &aNames,
6065 std::vector<com::Utf8Str> &aValues,
6066 std::vector<LONG64> &aTimestamps,
6067 std::vector<com::Utf8Str> &aFlags)
6068{
6069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6070 Utf8Str strPatterns(aPatterns);
6071
6072 /*
6073 * Look for matching patterns and build up a list.
6074 */
6075 HWData::GuestPropertyMap propMap;
6076 for (HWData::GuestPropertyMap::const_iterator
6077 it = mHWData->mGuestProperties.begin();
6078 it != mHWData->mGuestProperties.end();
6079 ++it)
6080 {
6081 if ( strPatterns.isEmpty()
6082 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6083 RTSTR_MAX,
6084 it->first.c_str(),
6085 RTSTR_MAX,
6086 NULL)
6087 )
6088 propMap.insert(*it);
6089 }
6090
6091 alock.release();
6092
6093 /*
6094 * And build up the arrays for returning the property information.
6095 */
6096 size_t cEntries = propMap.size();
6097
6098 aNames.resize(cEntries);
6099 aValues.resize(cEntries);
6100 aTimestamps.resize(cEntries);
6101 aFlags.resize(cEntries);
6102
6103 size_t i = 0;
6104 for (HWData::GuestPropertyMap::const_iterator
6105 it = propMap.begin();
6106 it != propMap.end();
6107 ++it, ++i)
6108 {
6109 aNames[i] = it->first;
6110 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
6111 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
6112
6113 aValues[i] = it->second.strValue;
6114 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
6115 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
6116
6117 aTimestamps[i] = it->second.mTimestamp;
6118
6119 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6120 GuestPropWriteFlags(it->second.mFlags, szFlags);
6121 aFlags[i] = szFlags;
6122 }
6123
6124 return S_OK;
6125}
6126
6127/**
6128 * Enumerate the properties managed by a VM.
6129 * @returns E_ACCESSDENIED if the VM process is not available or not
6130 * currently handling queries and the setting should then be done in
6131 * VBoxSVC.
6132 */
6133HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6134 std::vector<com::Utf8Str> &aNames,
6135 std::vector<com::Utf8Str> &aValues,
6136 std::vector<LONG64> &aTimestamps,
6137 std::vector<com::Utf8Str> &aFlags)
6138{
6139 HRESULT rc;
6140 ComPtr<IInternalSessionControl> directControl;
6141 {
6142 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6143 if (mData->mSession.mLockType == LockType_VM)
6144 directControl = mData->mSession.mDirectControl;
6145 }
6146
6147 com::SafeArray<BSTR> bNames;
6148 com::SafeArray<BSTR> bValues;
6149 com::SafeArray<LONG64> bTimestamps;
6150 com::SafeArray<BSTR> bFlags;
6151
6152 if (!directControl)
6153 rc = E_ACCESSDENIED;
6154 else
6155 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6156 ComSafeArrayAsOutParam(bNames),
6157 ComSafeArrayAsOutParam(bValues),
6158 ComSafeArrayAsOutParam(bTimestamps),
6159 ComSafeArrayAsOutParam(bFlags));
6160 size_t i;
6161 aNames.resize(bNames.size());
6162 for (i = 0; i < bNames.size(); ++i)
6163 aNames[i] = Utf8Str(bNames[i]);
6164 aValues.resize(bValues.size());
6165 for (i = 0; i < bValues.size(); ++i)
6166 aValues[i] = Utf8Str(bValues[i]);
6167 aTimestamps.resize(bTimestamps.size());
6168 for (i = 0; i < bTimestamps.size(); ++i)
6169 aTimestamps[i] = bTimestamps[i];
6170 aFlags.resize(bFlags.size());
6171 for (i = 0; i < bFlags.size(); ++i)
6172 aFlags[i] = Utf8Str(bFlags[i]);
6173
6174 return rc;
6175}
6176#endif // VBOX_WITH_GUEST_PROPS
6177HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6178 std::vector<com::Utf8Str> &aNames,
6179 std::vector<com::Utf8Str> &aValues,
6180 std::vector<LONG64> &aTimestamps,
6181 std::vector<com::Utf8Str> &aFlags)
6182{
6183#ifndef VBOX_WITH_GUEST_PROPS
6184 ReturnComNotImplemented();
6185#else // VBOX_WITH_GUEST_PROPS
6186
6187 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6188
6189 if (rc == E_ACCESSDENIED)
6190 /* The VM is not running or the service is not (yet) accessible */
6191 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6192 return rc;
6193#endif // VBOX_WITH_GUEST_PROPS
6194}
6195
6196HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6197 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6198{
6199 MediumAttachmentList atts;
6200
6201 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6202 if (FAILED(rc)) return rc;
6203
6204 aMediumAttachments.resize(atts.size());
6205 size_t i = 0;
6206 for (MediumAttachmentList::const_iterator
6207 it = atts.begin();
6208 it != atts.end();
6209 ++it, ++i)
6210 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6211
6212 return S_OK;
6213}
6214
6215HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6216 LONG aControllerPort,
6217 LONG aDevice,
6218 ComPtr<IMediumAttachment> &aAttachment)
6219{
6220 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6221 aName.c_str(), aControllerPort, aDevice));
6222
6223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6224
6225 aAttachment = NULL;
6226
6227 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6228 aName,
6229 aControllerPort,
6230 aDevice);
6231 if (pAttach.isNull())
6232 return setError(VBOX_E_OBJECT_NOT_FOUND,
6233 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6234 aDevice, aControllerPort, aName.c_str());
6235
6236 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6237
6238 return S_OK;
6239}
6240
6241
6242HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6243 StorageBus_T aConnectionType,
6244 ComPtr<IStorageController> &aController)
6245{
6246 if ( (aConnectionType <= StorageBus_Null)
6247 || (aConnectionType > StorageBus_VirtioSCSI))
6248 return setError(E_INVALIDARG,
6249 tr("Invalid connection type: %d"),
6250 aConnectionType);
6251
6252 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6253
6254 HRESULT rc = i_checkStateDependency(MutableStateDep);
6255 if (FAILED(rc)) return rc;
6256
6257 /* try to find one with the name first. */
6258 ComObjPtr<StorageController> ctrl;
6259
6260 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6261 if (SUCCEEDED(rc))
6262 return setError(VBOX_E_OBJECT_IN_USE,
6263 tr("Storage controller named '%s' already exists"),
6264 aName.c_str());
6265
6266 ctrl.createObject();
6267
6268 /* get a new instance number for the storage controller */
6269 ULONG ulInstance = 0;
6270 bool fBootable = true;
6271 for (StorageControllerList::const_iterator
6272 it = mStorageControllers->begin();
6273 it != mStorageControllers->end();
6274 ++it)
6275 {
6276 if ((*it)->i_getStorageBus() == aConnectionType)
6277 {
6278 ULONG ulCurInst = (*it)->i_getInstance();
6279
6280 if (ulCurInst >= ulInstance)
6281 ulInstance = ulCurInst + 1;
6282
6283 /* Only one controller of each type can be marked as bootable. */
6284 if ((*it)->i_getBootable())
6285 fBootable = false;
6286 }
6287 }
6288
6289 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6290 if (FAILED(rc)) return rc;
6291
6292 i_setModified(IsModified_Storage);
6293 mStorageControllers.backup();
6294 mStorageControllers->push_back(ctrl);
6295
6296 ctrl.queryInterfaceTo(aController.asOutParam());
6297
6298 /* inform the direct session if any */
6299 alock.release();
6300 i_onStorageControllerChange(i_getId(), aName);
6301
6302 return S_OK;
6303}
6304
6305HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6306 ComPtr<IStorageController> &aStorageController)
6307{
6308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6309
6310 ComObjPtr<StorageController> ctrl;
6311
6312 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6313 if (SUCCEEDED(rc))
6314 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6315
6316 return rc;
6317}
6318
6319HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6320 ULONG aInstance,
6321 ComPtr<IStorageController> &aStorageController)
6322{
6323 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6324
6325 for (StorageControllerList::const_iterator
6326 it = mStorageControllers->begin();
6327 it != mStorageControllers->end();
6328 ++it)
6329 {
6330 if ( (*it)->i_getStorageBus() == aConnectionType
6331 && (*it)->i_getInstance() == aInstance)
6332 {
6333 (*it).queryInterfaceTo(aStorageController.asOutParam());
6334 return S_OK;
6335 }
6336 }
6337
6338 return setError(VBOX_E_OBJECT_NOT_FOUND,
6339 tr("Could not find a storage controller with instance number '%lu'"),
6340 aInstance);
6341}
6342
6343HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6344{
6345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6346
6347 HRESULT rc = i_checkStateDependency(MutableStateDep);
6348 if (FAILED(rc)) return rc;
6349
6350 ComObjPtr<StorageController> ctrl;
6351
6352 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6353 if (SUCCEEDED(rc))
6354 {
6355 /* Ensure that only one controller of each type is marked as bootable. */
6356 if (aBootable == TRUE)
6357 {
6358 for (StorageControllerList::const_iterator
6359 it = mStorageControllers->begin();
6360 it != mStorageControllers->end();
6361 ++it)
6362 {
6363 ComObjPtr<StorageController> aCtrl = (*it);
6364
6365 if ( (aCtrl->i_getName() != aName)
6366 && aCtrl->i_getBootable() == TRUE
6367 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6368 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6369 {
6370 aCtrl->i_setBootable(FALSE);
6371 break;
6372 }
6373 }
6374 }
6375
6376 if (SUCCEEDED(rc))
6377 {
6378 ctrl->i_setBootable(aBootable);
6379 i_setModified(IsModified_Storage);
6380 }
6381 }
6382
6383 if (SUCCEEDED(rc))
6384 {
6385 /* inform the direct session if any */
6386 alock.release();
6387 i_onStorageControllerChange(i_getId(), aName);
6388 }
6389
6390 return rc;
6391}
6392
6393HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6394{
6395 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6396
6397 HRESULT rc = i_checkStateDependency(MutableStateDep);
6398 if (FAILED(rc)) return rc;
6399
6400 ComObjPtr<StorageController> ctrl;
6401 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6402 if (FAILED(rc)) return rc;
6403
6404 MediumAttachmentList llDetachedAttachments;
6405 {
6406 /* find all attached devices to the appropriate storage controller and detach them all */
6407 // make a temporary list because detachDevice invalidates iterators into
6408 // mMediumAttachments
6409 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6410
6411 for (MediumAttachmentList::const_iterator
6412 it = llAttachments2.begin();
6413 it != llAttachments2.end();
6414 ++it)
6415 {
6416 MediumAttachment *pAttachTemp = *it;
6417
6418 AutoCaller localAutoCaller(pAttachTemp);
6419 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6420
6421 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6422
6423 if (pAttachTemp->i_getControllerName() == aName)
6424 {
6425 llDetachedAttachments.push_back(pAttachTemp);
6426 rc = i_detachDevice(pAttachTemp, alock, NULL);
6427 if (FAILED(rc)) return rc;
6428 }
6429 }
6430 }
6431
6432 /* send event about detached devices before removing parent controller */
6433 for (MediumAttachmentList::const_iterator
6434 it = llDetachedAttachments.begin();
6435 it != llDetachedAttachments.end();
6436 ++it)
6437 {
6438 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6439 }
6440
6441 /* We can remove it now. */
6442 i_setModified(IsModified_Storage);
6443 mStorageControllers.backup();
6444
6445 ctrl->i_unshare();
6446
6447 mStorageControllers->remove(ctrl);
6448
6449 /* inform the direct session if any */
6450 alock.release();
6451 i_onStorageControllerChange(i_getId(), aName);
6452
6453 return S_OK;
6454}
6455
6456HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6457 ComPtr<IUSBController> &aController)
6458{
6459 if ( (aType <= USBControllerType_Null)
6460 || (aType >= USBControllerType_Last))
6461 return setError(E_INVALIDARG,
6462 tr("Invalid USB controller type: %d"),
6463 aType);
6464
6465 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6466
6467 HRESULT rc = i_checkStateDependency(MutableStateDep);
6468 if (FAILED(rc)) return rc;
6469
6470 /* try to find one with the same type first. */
6471 ComObjPtr<USBController> ctrl;
6472
6473 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6474 if (SUCCEEDED(rc))
6475 return setError(VBOX_E_OBJECT_IN_USE,
6476 tr("USB controller named '%s' already exists"),
6477 aName.c_str());
6478
6479 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6480 ULONG maxInstances;
6481 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6482 if (FAILED(rc))
6483 return rc;
6484
6485 ULONG cInstances = i_getUSBControllerCountByType(aType);
6486 if (cInstances >= maxInstances)
6487 return setError(E_INVALIDARG,
6488 tr("Too many USB controllers of this type"));
6489
6490 ctrl.createObject();
6491
6492 rc = ctrl->init(this, aName, aType);
6493 if (FAILED(rc)) return rc;
6494
6495 i_setModified(IsModified_USB);
6496 mUSBControllers.backup();
6497 mUSBControllers->push_back(ctrl);
6498
6499 ctrl.queryInterfaceTo(aController.asOutParam());
6500
6501 /* inform the direct session if any */
6502 alock.release();
6503 i_onUSBControllerChange();
6504
6505 return S_OK;
6506}
6507
6508HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6509{
6510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6511
6512 ComObjPtr<USBController> ctrl;
6513
6514 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6515 if (SUCCEEDED(rc))
6516 ctrl.queryInterfaceTo(aController.asOutParam());
6517
6518 return rc;
6519}
6520
6521HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6522 ULONG *aControllers)
6523{
6524 if ( (aType <= USBControllerType_Null)
6525 || (aType >= USBControllerType_Last))
6526 return setError(E_INVALIDARG,
6527 tr("Invalid USB controller type: %d"),
6528 aType);
6529
6530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6531
6532 ComObjPtr<USBController> ctrl;
6533
6534 *aControllers = i_getUSBControllerCountByType(aType);
6535
6536 return S_OK;
6537}
6538
6539HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6540{
6541
6542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6543
6544 HRESULT rc = i_checkStateDependency(MutableStateDep);
6545 if (FAILED(rc)) return rc;
6546
6547 ComObjPtr<USBController> ctrl;
6548 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6549 if (FAILED(rc)) return rc;
6550
6551 i_setModified(IsModified_USB);
6552 mUSBControllers.backup();
6553
6554 ctrl->i_unshare();
6555
6556 mUSBControllers->remove(ctrl);
6557
6558 /* inform the direct session if any */
6559 alock.release();
6560 i_onUSBControllerChange();
6561
6562 return S_OK;
6563}
6564
6565HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6566 ULONG *aOriginX,
6567 ULONG *aOriginY,
6568 ULONG *aWidth,
6569 ULONG *aHeight,
6570 BOOL *aEnabled)
6571{
6572 uint32_t u32OriginX= 0;
6573 uint32_t u32OriginY= 0;
6574 uint32_t u32Width = 0;
6575 uint32_t u32Height = 0;
6576 uint16_t u16Flags = 0;
6577
6578#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6579 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6580#else
6581 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6582#endif
6583 int vrc = readSavedGuestScreenInfo(SavedStateStream, mSSData->strStateFilePath, aScreenId,
6584 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6585 if (RT_FAILURE(vrc))
6586 {
6587#ifdef RT_OS_WINDOWS
6588 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6589 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6590 * So just assign fEnable to TRUE again.
6591 * The right fix would be to change GUI API wrappers to make sure that parameters
6592 * are changed only if API succeeds.
6593 */
6594 *aEnabled = TRUE;
6595#endif
6596 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6597 tr("Saved guest size is not available (%Rrc)"),
6598 vrc);
6599 }
6600
6601 *aOriginX = u32OriginX;
6602 *aOriginY = u32OriginY;
6603 *aWidth = u32Width;
6604 *aHeight = u32Height;
6605 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6606
6607 return S_OK;
6608}
6609
6610HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6611 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6612{
6613 if (aScreenId != 0)
6614 return E_NOTIMPL;
6615
6616 if ( aBitmapFormat != BitmapFormat_BGR0
6617 && aBitmapFormat != BitmapFormat_BGRA
6618 && aBitmapFormat != BitmapFormat_RGBA
6619 && aBitmapFormat != BitmapFormat_PNG)
6620 return setError(E_NOTIMPL,
6621 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6622
6623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6624
6625 uint8_t *pu8Data = NULL;
6626 uint32_t cbData = 0;
6627 uint32_t u32Width = 0;
6628 uint32_t u32Height = 0;
6629
6630#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6631 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6632#else
6633 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6634#endif
6635 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 0 /* u32Type */,
6636 &pu8Data, &cbData, &u32Width, &u32Height);
6637 if (RT_FAILURE(vrc))
6638 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6639 tr("Saved thumbnail data is not available (%Rrc)"),
6640 vrc);
6641
6642 HRESULT hr = S_OK;
6643
6644 *aWidth = u32Width;
6645 *aHeight = u32Height;
6646
6647 if (cbData > 0)
6648 {
6649 /* Convert pixels to the format expected by the API caller. */
6650 if (aBitmapFormat == BitmapFormat_BGR0)
6651 {
6652 /* [0] B, [1] G, [2] R, [3] 0. */
6653 aData.resize(cbData);
6654 memcpy(&aData.front(), pu8Data, cbData);
6655 }
6656 else if (aBitmapFormat == BitmapFormat_BGRA)
6657 {
6658 /* [0] B, [1] G, [2] R, [3] A. */
6659 aData.resize(cbData);
6660 for (uint32_t i = 0; i < cbData; i += 4)
6661 {
6662 aData[i] = pu8Data[i];
6663 aData[i + 1] = pu8Data[i + 1];
6664 aData[i + 2] = pu8Data[i + 2];
6665 aData[i + 3] = 0xff;
6666 }
6667 }
6668 else if (aBitmapFormat == BitmapFormat_RGBA)
6669 {
6670 /* [0] R, [1] G, [2] B, [3] A. */
6671 aData.resize(cbData);
6672 for (uint32_t i = 0; i < cbData; i += 4)
6673 {
6674 aData[i] = pu8Data[i + 2];
6675 aData[i + 1] = pu8Data[i + 1];
6676 aData[i + 2] = pu8Data[i];
6677 aData[i + 3] = 0xff;
6678 }
6679 }
6680 else if (aBitmapFormat == BitmapFormat_PNG)
6681 {
6682 uint8_t *pu8PNG = NULL;
6683 uint32_t cbPNG = 0;
6684 uint32_t cxPNG = 0;
6685 uint32_t cyPNG = 0;
6686
6687 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6688
6689 if (RT_SUCCESS(vrc))
6690 {
6691 aData.resize(cbPNG);
6692 if (cbPNG)
6693 memcpy(&aData.front(), pu8PNG, cbPNG);
6694 }
6695 else
6696 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6697 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6698 vrc);
6699
6700 RTMemFree(pu8PNG);
6701 }
6702 }
6703
6704 freeSavedDisplayScreenshot(pu8Data);
6705
6706 return hr;
6707}
6708
6709HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6710 ULONG *aWidth,
6711 ULONG *aHeight,
6712 std::vector<BitmapFormat_T> &aBitmapFormats)
6713{
6714 if (aScreenId != 0)
6715 return E_NOTIMPL;
6716
6717 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6718
6719 uint8_t *pu8Data = NULL;
6720 uint32_t cbData = 0;
6721 uint32_t u32Width = 0;
6722 uint32_t u32Height = 0;
6723
6724#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6725 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6726#else
6727 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6728#endif
6729 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6730 &pu8Data, &cbData, &u32Width, &u32Height);
6731
6732 if (RT_FAILURE(vrc))
6733 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6734 tr("Saved screenshot data is not available (%Rrc)"),
6735 vrc);
6736
6737 *aWidth = u32Width;
6738 *aHeight = u32Height;
6739 aBitmapFormats.resize(1);
6740 aBitmapFormats[0] = BitmapFormat_PNG;
6741
6742 freeSavedDisplayScreenshot(pu8Data);
6743
6744 return S_OK;
6745}
6746
6747HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6748 BitmapFormat_T aBitmapFormat,
6749 ULONG *aWidth,
6750 ULONG *aHeight,
6751 std::vector<BYTE> &aData)
6752{
6753 if (aScreenId != 0)
6754 return E_NOTIMPL;
6755
6756 if (aBitmapFormat != BitmapFormat_PNG)
6757 return E_NOTIMPL;
6758
6759 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6760
6761 uint8_t *pu8Data = NULL;
6762 uint32_t cbData = 0;
6763 uint32_t u32Width = 0;
6764 uint32_t u32Height = 0;
6765
6766#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6767 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6768#else
6769 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6770#endif
6771 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6772 &pu8Data, &cbData, &u32Width, &u32Height);
6773
6774 if (RT_FAILURE(vrc))
6775 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6776 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6777 vrc);
6778
6779 *aWidth = u32Width;
6780 *aHeight = u32Height;
6781
6782 aData.resize(cbData);
6783 if (cbData)
6784 memcpy(&aData.front(), pu8Data, cbData);
6785
6786 freeSavedDisplayScreenshot(pu8Data);
6787
6788 return S_OK;
6789}
6790
6791HRESULT Machine::hotPlugCPU(ULONG aCpu)
6792{
6793 HRESULT rc = S_OK;
6794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6795
6796 if (!mHWData->mCPUHotPlugEnabled)
6797 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6798
6799 if (aCpu >= mHWData->mCPUCount)
6800 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6801
6802 if (mHWData->mCPUAttached[aCpu])
6803 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6804
6805 rc = i_checkStateDependency(MutableOrRunningStateDep);
6806 if (FAILED(rc)) return rc;
6807
6808 alock.release();
6809 rc = i_onCPUChange(aCpu, false);
6810 alock.acquire();
6811 if (FAILED(rc)) return rc;
6812
6813 i_setModified(IsModified_MachineData);
6814 mHWData.backup();
6815 mHWData->mCPUAttached[aCpu] = true;
6816
6817 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6818 if (Global::IsOnline(mData->mMachineState))
6819 i_saveSettings(NULL, alock);
6820
6821 return S_OK;
6822}
6823
6824HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6825{
6826 HRESULT rc = S_OK;
6827
6828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6829
6830 if (!mHWData->mCPUHotPlugEnabled)
6831 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6832
6833 if (aCpu >= SchemaDefs::MaxCPUCount)
6834 return setError(E_INVALIDARG,
6835 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6836 SchemaDefs::MaxCPUCount);
6837
6838 if (!mHWData->mCPUAttached[aCpu])
6839 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6840
6841 /* CPU 0 can't be detached */
6842 if (aCpu == 0)
6843 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6844
6845 rc = i_checkStateDependency(MutableOrRunningStateDep);
6846 if (FAILED(rc)) return rc;
6847
6848 alock.release();
6849 rc = i_onCPUChange(aCpu, true);
6850 alock.acquire();
6851 if (FAILED(rc)) return rc;
6852
6853 i_setModified(IsModified_MachineData);
6854 mHWData.backup();
6855 mHWData->mCPUAttached[aCpu] = false;
6856
6857 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6858 if (Global::IsOnline(mData->mMachineState))
6859 i_saveSettings(NULL, alock);
6860
6861 return S_OK;
6862}
6863
6864HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6865{
6866 *aAttached = false;
6867
6868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6869
6870 /* If hotplug is enabled the CPU is always enabled. */
6871 if (!mHWData->mCPUHotPlugEnabled)
6872 {
6873 if (aCpu < mHWData->mCPUCount)
6874 *aAttached = true;
6875 }
6876 else
6877 {
6878 if (aCpu < SchemaDefs::MaxCPUCount)
6879 *aAttached = mHWData->mCPUAttached[aCpu];
6880 }
6881
6882 return S_OK;
6883}
6884
6885HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6886{
6887 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6888
6889 Utf8Str log = i_getLogFilename(aIdx);
6890 if (!RTFileExists(log.c_str()))
6891 log.setNull();
6892 aFilename = log;
6893
6894 return S_OK;
6895}
6896
6897HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6898{
6899 if (aSize < 0)
6900 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6901
6902 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6903
6904 HRESULT rc = S_OK;
6905 Utf8Str log = i_getLogFilename(aIdx);
6906
6907 /* do not unnecessarily hold the lock while doing something which does
6908 * not need the lock and potentially takes a long time. */
6909 alock.release();
6910
6911 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6912 * keeps the SOAP reply size under 1M for the webservice (we're using
6913 * base64 encoded strings for binary data for years now, avoiding the
6914 * expansion of each byte array element to approx. 25 bytes of XML. */
6915 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6916 aData.resize(cbData);
6917
6918 int vrc = VINF_SUCCESS;
6919 RTVFSIOSTREAM hVfsIosLog = NIL_RTVFSIOSTREAM;
6920
6921#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6922 if (mData->mstrLogKeyId.isNotEmpty() && mData->mstrLogKeyStore.isNotEmpty())
6923 {
6924 PCVBOXCRYPTOIF pCryptoIf = NULL;
6925 rc = i_getVirtualBox()->i_retainCryptoIf(&pCryptoIf);
6926 if (SUCCEEDED(rc))
6927 {
6928 alock.acquire();
6929
6930 SecretKey *pKey = NULL;
6931 vrc = mData->mpKeyStore->retainSecretKey(mData->mstrLogKeyId, &pKey);
6932 alock.release();
6933
6934 if (RT_SUCCESS(vrc))
6935 {
6936 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6937 if (RT_SUCCESS(vrc))
6938 {
6939 RTVFSIOSTREAM hVfsIosLogDec = NIL_RTVFSIOSTREAM;
6940 vrc = pCryptoIf->pfnCryptoIoStrmFromVfsIoStrmDecrypt(hVfsIosLog, mData->mstrLogKeyStore.c_str(),
6941 (const char *)pKey->getKeyBuffer(), &hVfsIosLogDec);
6942 if (RT_SUCCESS(vrc))
6943 {
6944 RTVfsIoStrmRelease(hVfsIosLog);
6945 hVfsIosLog = hVfsIosLogDec;
6946 }
6947 }
6948
6949 pKey->release();
6950 }
6951
6952 i_getVirtualBox()->i_releaseCryptoIf(pCryptoIf);
6953 }
6954 }
6955 else
6956 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6957#else
6958 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6959#endif
6960 if (RT_SUCCESS(vrc))
6961 {
6962 vrc = RTVfsIoStrmReadAt(hVfsIosLog, aOffset,
6963 cbData ? &aData.front() : NULL, cbData,
6964 true /*fBlocking*/, &cbData);
6965 if (RT_SUCCESS(vrc))
6966 aData.resize(cbData);
6967 else
6968 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6969 tr("Could not read log file '%s' (%Rrc)"),
6970 log.c_str(), vrc);
6971
6972 RTVfsIoStrmRelease(hVfsIosLog);
6973 }
6974 else
6975 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6976 tr("Could not open log file '%s' (%Rrc)"),
6977 log.c_str(), vrc);
6978
6979 if (FAILED(rc))
6980 aData.resize(0);
6981
6982 return rc;
6983}
6984
6985
6986/**
6987 * Currently this method doesn't attach device to the running VM,
6988 * just makes sure it's plugged on next VM start.
6989 */
6990HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6991{
6992 // lock scope
6993 {
6994 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6995
6996 HRESULT rc = i_checkStateDependency(MutableStateDep);
6997 if (FAILED(rc)) return rc;
6998
6999 ChipsetType_T aChipset = ChipsetType_PIIX3;
7000 COMGETTER(ChipsetType)(&aChipset);
7001
7002 if (aChipset != ChipsetType_ICH9)
7003 {
7004 return setError(E_INVALIDARG,
7005 tr("Host PCI attachment only supported with ICH9 chipset"));
7006 }
7007
7008 // check if device with this host PCI address already attached
7009 for (HWData::PCIDeviceAssignmentList::const_iterator
7010 it = mHWData->mPCIDeviceAssignments.begin();
7011 it != mHWData->mPCIDeviceAssignments.end();
7012 ++it)
7013 {
7014 LONG iHostAddress = -1;
7015 ComPtr<PCIDeviceAttachment> pAttach;
7016 pAttach = *it;
7017 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7018 if (iHostAddress == aHostAddress)
7019 return setError(E_INVALIDARG,
7020 tr("Device with host PCI address already attached to this VM"));
7021 }
7022
7023 ComObjPtr<PCIDeviceAttachment> pda;
7024 char name[32];
7025
7026 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
7027 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
7028 pda.createObject();
7029 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
7030 i_setModified(IsModified_MachineData);
7031 mHWData.backup();
7032 mHWData->mPCIDeviceAssignments.push_back(pda);
7033 }
7034
7035 return S_OK;
7036}
7037
7038/**
7039 * Currently this method doesn't detach device from the running VM,
7040 * just makes sure it's not plugged on next VM start.
7041 */
7042HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
7043{
7044 ComObjPtr<PCIDeviceAttachment> pAttach;
7045 bool fRemoved = false;
7046 HRESULT rc;
7047
7048 // lock scope
7049 {
7050 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7051
7052 rc = i_checkStateDependency(MutableStateDep);
7053 if (FAILED(rc)) return rc;
7054
7055 for (HWData::PCIDeviceAssignmentList::const_iterator
7056 it = mHWData->mPCIDeviceAssignments.begin();
7057 it != mHWData->mPCIDeviceAssignments.end();
7058 ++it)
7059 {
7060 LONG iHostAddress = -1;
7061 pAttach = *it;
7062 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7063 if (iHostAddress != -1 && iHostAddress == aHostAddress)
7064 {
7065 i_setModified(IsModified_MachineData);
7066 mHWData.backup();
7067 mHWData->mPCIDeviceAssignments.remove(pAttach);
7068 fRemoved = true;
7069 break;
7070 }
7071 }
7072 }
7073
7074
7075 /* Fire event outside of the lock */
7076 if (fRemoved)
7077 {
7078 Assert(!pAttach.isNull());
7079 ComPtr<IEventSource> es;
7080 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7081 Assert(SUCCEEDED(rc));
7082 Bstr mid;
7083 rc = this->COMGETTER(Id)(mid.asOutParam());
7084 Assert(SUCCEEDED(rc));
7085 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7086 }
7087
7088 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7089 tr("No host PCI device %08x attached"),
7090 aHostAddress
7091 );
7092}
7093
7094HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
7095{
7096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7097
7098 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
7099 size_t i = 0;
7100 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
7101 it = mHWData->mPCIDeviceAssignments.begin();
7102 it != mHWData->mPCIDeviceAssignments.end();
7103 ++it, ++i)
7104 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
7105
7106 return S_OK;
7107}
7108
7109HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
7110{
7111 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
7112
7113 return S_OK;
7114}
7115
7116HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
7117{
7118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7119
7120 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
7121
7122 return S_OK;
7123}
7124
7125HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
7126{
7127 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7128 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7129 if (SUCCEEDED(hrc))
7130 {
7131 hrc = mHWData.backupEx();
7132 if (SUCCEEDED(hrc))
7133 {
7134 i_setModified(IsModified_MachineData);
7135 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
7136 }
7137 }
7138 return hrc;
7139}
7140
7141HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
7142{
7143 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7144 aTracingConfig = mHWData->mDebugging.strTracingConfig;
7145 return S_OK;
7146}
7147
7148HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
7149{
7150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7151 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7152 if (SUCCEEDED(hrc))
7153 {
7154 hrc = mHWData.backupEx();
7155 if (SUCCEEDED(hrc))
7156 {
7157 mHWData->mDebugging.strTracingConfig = aTracingConfig;
7158 if (SUCCEEDED(hrc))
7159 i_setModified(IsModified_MachineData);
7160 }
7161 }
7162 return hrc;
7163}
7164
7165HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
7166{
7167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7168
7169 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
7170
7171 return S_OK;
7172}
7173
7174HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
7175{
7176 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7177 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7178 if (SUCCEEDED(hrc))
7179 {
7180 hrc = mHWData.backupEx();
7181 if (SUCCEEDED(hrc))
7182 {
7183 i_setModified(IsModified_MachineData);
7184 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
7185 }
7186 }
7187 return hrc;
7188}
7189
7190HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7191{
7192 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7193
7194 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7195
7196 return S_OK;
7197}
7198
7199HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7200{
7201 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7202
7203 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7204 if ( SUCCEEDED(hrc)
7205 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7206 {
7207 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7208 int vrc;
7209
7210 if (aAutostartEnabled)
7211 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7212 else
7213 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7214
7215 if (RT_SUCCESS(vrc))
7216 {
7217 hrc = mHWData.backupEx();
7218 if (SUCCEEDED(hrc))
7219 {
7220 i_setModified(IsModified_MachineData);
7221 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7222 }
7223 }
7224 else if (vrc == VERR_NOT_SUPPORTED)
7225 hrc = setError(VBOX_E_NOT_SUPPORTED,
7226 tr("The VM autostart feature is not supported on this platform"));
7227 else if (vrc == VERR_PATH_NOT_FOUND)
7228 hrc = setError(E_FAIL,
7229 tr("The path to the autostart database is not set"));
7230 else
7231 hrc = setError(E_UNEXPECTED,
7232 aAutostartEnabled ?
7233 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
7234 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
7235 mUserData->s.strName.c_str(), vrc);
7236 }
7237 return hrc;
7238}
7239
7240HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7241{
7242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7243
7244 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7245
7246 return S_OK;
7247}
7248
7249HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7250{
7251 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7252 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7253 if (SUCCEEDED(hrc))
7254 {
7255 hrc = mHWData.backupEx();
7256 if (SUCCEEDED(hrc))
7257 {
7258 i_setModified(IsModified_MachineData);
7259 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7260 }
7261 }
7262 return hrc;
7263}
7264
7265HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7266{
7267 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7268
7269 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7270
7271 return S_OK;
7272}
7273
7274HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7275{
7276 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7277 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7278 if ( SUCCEEDED(hrc)
7279 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7280 {
7281 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7282 int vrc;
7283
7284 if (aAutostopType != AutostopType_Disabled)
7285 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7286 else
7287 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7288
7289 if (RT_SUCCESS(vrc))
7290 {
7291 hrc = mHWData.backupEx();
7292 if (SUCCEEDED(hrc))
7293 {
7294 i_setModified(IsModified_MachineData);
7295 mHWData->mAutostart.enmAutostopType = aAutostopType;
7296 }
7297 }
7298 else if (vrc == VERR_NOT_SUPPORTED)
7299 hrc = setError(VBOX_E_NOT_SUPPORTED,
7300 tr("The VM autostop feature is not supported on this platform"));
7301 else if (vrc == VERR_PATH_NOT_FOUND)
7302 hrc = setError(E_FAIL,
7303 tr("The path to the autostart database is not set"));
7304 else
7305 hrc = setError(E_UNEXPECTED,
7306 aAutostopType != AutostopType_Disabled ?
7307 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
7308 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
7309 mUserData->s.strName.c_str(), vrc);
7310 }
7311 return hrc;
7312}
7313
7314HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7315{
7316 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7317
7318 aDefaultFrontend = mHWData->mDefaultFrontend;
7319
7320 return S_OK;
7321}
7322
7323HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7324{
7325 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7326 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7327 if (SUCCEEDED(hrc))
7328 {
7329 hrc = mHWData.backupEx();
7330 if (SUCCEEDED(hrc))
7331 {
7332 i_setModified(IsModified_MachineData);
7333 mHWData->mDefaultFrontend = aDefaultFrontend;
7334 }
7335 }
7336 return hrc;
7337}
7338
7339HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7340{
7341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7342 size_t cbIcon = mUserData->s.ovIcon.size();
7343 aIcon.resize(cbIcon);
7344 if (cbIcon)
7345 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7346 return S_OK;
7347}
7348
7349HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7350{
7351 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7352 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7353 if (SUCCEEDED(hrc))
7354 {
7355 i_setModified(IsModified_MachineData);
7356 mUserData.backup();
7357 size_t cbIcon = aIcon.size();
7358 mUserData->s.ovIcon.resize(cbIcon);
7359 if (cbIcon)
7360 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7361 }
7362 return hrc;
7363}
7364
7365HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7366{
7367#ifdef VBOX_WITH_USB
7368 *aUSBProxyAvailable = true;
7369#else
7370 *aUSBProxyAvailable = false;
7371#endif
7372 return S_OK;
7373}
7374
7375HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
7376{
7377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7378
7379 *aVMProcessPriority = mUserData->s.enmVMPriority;
7380
7381 return S_OK;
7382}
7383
7384HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
7385{
7386 RT_NOREF(aVMProcessPriority);
7387 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7388 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7389 if (SUCCEEDED(hrc))
7390 {
7391 hrc = mUserData.backupEx();
7392 if (SUCCEEDED(hrc))
7393 {
7394 i_setModified(IsModified_MachineData);
7395 mUserData->s.enmVMPriority = aVMProcessPriority;
7396 }
7397 }
7398 alock.release();
7399 if (SUCCEEDED(hrc))
7400 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
7401 return hrc;
7402}
7403
7404HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7405 ComPtr<IProgress> &aProgress)
7406{
7407 ComObjPtr<Progress> pP;
7408 Progress *ppP = pP;
7409 IProgress *iP = static_cast<IProgress *>(ppP);
7410 IProgress **pProgress = &iP;
7411
7412 IMachine *pTarget = aTarget;
7413
7414 /* Convert the options. */
7415 RTCList<CloneOptions_T> optList;
7416 if (aOptions.size())
7417 for (size_t i = 0; i < aOptions.size(); ++i)
7418 optList.append(aOptions[i]);
7419
7420 if (optList.contains(CloneOptions_Link))
7421 {
7422 if (!i_isSnapshotMachine())
7423 return setError(E_INVALIDARG,
7424 tr("Linked clone can only be created from a snapshot"));
7425 if (aMode != CloneMode_MachineState)
7426 return setError(E_INVALIDARG,
7427 tr("Linked clone can only be created for a single machine state"));
7428 }
7429 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7430
7431 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7432
7433 HRESULT rc = pWorker->start(pProgress);
7434
7435 pP = static_cast<Progress *>(*pProgress);
7436 pP.queryInterfaceTo(aProgress.asOutParam());
7437
7438 return rc;
7439
7440}
7441
7442HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7443 const com::Utf8Str &aType,
7444 ComPtr<IProgress> &aProgress)
7445{
7446 LogFlowThisFuncEnter();
7447
7448 ComObjPtr<Progress> ptrProgress;
7449 HRESULT hrc = ptrProgress.createObject();
7450 if (SUCCEEDED(hrc))
7451 {
7452 com::Utf8Str strDefaultPath;
7453 if (aTargetPath.isEmpty())
7454 i_calculateFullPath(".", strDefaultPath);
7455
7456 /* Initialize our worker task */
7457 MachineMoveVM *pTask = NULL;
7458 try
7459 {
7460 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7461 }
7462 catch (std::bad_alloc &)
7463 {
7464 return E_OUTOFMEMORY;
7465 }
7466
7467 hrc = pTask->init();//no exceptions are thrown
7468
7469 if (SUCCEEDED(hrc))
7470 {
7471 hrc = pTask->createThread();
7472 pTask = NULL; /* Consumed by createThread(). */
7473 if (SUCCEEDED(hrc))
7474 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7475 else
7476 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7477 }
7478 else
7479 delete pTask;
7480 }
7481
7482 LogFlowThisFuncLeave();
7483 return hrc;
7484
7485}
7486
7487HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7488{
7489 NOREF(aProgress);
7490 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7491
7492 // This check should always fail.
7493 HRESULT rc = i_checkStateDependency(MutableStateDep);
7494 if (FAILED(rc)) return rc;
7495
7496 AssertFailedReturn(E_NOTIMPL);
7497}
7498
7499HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7500{
7501 NOREF(aSavedStateFile);
7502 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7503
7504 // This check should always fail.
7505 HRESULT rc = i_checkStateDependency(MutableStateDep);
7506 if (FAILED(rc)) return rc;
7507
7508 AssertFailedReturn(E_NOTIMPL);
7509}
7510
7511HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7512{
7513 NOREF(aFRemoveFile);
7514 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7515
7516 // This check should always fail.
7517 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7518 if (FAILED(rc)) return rc;
7519
7520 AssertFailedReturn(E_NOTIMPL);
7521}
7522
7523// public methods for internal purposes
7524/////////////////////////////////////////////////////////////////////////////
7525
7526/**
7527 * Adds the given IsModified_* flag to the dirty flags of the machine.
7528 * This must be called either during i_loadSettings or under the machine write lock.
7529 * @param fl Flag
7530 * @param fAllowStateModification If state modifications are allowed.
7531 */
7532void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7533{
7534 mData->flModifications |= fl;
7535 if (fAllowStateModification && i_isStateModificationAllowed())
7536 mData->mCurrentStateModified = true;
7537}
7538
7539/**
7540 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7541 * care of the write locking.
7542 *
7543 * @param fModification The flag to add.
7544 * @param fAllowStateModification If state modifications are allowed.
7545 */
7546void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7547{
7548 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7549 i_setModified(fModification, fAllowStateModification);
7550}
7551
7552/**
7553 * Saves the registry entry of this machine to the given configuration node.
7554 *
7555 * @param data Machine registry data.
7556 *
7557 * @note locks this object for reading.
7558 */
7559HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7560{
7561 AutoLimitedCaller autoCaller(this);
7562 AssertComRCReturnRC(autoCaller.rc());
7563
7564 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7565
7566 data.uuid = mData->mUuid;
7567 data.strSettingsFile = mData->m_strConfigFile;
7568
7569 return S_OK;
7570}
7571
7572/**
7573 * Calculates the absolute path of the given path taking the directory of the
7574 * machine settings file as the current directory.
7575 *
7576 * @param strPath Path to calculate the absolute path for.
7577 * @param aResult Where to put the result (used only on success, can be the
7578 * same Utf8Str instance as passed in @a aPath).
7579 * @return IPRT result.
7580 *
7581 * @note Locks this object for reading.
7582 */
7583int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7584{
7585 AutoCaller autoCaller(this);
7586 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7587
7588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7589
7590 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7591
7592 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7593
7594 strSettingsDir.stripFilename();
7595 char szFolder[RTPATH_MAX];
7596 size_t cbFolder = sizeof(szFolder);
7597 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7598 if (RT_SUCCESS(vrc))
7599 aResult = szFolder;
7600
7601 return vrc;
7602}
7603
7604/**
7605 * Copies strSource to strTarget, making it relative to the machine folder
7606 * if it is a subdirectory thereof, or simply copying it otherwise.
7607 *
7608 * @param strSource Path to evaluate and copy.
7609 * @param strTarget Buffer to receive target path.
7610 *
7611 * @note Locks this object for reading.
7612 */
7613void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7614 Utf8Str &strTarget)
7615{
7616 AutoCaller autoCaller(this);
7617 AssertComRCReturn(autoCaller.rc(), (void)0);
7618
7619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7620
7621 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7622 // use strTarget as a temporary buffer to hold the machine settings dir
7623 strTarget = mData->m_strConfigFileFull;
7624 strTarget.stripFilename();
7625 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7626 {
7627 // is relative: then append what's left
7628 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7629 // for empty paths (only possible for subdirs) use "." to avoid
7630 // triggering default settings for not present config attributes.
7631 if (strTarget.isEmpty())
7632 strTarget = ".";
7633 }
7634 else
7635 // is not relative: then overwrite
7636 strTarget = strSource;
7637}
7638
7639/**
7640 * Returns the full path to the machine's log folder in the
7641 * \a aLogFolder argument.
7642 */
7643void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7644{
7645 AutoCaller autoCaller(this);
7646 AssertComRCReturnVoid(autoCaller.rc());
7647
7648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7649
7650 char szTmp[RTPATH_MAX];
7651 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7652 if (RT_SUCCESS(vrc))
7653 {
7654 if (szTmp[0] && !mUserData.isNull())
7655 {
7656 char szTmp2[RTPATH_MAX];
7657 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7658 if (RT_SUCCESS(vrc))
7659 aLogFolder.printf("%s%c%s",
7660 szTmp2,
7661 RTPATH_DELIMITER,
7662 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7663 }
7664 else
7665 vrc = VERR_PATH_IS_RELATIVE;
7666 }
7667
7668 if (RT_FAILURE(vrc))
7669 {
7670 // fallback if VBOX_USER_LOGHOME is not set or invalid
7671 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7672 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7673 aLogFolder.append(RTPATH_DELIMITER);
7674 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7675 }
7676}
7677
7678/**
7679 * Returns the full path to the machine's log file for an given index.
7680 */
7681Utf8Str Machine::i_getLogFilename(ULONG idx)
7682{
7683 Utf8Str logFolder;
7684 getLogFolder(logFolder);
7685 Assert(logFolder.length());
7686
7687 Utf8Str log;
7688 if (idx == 0)
7689 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7690#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7691 else if (idx == 1)
7692 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7693 else
7694 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7695#else
7696 else
7697 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7698#endif
7699 return log;
7700}
7701
7702/**
7703 * Returns the full path to the machine's hardened log file.
7704 */
7705Utf8Str Machine::i_getHardeningLogFilename(void)
7706{
7707 Utf8Str strFilename;
7708 getLogFolder(strFilename);
7709 Assert(strFilename.length());
7710 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7711 return strFilename;
7712}
7713
7714/**
7715 * Returns the default NVRAM filename based on the location of the VM config.
7716 * Note that this is a relative path.
7717 */
7718Utf8Str Machine::i_getDefaultNVRAMFilename()
7719{
7720 AutoCaller autoCaller(this);
7721 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7722
7723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7724
7725 if (i_isSnapshotMachine())
7726 return Utf8Str::Empty;
7727
7728 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7729 strNVRAMFilePath.stripPath();
7730 strNVRAMFilePath.stripSuffix();
7731 strNVRAMFilePath += ".nvram";
7732
7733 return strNVRAMFilePath;
7734}
7735
7736/**
7737 * Returns the NVRAM filename for a new snapshot. This intentionally works
7738 * similarly to the saved state file naming. Note that this is usually
7739 * a relative path, unless the snapshot folder is absolute.
7740 */
7741Utf8Str Machine::i_getSnapshotNVRAMFilename()
7742{
7743 AutoCaller autoCaller(this);
7744 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7745
7746 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7747
7748 RTTIMESPEC ts;
7749 RTTimeNow(&ts);
7750 RTTIME time;
7751 RTTimeExplode(&time, &ts);
7752
7753 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7754 strNVRAMFilePath += RTPATH_DELIMITER;
7755 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7756 time.i32Year, time.u8Month, time.u8MonthDay,
7757 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7758
7759 return strNVRAMFilePath;
7760}
7761
7762/**
7763 * Returns the version of the settings file.
7764 */
7765SettingsVersion_T Machine::i_getSettingsVersion(void)
7766{
7767 AutoCaller autoCaller(this);
7768 AssertComRCReturn(autoCaller.rc(), SettingsVersion_Null);
7769
7770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7771
7772 return mData->pMachineConfigFile->getSettingsVersion();
7773}
7774
7775/**
7776 * Composes a unique saved state filename based on the current system time. The filename is
7777 * granular to the second so this will work so long as no more than one snapshot is taken on
7778 * a machine per second.
7779 *
7780 * Before version 4.1, we used this formula for saved state files:
7781 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7782 * which no longer works because saved state files can now be shared between the saved state of the
7783 * "saved" machine and an online snapshot, and the following would cause problems:
7784 * 1) save machine
7785 * 2) create online snapshot from that machine state --> reusing saved state file
7786 * 3) save machine again --> filename would be reused, breaking the online snapshot
7787 *
7788 * So instead we now use a timestamp.
7789 *
7790 * @param strStateFilePath
7791 */
7792
7793void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7794{
7795 AutoCaller autoCaller(this);
7796 AssertComRCReturnVoid(autoCaller.rc());
7797
7798 {
7799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7800 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7801 }
7802
7803 RTTIMESPEC ts;
7804 RTTimeNow(&ts);
7805 RTTIME time;
7806 RTTimeExplode(&time, &ts);
7807
7808 strStateFilePath += RTPATH_DELIMITER;
7809 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7810 time.i32Year, time.u8Month, time.u8MonthDay,
7811 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7812}
7813
7814/**
7815 * Returns whether at least one USB controller is present for the VM.
7816 */
7817bool Machine::i_isUSBControllerPresent()
7818{
7819 AutoCaller autoCaller(this);
7820 AssertComRCReturn(autoCaller.rc(), false);
7821
7822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7823
7824 return (mUSBControllers->size() > 0);
7825}
7826
7827
7828/**
7829 * @note Locks this object for writing, calls the client process
7830 * (inside the lock).
7831 */
7832HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7833 const Utf8Str &strFrontend,
7834 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7835 ProgressProxy *aProgress)
7836{
7837 LogFlowThisFuncEnter();
7838
7839 AssertReturn(aControl, E_FAIL);
7840 AssertReturn(aProgress, E_FAIL);
7841 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7842
7843 AutoCaller autoCaller(this);
7844 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7845
7846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7847
7848 if (!mData->mRegistered)
7849 return setError(E_UNEXPECTED,
7850 tr("The machine '%s' is not registered"),
7851 mUserData->s.strName.c_str());
7852
7853 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7854
7855 /* The process started when launching a VM with separate UI/VM processes is always
7856 * the UI process, i.e. needs special handling as it won't claim the session. */
7857 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7858
7859 if (fSeparate)
7860 {
7861 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7862 return setError(VBOX_E_INVALID_OBJECT_STATE,
7863 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7864 mUserData->s.strName.c_str());
7865 }
7866 else
7867 {
7868 if ( mData->mSession.mState == SessionState_Locked
7869 || mData->mSession.mState == SessionState_Spawning
7870 || mData->mSession.mState == SessionState_Unlocking)
7871 return setError(VBOX_E_INVALID_OBJECT_STATE,
7872 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7873 mUserData->s.strName.c_str());
7874
7875 /* may not be busy */
7876 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7877 }
7878
7879 /* Hardening logging */
7880#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7881 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7882 {
7883 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7884 int vrc2;
7885 /* ignore rc */ i_deleteFile(strHardeningLogFile, false /* fIgnoreFailures */, tr("hardening log file"), &vrc2);
7886 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7887 {
7888 Utf8Str strStartupLogDir = strHardeningLogFile;
7889 strStartupLogDir.stripFilename();
7890 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7891 file without stripping the file. */
7892 }
7893 strSupHardeningLogArg.append(strHardeningLogFile);
7894
7895 /* Remove legacy log filename to avoid confusion. */
7896 Utf8Str strOldStartupLogFile;
7897 getLogFolder(strOldStartupLogFile);
7898 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7899 i_deleteFile(strOldStartupLogFile, true /* fIgnoreFailures */);
7900 }
7901#else
7902 Utf8Str strSupHardeningLogArg;
7903#endif
7904
7905 Utf8Str strAppOverride;
7906#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7907 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7908#endif
7909
7910 bool fUseVBoxSDS = false;
7911 Utf8Str strCanonicalName;
7912 if (false)
7913 { }
7914#ifdef VBOX_WITH_QTGUI
7915 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7916 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7917 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7918 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7919 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7920 {
7921 strCanonicalName = "GUI/Qt";
7922 fUseVBoxSDS = true;
7923 }
7924#endif
7925#ifdef VBOX_WITH_VBOXSDL
7926 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7927 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7928 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7929 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7930 {
7931 strCanonicalName = "GUI/SDL";
7932 fUseVBoxSDS = true;
7933 }
7934#endif
7935#ifdef VBOX_WITH_HEADLESS
7936 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7937 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7938 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7939 {
7940 strCanonicalName = "headless";
7941 }
7942#endif
7943 else
7944 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7945
7946 Utf8Str idStr = mData->mUuid.toString();
7947 Utf8Str const &strMachineName = mUserData->s.strName;
7948 RTPROCESS pid = NIL_RTPROCESS;
7949
7950#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7951 RT_NOREF(fUseVBoxSDS);
7952#else
7953 DWORD idCallerSession = ~(DWORD)0;
7954 if (fUseVBoxSDS)
7955 {
7956 /*
7957 * The VBoxSDS should be used for process launching the VM with
7958 * GUI only if the caller and the VBoxSDS are in different Windows
7959 * sessions and the caller in the interactive one.
7960 */
7961 fUseVBoxSDS = false;
7962
7963 /* Get windows session of the current process. The process token used
7964 due to several reasons:
7965 1. The token is absent for the current thread except someone set it
7966 for us.
7967 2. Needs to get the id of the session where the process is started.
7968 We only need to do this once, though. */
7969 static DWORD s_idCurrentSession = ~(DWORD)0;
7970 DWORD idCurrentSession = s_idCurrentSession;
7971 if (idCurrentSession == ~(DWORD)0)
7972 {
7973 HANDLE hCurrentProcessToken = NULL;
7974 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7975 {
7976 DWORD cbIgn = 0;
7977 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7978 s_idCurrentSession = idCurrentSession;
7979 else
7980 {
7981 idCurrentSession = ~(DWORD)0;
7982 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7983 }
7984 CloseHandle(hCurrentProcessToken);
7985 }
7986 else
7987 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7988 }
7989
7990 /* get the caller's session */
7991 HRESULT hrc = CoImpersonateClient();
7992 if (SUCCEEDED(hrc))
7993 {
7994 HANDLE hCallerThreadToken;
7995 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7996 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7997 &hCallerThreadToken))
7998 {
7999 SetLastError(NO_ERROR);
8000 DWORD cbIgn = 0;
8001 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
8002 {
8003 /* Only need to use SDS if the session ID differs: */
8004 if (idCurrentSession != idCallerSession)
8005 {
8006 fUseVBoxSDS = false;
8007
8008 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
8009 DWORD cbTokenGroups = 0;
8010 PTOKEN_GROUPS pTokenGroups = NULL;
8011 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
8012 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
8013 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
8014 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
8015 {
8016 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
8017 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
8018 PSID pInteractiveSid = NULL;
8019 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
8020 {
8021 /* Iterate over the groups looking for the interactive SID: */
8022 fUseVBoxSDS = false;
8023 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
8024 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
8025 {
8026 fUseVBoxSDS = true;
8027 break;
8028 }
8029 FreeSid(pInteractiveSid);
8030 }
8031 }
8032 else
8033 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
8034 RTMemTmpFree(pTokenGroups);
8035 }
8036 }
8037 else
8038 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
8039 CloseHandle(hCallerThreadToken);
8040 }
8041 else
8042 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
8043 CoRevertToSelf();
8044 }
8045 else
8046 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
8047 }
8048 if (fUseVBoxSDS)
8049 {
8050 /* connect to VBoxSDS */
8051 ComPtr<IVirtualBoxSDS> pVBoxSDS;
8052 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
8053 if (FAILED(rc))
8054 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
8055 strMachineName.c_str());
8056
8057 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
8058 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
8059 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
8060 service to access the files. */
8061 rc = CoSetProxyBlanket(pVBoxSDS,
8062 RPC_C_AUTHN_DEFAULT,
8063 RPC_C_AUTHZ_DEFAULT,
8064 COLE_DEFAULT_PRINCIPAL,
8065 RPC_C_AUTHN_LEVEL_DEFAULT,
8066 RPC_C_IMP_LEVEL_IMPERSONATE,
8067 NULL,
8068 EOAC_DEFAULT);
8069 if (FAILED(rc))
8070 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
8071
8072 size_t const cEnvVars = aEnvironmentChanges.size();
8073 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
8074 for (size_t i = 0; i < cEnvVars; i++)
8075 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
8076
8077 ULONG uPid = 0;
8078 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
8079 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
8080 idCallerSession, &uPid);
8081 if (FAILED(rc))
8082 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
8083 pid = (RTPROCESS)uPid;
8084 }
8085 else
8086#endif /* VBOX_WITH_SDS && RT_OS_WINDOWS */
8087 {
8088 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
8089 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
8090 if (RT_FAILURE(vrc))
8091 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
8092 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
8093 }
8094
8095 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
8096 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
8097
8098 if (!fSeparate)
8099 {
8100 /*
8101 * Note that we don't release the lock here before calling the client,
8102 * because it doesn't need to call us back if called with a NULL argument.
8103 * Releasing the lock here is dangerous because we didn't prepare the
8104 * launch data yet, but the client we've just started may happen to be
8105 * too fast and call LockMachine() that will fail (because of PID, etc.),
8106 * so that the Machine will never get out of the Spawning session state.
8107 */
8108
8109 /* inform the session that it will be a remote one */
8110 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8111#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8112 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8113#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8114 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8115#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8116 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8117
8118 if (FAILED(rc))
8119 {
8120 /* restore the session state */
8121 mData->mSession.mState = SessionState_Unlocked;
8122 alock.release();
8123 mParent->i_addProcessToReap(pid);
8124 /* The failure may occur w/o any error info (from RPC), so provide one */
8125 return setError(VBOX_E_VM_ERROR,
8126 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8127 }
8128
8129 /* attach launch data to the machine */
8130 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8131 mData->mSession.mRemoteControls.push_back(aControl);
8132 mData->mSession.mProgress = aProgress;
8133 mData->mSession.mPID = pid;
8134 mData->mSession.mState = SessionState_Spawning;
8135 Assert(strCanonicalName.isNotEmpty());
8136 mData->mSession.mName = strCanonicalName;
8137 }
8138 else
8139 {
8140 /* For separate UI process we declare the launch as completed instantly, as the
8141 * actual headless VM start may or may not come. No point in remembering anything
8142 * yet, as what matters for us is when the headless VM gets started. */
8143 aProgress->i_notifyComplete(S_OK);
8144 }
8145
8146 alock.release();
8147 mParent->i_addProcessToReap(pid);
8148
8149 LogFlowThisFuncLeave();
8150 return S_OK;
8151}
8152
8153/**
8154 * Returns @c true if the given session machine instance has an open direct
8155 * session (and optionally also for direct sessions which are closing) and
8156 * returns the session control machine instance if so.
8157 *
8158 * Note that when the method returns @c false, the arguments remain unchanged.
8159 *
8160 * @param aMachine Session machine object.
8161 * @param aControl Direct session control object (optional).
8162 * @param aRequireVM If true then only allow VM sessions.
8163 * @param aAllowClosing If true then additionally a session which is currently
8164 * being closed will also be allowed.
8165 *
8166 * @note locks this object for reading.
8167 */
8168bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8169 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8170 bool aRequireVM /*= false*/,
8171 bool aAllowClosing /*= false*/)
8172{
8173 AutoLimitedCaller autoCaller(this);
8174 AssertComRCReturn(autoCaller.rc(), false);
8175
8176 /* just return false for inaccessible machines */
8177 if (getObjectState().getState() != ObjectState::Ready)
8178 return false;
8179
8180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8181
8182 if ( ( mData->mSession.mState == SessionState_Locked
8183 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
8184 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8185 )
8186 {
8187 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8188
8189 aMachine = mData->mSession.mMachine;
8190
8191 if (aControl != NULL)
8192 *aControl = mData->mSession.mDirectControl;
8193
8194 return true;
8195 }
8196
8197 return false;
8198}
8199
8200/**
8201 * Returns @c true if the given machine has an spawning direct session.
8202 *
8203 * @note locks this object for reading.
8204 */
8205bool Machine::i_isSessionSpawning()
8206{
8207 AutoLimitedCaller autoCaller(this);
8208 AssertComRCReturn(autoCaller.rc(), false);
8209
8210 /* just return false for inaccessible machines */
8211 if (getObjectState().getState() != ObjectState::Ready)
8212 return false;
8213
8214 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8215
8216 if (mData->mSession.mState == SessionState_Spawning)
8217 return true;
8218
8219 return false;
8220}
8221
8222/**
8223 * Called from the client watcher thread to check for unexpected client process
8224 * death during Session_Spawning state (e.g. before it successfully opened a
8225 * direct session).
8226 *
8227 * On Win32 and on OS/2, this method is called only when we've got the
8228 * direct client's process termination notification, so it always returns @c
8229 * true.
8230 *
8231 * On other platforms, this method returns @c true if the client process is
8232 * terminated and @c false if it's still alive.
8233 *
8234 * @note Locks this object for writing.
8235 */
8236bool Machine::i_checkForSpawnFailure()
8237{
8238 AutoCaller autoCaller(this);
8239 if (!autoCaller.isOk())
8240 {
8241 /* nothing to do */
8242 LogFlowThisFunc(("Already uninitialized!\n"));
8243 return true;
8244 }
8245
8246 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8247
8248 if (mData->mSession.mState != SessionState_Spawning)
8249 {
8250 /* nothing to do */
8251 LogFlowThisFunc(("Not spawning any more!\n"));
8252 return true;
8253 }
8254
8255 HRESULT rc = S_OK;
8256
8257 /* PID not yet initialized, skip check. */
8258 if (mData->mSession.mPID == NIL_RTPROCESS)
8259 return false;
8260
8261 RTPROCSTATUS status;
8262 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8263
8264 if (vrc != VERR_PROCESS_RUNNING)
8265 {
8266 Utf8Str strExtraInfo;
8267
8268#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8269 /* If the startup logfile exists and is of non-zero length, tell the
8270 user to look there for more details to encourage them to attach it
8271 when reporting startup issues. */
8272 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8273 uint64_t cbStartupLogFile = 0;
8274 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
8275 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8276 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
8277#endif
8278
8279 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8280 rc = setError(E_FAIL,
8281 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8282 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8283 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8284 rc = setError(E_FAIL,
8285 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8286 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8287 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8288 rc = setError(E_FAIL,
8289 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8290 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8291 else
8292 rc = setErrorBoth(E_FAIL, vrc,
8293 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8294 i_getName().c_str(), vrc, strExtraInfo.c_str());
8295 }
8296
8297 if (FAILED(rc))
8298 {
8299 /* Close the remote session, remove the remote control from the list
8300 * and reset session state to Closed (@note keep the code in sync with
8301 * the relevant part in LockMachine()). */
8302
8303 Assert(mData->mSession.mRemoteControls.size() == 1);
8304 if (mData->mSession.mRemoteControls.size() == 1)
8305 {
8306 ErrorInfoKeeper eik;
8307 mData->mSession.mRemoteControls.front()->Uninitialize();
8308 }
8309
8310 mData->mSession.mRemoteControls.clear();
8311 mData->mSession.mState = SessionState_Unlocked;
8312
8313 /* finalize the progress after setting the state */
8314 if (!mData->mSession.mProgress.isNull())
8315 {
8316 mData->mSession.mProgress->notifyComplete(rc);
8317 mData->mSession.mProgress.setNull();
8318 }
8319
8320 mData->mSession.mPID = NIL_RTPROCESS;
8321
8322 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
8323 return true;
8324 }
8325
8326 return false;
8327}
8328
8329/**
8330 * Checks whether the machine can be registered. If so, commits and saves
8331 * all settings.
8332 *
8333 * @note Must be called from mParent's write lock. Locks this object and
8334 * children for writing.
8335 */
8336HRESULT Machine::i_prepareRegister()
8337{
8338 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8339
8340 AutoLimitedCaller autoCaller(this);
8341 AssertComRCReturnRC(autoCaller.rc());
8342
8343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8344
8345 /* wait for state dependents to drop to zero */
8346 i_ensureNoStateDependencies(alock);
8347
8348 if (!mData->mAccessible)
8349 return setError(VBOX_E_INVALID_OBJECT_STATE,
8350 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8351 mUserData->s.strName.c_str(),
8352 mData->mUuid.toString().c_str());
8353
8354 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8355
8356 if (mData->mRegistered)
8357 return setError(VBOX_E_INVALID_OBJECT_STATE,
8358 tr("The machine '%s' with UUID {%s} is already registered"),
8359 mUserData->s.strName.c_str(),
8360 mData->mUuid.toString().c_str());
8361
8362 HRESULT rc = S_OK;
8363
8364 // Ensure the settings are saved. If we are going to be registered and
8365 // no config file exists yet, create it by calling i_saveSettings() too.
8366 if ( (mData->flModifications)
8367 || (!mData->pMachineConfigFile->fileExists())
8368 )
8369 {
8370 rc = i_saveSettings(NULL, alock);
8371 // no need to check whether VirtualBox.xml needs saving too since
8372 // we can't have a machine XML file rename pending
8373 if (FAILED(rc)) return rc;
8374 }
8375
8376 /* more config checking goes here */
8377
8378 if (SUCCEEDED(rc))
8379 {
8380 /* we may have had implicit modifications we want to fix on success */
8381 i_commit();
8382
8383 mData->mRegistered = true;
8384 }
8385 else
8386 {
8387 /* we may have had implicit modifications we want to cancel on failure*/
8388 i_rollback(false /* aNotify */);
8389 }
8390
8391 return rc;
8392}
8393
8394/**
8395 * Increases the number of objects dependent on the machine state or on the
8396 * registered state. Guarantees that these two states will not change at least
8397 * until #i_releaseStateDependency() is called.
8398 *
8399 * Depending on the @a aDepType value, additional state checks may be made.
8400 * These checks will set extended error info on failure. See
8401 * #i_checkStateDependency() for more info.
8402 *
8403 * If this method returns a failure, the dependency is not added and the caller
8404 * is not allowed to rely on any particular machine state or registration state
8405 * value and may return the failed result code to the upper level.
8406 *
8407 * @param aDepType Dependency type to add.
8408 * @param aState Current machine state (NULL if not interested).
8409 * @param aRegistered Current registered state (NULL if not interested).
8410 *
8411 * @note Locks this object for writing.
8412 */
8413HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8414 MachineState_T *aState /* = NULL */,
8415 BOOL *aRegistered /* = NULL */)
8416{
8417 AutoCaller autoCaller(this);
8418 AssertComRCReturnRC(autoCaller.rc());
8419
8420 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8421
8422 HRESULT rc = i_checkStateDependency(aDepType);
8423 if (FAILED(rc)) return rc;
8424
8425 {
8426 if (mData->mMachineStateChangePending != 0)
8427 {
8428 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8429 * drop to zero so don't add more. It may make sense to wait a bit
8430 * and retry before reporting an error (since the pending state
8431 * transition should be really quick) but let's just assert for
8432 * now to see if it ever happens on practice. */
8433
8434 AssertFailed();
8435
8436 return setError(E_ACCESSDENIED,
8437 tr("Machine state change is in progress. Please retry the operation later."));
8438 }
8439
8440 ++mData->mMachineStateDeps;
8441 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8442 }
8443
8444 if (aState)
8445 *aState = mData->mMachineState;
8446 if (aRegistered)
8447 *aRegistered = mData->mRegistered;
8448
8449 return S_OK;
8450}
8451
8452/**
8453 * Decreases the number of objects dependent on the machine state.
8454 * Must always complete the #i_addStateDependency() call after the state
8455 * dependency is no more necessary.
8456 */
8457void Machine::i_releaseStateDependency()
8458{
8459 AutoCaller autoCaller(this);
8460 AssertComRCReturnVoid(autoCaller.rc());
8461
8462 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8463
8464 /* releaseStateDependency() w/o addStateDependency()? */
8465 AssertReturnVoid(mData->mMachineStateDeps != 0);
8466 -- mData->mMachineStateDeps;
8467
8468 if (mData->mMachineStateDeps == 0)
8469 {
8470 /* inform i_ensureNoStateDependencies() that there are no more deps */
8471 if (mData->mMachineStateChangePending != 0)
8472 {
8473 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8474 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8475 }
8476 }
8477}
8478
8479Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8480{
8481 /* start with nothing found */
8482 Utf8Str strResult("");
8483
8484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8485
8486 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8487 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8488 // found:
8489 strResult = it->second; // source is a Utf8Str
8490
8491 return strResult;
8492}
8493
8494// protected methods
8495/////////////////////////////////////////////////////////////////////////////
8496
8497/**
8498 * Performs machine state checks based on the @a aDepType value. If a check
8499 * fails, this method will set extended error info, otherwise it will return
8500 * S_OK. It is supposed, that on failure, the caller will immediately return
8501 * the return value of this method to the upper level.
8502 *
8503 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8504 *
8505 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8506 * current state of this machine object allows to change settings of the
8507 * machine (i.e. the machine is not registered, or registered but not running
8508 * and not saved). It is useful to call this method from Machine setters
8509 * before performing any change.
8510 *
8511 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8512 * as for MutableStateDep except that if the machine is saved, S_OK is also
8513 * returned. This is useful in setters which allow changing machine
8514 * properties when it is in the saved state.
8515 *
8516 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8517 * if the current state of this machine object allows to change runtime
8518 * changeable settings of the machine (i.e. the machine is not registered, or
8519 * registered but either running or not running and not saved). It is useful
8520 * to call this method from Machine setters before performing any changes to
8521 * runtime changeable settings.
8522 *
8523 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8524 * the same as for MutableOrRunningStateDep except that if the machine is
8525 * saved, S_OK is also returned. This is useful in setters which allow
8526 * changing runtime and saved state changeable machine properties.
8527 *
8528 * @param aDepType Dependency type to check.
8529 *
8530 * @note Non Machine based classes should use #i_addStateDependency() and
8531 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8532 * template.
8533 *
8534 * @note This method must be called from under this object's read or write
8535 * lock.
8536 */
8537HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8538{
8539 switch (aDepType)
8540 {
8541 case AnyStateDep:
8542 {
8543 break;
8544 }
8545 case MutableStateDep:
8546 {
8547 if ( mData->mRegistered
8548 && ( !i_isSessionMachine()
8549 || ( mData->mMachineState != MachineState_Aborted
8550 && mData->mMachineState != MachineState_Teleported
8551 && mData->mMachineState != MachineState_PoweredOff
8552 )
8553 )
8554 )
8555 return setError(VBOX_E_INVALID_VM_STATE,
8556 tr("The machine is not mutable (state is %s)"),
8557 Global::stringifyMachineState(mData->mMachineState));
8558 break;
8559 }
8560 case MutableOrSavedStateDep:
8561 {
8562 if ( mData->mRegistered
8563 && ( !i_isSessionMachine()
8564 || ( mData->mMachineState != MachineState_Aborted
8565 && mData->mMachineState != MachineState_Teleported
8566 && mData->mMachineState != MachineState_Saved
8567 && mData->mMachineState != MachineState_AbortedSaved
8568 && mData->mMachineState != MachineState_PoweredOff
8569 )
8570 )
8571 )
8572 return setError(VBOX_E_INVALID_VM_STATE,
8573 tr("The machine is not mutable or saved (state is %s)"),
8574 Global::stringifyMachineState(mData->mMachineState));
8575 break;
8576 }
8577 case MutableOrRunningStateDep:
8578 {
8579 if ( mData->mRegistered
8580 && ( !i_isSessionMachine()
8581 || ( mData->mMachineState != MachineState_Aborted
8582 && mData->mMachineState != MachineState_Teleported
8583 && mData->mMachineState != MachineState_PoweredOff
8584 && !Global::IsOnline(mData->mMachineState)
8585 )
8586 )
8587 )
8588 return setError(VBOX_E_INVALID_VM_STATE,
8589 tr("The machine is not mutable or running (state is %s)"),
8590 Global::stringifyMachineState(mData->mMachineState));
8591 break;
8592 }
8593 case MutableOrSavedOrRunningStateDep:
8594 {
8595 if ( mData->mRegistered
8596 && ( !i_isSessionMachine()
8597 || ( mData->mMachineState != MachineState_Aborted
8598 && mData->mMachineState != MachineState_Teleported
8599 && mData->mMachineState != MachineState_Saved
8600 && mData->mMachineState != MachineState_AbortedSaved
8601 && mData->mMachineState != MachineState_PoweredOff
8602 && !Global::IsOnline(mData->mMachineState)
8603 )
8604 )
8605 )
8606 return setError(VBOX_E_INVALID_VM_STATE,
8607 tr("The machine is not mutable, saved or running (state is %s)"),
8608 Global::stringifyMachineState(mData->mMachineState));
8609 break;
8610 }
8611 }
8612
8613 return S_OK;
8614}
8615
8616/**
8617 * Helper to initialize all associated child objects and allocate data
8618 * structures.
8619 *
8620 * This method must be called as a part of the object's initialization procedure
8621 * (usually done in the #init() method).
8622 *
8623 * @note Must be called only from #init() or from #i_registeredInit().
8624 */
8625HRESULT Machine::initDataAndChildObjects()
8626{
8627 AutoCaller autoCaller(this);
8628 AssertComRCReturnRC(autoCaller.rc());
8629 AssertReturn( getObjectState().getState() == ObjectState::InInit
8630 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8631
8632 AssertReturn(!mData->mAccessible, E_FAIL);
8633
8634 /* allocate data structures */
8635 mSSData.allocate();
8636 mUserData.allocate();
8637 mHWData.allocate();
8638 mMediumAttachments.allocate();
8639 mStorageControllers.allocate();
8640 mUSBControllers.allocate();
8641
8642 /* initialize mOSTypeId */
8643 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8644
8645/** @todo r=bird: init() methods never fails, right? Why don't we make them
8646 * return void then! */
8647
8648 /* create associated BIOS settings object */
8649 unconst(mBIOSSettings).createObject();
8650 mBIOSSettings->init(this);
8651
8652 /* create associated recording settings object */
8653 unconst(mRecordingSettings).createObject();
8654 mRecordingSettings->init(this);
8655
8656 /* create associated trusted platform module object */
8657 unconst(mTrustedPlatformModule).createObject();
8658 mTrustedPlatformModule->init(this);
8659
8660 /* create associated NVRAM store object */
8661 unconst(mNvramStore).createObject();
8662 mNvramStore->init(this);
8663
8664 /* create the graphics adapter object (always present) */
8665 unconst(mGraphicsAdapter).createObject();
8666 mGraphicsAdapter->init(this);
8667
8668 /* create an associated VRDE object (default is disabled) */
8669 unconst(mVRDEServer).createObject();
8670 mVRDEServer->init(this);
8671
8672 /* create associated serial port objects */
8673 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8674 {
8675 unconst(mSerialPorts[slot]).createObject();
8676 mSerialPorts[slot]->init(this, slot);
8677 }
8678
8679 /* create associated parallel port objects */
8680 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8681 {
8682 unconst(mParallelPorts[slot]).createObject();
8683 mParallelPorts[slot]->init(this, slot);
8684 }
8685
8686 /* create the audio settings object */
8687 unconst(mAudioSettings).createObject();
8688 mAudioSettings->init(this);
8689
8690 /* create the USB device filters object (always present) */
8691 unconst(mUSBDeviceFilters).createObject();
8692 mUSBDeviceFilters->init(this);
8693
8694 /* create associated network adapter objects */
8695 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8696 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8697 {
8698 unconst(mNetworkAdapters[slot]).createObject();
8699 mNetworkAdapters[slot]->init(this, slot);
8700 }
8701
8702 /* create the bandwidth control */
8703 unconst(mBandwidthControl).createObject();
8704 mBandwidthControl->init(this);
8705
8706 /* create the guest debug control object */
8707 unconst(mGuestDebugControl).createObject();
8708 mGuestDebugControl->init(this);
8709
8710 return S_OK;
8711}
8712
8713/**
8714 * Helper to uninitialize all associated child objects and to free all data
8715 * structures.
8716 *
8717 * This method must be called as a part of the object's uninitialization
8718 * procedure (usually done in the #uninit() method).
8719 *
8720 * @note Must be called only from #uninit() or from #i_registeredInit().
8721 */
8722void Machine::uninitDataAndChildObjects()
8723{
8724 AutoCaller autoCaller(this);
8725 AssertComRCReturnVoid(autoCaller.rc());
8726 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8727 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8728 || getObjectState().getState() == ObjectState::InUninit
8729 || getObjectState().getState() == ObjectState::Limited);
8730
8731 /* tell all our other child objects we've been uninitialized */
8732 if (mGuestDebugControl)
8733 {
8734 mGuestDebugControl->uninit();
8735 unconst(mGuestDebugControl).setNull();
8736 }
8737
8738 if (mBandwidthControl)
8739 {
8740 mBandwidthControl->uninit();
8741 unconst(mBandwidthControl).setNull();
8742 }
8743
8744 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8745 {
8746 if (mNetworkAdapters[slot])
8747 {
8748 mNetworkAdapters[slot]->uninit();
8749 unconst(mNetworkAdapters[slot]).setNull();
8750 }
8751 }
8752
8753 if (mUSBDeviceFilters)
8754 {
8755 mUSBDeviceFilters->uninit();
8756 unconst(mUSBDeviceFilters).setNull();
8757 }
8758
8759 if (mAudioSettings)
8760 {
8761 mAudioSettings->uninit();
8762 unconst(mAudioSettings).setNull();
8763 }
8764
8765 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8766 {
8767 if (mParallelPorts[slot])
8768 {
8769 mParallelPorts[slot]->uninit();
8770 unconst(mParallelPorts[slot]).setNull();
8771 }
8772 }
8773
8774 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8775 {
8776 if (mSerialPorts[slot])
8777 {
8778 mSerialPorts[slot]->uninit();
8779 unconst(mSerialPorts[slot]).setNull();
8780 }
8781 }
8782
8783 if (mVRDEServer)
8784 {
8785 mVRDEServer->uninit();
8786 unconst(mVRDEServer).setNull();
8787 }
8788
8789 if (mGraphicsAdapter)
8790 {
8791 mGraphicsAdapter->uninit();
8792 unconst(mGraphicsAdapter).setNull();
8793 }
8794
8795 if (mBIOSSettings)
8796 {
8797 mBIOSSettings->uninit();
8798 unconst(mBIOSSettings).setNull();
8799 }
8800
8801 if (mRecordingSettings)
8802 {
8803 mRecordingSettings->uninit();
8804 unconst(mRecordingSettings).setNull();
8805 }
8806
8807 if (mTrustedPlatformModule)
8808 {
8809 mTrustedPlatformModule->uninit();
8810 unconst(mTrustedPlatformModule).setNull();
8811 }
8812
8813 if (mNvramStore)
8814 {
8815 mNvramStore->uninit();
8816 unconst(mNvramStore).setNull();
8817 }
8818
8819 /* Deassociate media (only when a real Machine or a SnapshotMachine
8820 * instance is uninitialized; SessionMachine instances refer to real
8821 * Machine media). This is necessary for a clean re-initialization of
8822 * the VM after successfully re-checking the accessibility state. Note
8823 * that in case of normal Machine or SnapshotMachine uninitialization (as
8824 * a result of unregistering or deleting the snapshot), outdated media
8825 * attachments will already be uninitialized and deleted, so this
8826 * code will not affect them. */
8827 if ( !mMediumAttachments.isNull()
8828 && !i_isSessionMachine()
8829 )
8830 {
8831 for (MediumAttachmentList::const_iterator
8832 it = mMediumAttachments->begin();
8833 it != mMediumAttachments->end();
8834 ++it)
8835 {
8836 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8837 if (pMedium.isNull())
8838 continue;
8839 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8840 AssertComRC(rc);
8841 }
8842 }
8843
8844 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8845 {
8846 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children)
8847 if (mData->mFirstSnapshot)
8848 {
8849 // Snapshots tree is protected by machine write lock.
8850 // Otherwise we assert in Snapshot::uninit()
8851 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8852 mData->mFirstSnapshot->uninit();
8853 mData->mFirstSnapshot.setNull();
8854 }
8855
8856 mData->mCurrentSnapshot.setNull();
8857 }
8858
8859 /* free data structures (the essential mData structure is not freed here
8860 * since it may be still in use) */
8861 mMediumAttachments.free();
8862 mStorageControllers.free();
8863 mUSBControllers.free();
8864 mHWData.free();
8865 mUserData.free();
8866 mSSData.free();
8867}
8868
8869/**
8870 * Returns a pointer to the Machine object for this machine that acts like a
8871 * parent for complex machine data objects such as shared folders, etc.
8872 *
8873 * For primary Machine objects and for SnapshotMachine objects, returns this
8874 * object's pointer itself. For SessionMachine objects, returns the peer
8875 * (primary) machine pointer.
8876 */
8877Machine *Machine::i_getMachine()
8878{
8879 if (i_isSessionMachine())
8880 return (Machine*)mPeer;
8881 return this;
8882}
8883
8884/**
8885 * Makes sure that there are no machine state dependents. If necessary, waits
8886 * for the number of dependents to drop to zero.
8887 *
8888 * Make sure this method is called from under this object's write lock to
8889 * guarantee that no new dependents may be added when this method returns
8890 * control to the caller.
8891 *
8892 * @note Receives a lock to this object for writing. The lock will be released
8893 * while waiting (if necessary).
8894 *
8895 * @warning To be used only in methods that change the machine state!
8896 */
8897void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8898{
8899 AssertReturnVoid(isWriteLockOnCurrentThread());
8900
8901 /* Wait for all state dependents if necessary */
8902 if (mData->mMachineStateDeps != 0)
8903 {
8904 /* lazy semaphore creation */
8905 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8906 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8907
8908 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8909 mData->mMachineStateDeps));
8910
8911 ++mData->mMachineStateChangePending;
8912
8913 /* reset the semaphore before waiting, the last dependent will signal
8914 * it */
8915 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8916
8917 alock.release();
8918
8919 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8920
8921 alock.acquire();
8922
8923 -- mData->mMachineStateChangePending;
8924 }
8925}
8926
8927/**
8928 * Changes the machine state and informs callbacks.
8929 *
8930 * This method is not intended to fail so it either returns S_OK or asserts (and
8931 * returns a failure).
8932 *
8933 * @note Locks this object for writing.
8934 */
8935HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8936{
8937 LogFlowThisFuncEnter();
8938 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8939 Assert(aMachineState != MachineState_Null);
8940
8941 AutoCaller autoCaller(this);
8942 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8943
8944 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8945
8946 /* wait for state dependents to drop to zero */
8947 i_ensureNoStateDependencies(alock);
8948
8949 MachineState_T const enmOldState = mData->mMachineState;
8950 if (enmOldState != aMachineState)
8951 {
8952 mData->mMachineState = aMachineState;
8953 RTTimeNow(&mData->mLastStateChange);
8954
8955#ifdef VBOX_WITH_DTRACE_R3_MAIN
8956 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8957#endif
8958 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8959 }
8960
8961 LogFlowThisFuncLeave();
8962 return S_OK;
8963}
8964
8965/**
8966 * Searches for a shared folder with the given logical name
8967 * in the collection of shared folders.
8968 *
8969 * @param aName logical name of the shared folder
8970 * @param aSharedFolder where to return the found object
8971 * @param aSetError whether to set the error info if the folder is
8972 * not found
8973 * @return
8974 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8975 *
8976 * @note
8977 * must be called from under the object's lock!
8978 */
8979HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8980 ComObjPtr<SharedFolder> &aSharedFolder,
8981 bool aSetError /* = false */)
8982{
8983 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8984 for (HWData::SharedFolderList::const_iterator
8985 it = mHWData->mSharedFolders.begin();
8986 it != mHWData->mSharedFolders.end();
8987 ++it)
8988 {
8989 SharedFolder *pSF = *it;
8990 AutoCaller autoCaller(pSF);
8991 if (pSF->i_getName() == aName)
8992 {
8993 aSharedFolder = pSF;
8994 rc = S_OK;
8995 break;
8996 }
8997 }
8998
8999 if (aSetError && FAILED(rc))
9000 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
9001
9002 return rc;
9003}
9004
9005/**
9006 * Initializes all machine instance data from the given settings structures
9007 * from XML. The exception is the machine UUID which needs special handling
9008 * depending on the caller's use case, so the caller needs to set that herself.
9009 *
9010 * This gets called in several contexts during machine initialization:
9011 *
9012 * -- When machine XML exists on disk already and needs to be loaded into memory,
9013 * for example, from #i_registeredInit() to load all registered machines on
9014 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
9015 * attached to the machine should be part of some media registry already.
9016 *
9017 * -- During OVF import, when a machine config has been constructed from an
9018 * OVF file. In this case, puuidRegistry is set to the machine UUID to
9019 * ensure that the media listed as attachments in the config (which have
9020 * been imported from the OVF) receive the correct registry ID.
9021 *
9022 * -- During VM cloning.
9023 *
9024 * @param config Machine settings from XML.
9025 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
9026 * for each attached medium in the config.
9027 * @return
9028 */
9029HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
9030 const Guid *puuidRegistry)
9031{
9032 // copy name, description, OS type, teleporter, UTC etc.
9033 mUserData->s = config.machineUserData;
9034
9035 // look up the object by Id to check it is valid
9036 ComObjPtr<GuestOSType> pGuestOSType;
9037 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9038 if (!pGuestOSType.isNull())
9039 mUserData->s.strOsType = pGuestOSType->i_id();
9040
9041#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9042 // stateFile encryption (optional)
9043 mSSData->strStateKeyId = config.strStateKeyId;
9044 mSSData->strStateKeyStore = config.strStateKeyStore;
9045 mData->mstrLogKeyId = config.strLogKeyId;
9046 mData->mstrLogKeyStore = config.strLogKeyStore;
9047#endif
9048
9049 // stateFile (optional)
9050 if (config.strStateFile.isEmpty())
9051 mSSData->strStateFilePath.setNull();
9052 else
9053 {
9054 Utf8Str stateFilePathFull(config.strStateFile);
9055 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
9056 if (RT_FAILURE(vrc))
9057 return setErrorBoth(E_FAIL, vrc,
9058 tr("Invalid saved state file path '%s' (%Rrc)"),
9059 config.strStateFile.c_str(),
9060 vrc);
9061 mSSData->strStateFilePath = stateFilePathFull;
9062 }
9063
9064 // snapshot folder needs special processing so set it again
9065 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
9066 if (FAILED(rc)) return rc;
9067
9068 /* Copy the extra data items (config may or may not be the same as
9069 * mData->pMachineConfigFile) if necessary. When loading the XML files
9070 * from disk they are the same, but not for OVF import. */
9071 if (mData->pMachineConfigFile != &config)
9072 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
9073
9074 /* currentStateModified (optional, default is true) */
9075 mData->mCurrentStateModified = config.fCurrentStateModified;
9076
9077 mData->mLastStateChange = config.timeLastStateChange;
9078
9079 /*
9080 * note: all mUserData members must be assigned prior this point because
9081 * we need to commit changes in order to let mUserData be shared by all
9082 * snapshot machine instances.
9083 */
9084 mUserData.commitCopy();
9085
9086 // machine registry, if present (must be loaded before snapshots)
9087 if (config.canHaveOwnMediaRegistry())
9088 {
9089 // determine machine folder
9090 Utf8Str strMachineFolder = i_getSettingsFileFull();
9091 strMachineFolder.stripFilename();
9092 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
9093 config.mediaRegistry,
9094 strMachineFolder);
9095 if (FAILED(rc)) return rc;
9096 }
9097
9098 /* Snapshot node (optional) */
9099 size_t cRootSnapshots;
9100 if ((cRootSnapshots = config.llFirstSnapshot.size()))
9101 {
9102 // there must be only one root snapshot
9103 Assert(cRootSnapshots == 1);
9104 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9105
9106 rc = i_loadSnapshot(snap,
9107 config.uuidCurrentSnapshot);
9108 if (FAILED(rc)) return rc;
9109 }
9110
9111 // hardware data
9112 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart,
9113 config.recordingSettings);
9114 if (FAILED(rc)) return rc;
9115
9116 /*
9117 * NOTE: the assignment below must be the last thing to do,
9118 * otherwise it will be not possible to change the settings
9119 * somewhere in the code above because all setters will be
9120 * blocked by i_checkStateDependency(MutableStateDep).
9121 */
9122
9123 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
9124 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
9125 {
9126 /* no need to use i_setMachineState() during init() */
9127 mData->mMachineState = MachineState_AbortedSaved;
9128 }
9129 else if (config.fAborted)
9130 {
9131 mSSData->strStateFilePath.setNull();
9132
9133 /* no need to use i_setMachineState() during init() */
9134 mData->mMachineState = MachineState_Aborted;
9135 }
9136 else if (!mSSData->strStateFilePath.isEmpty())
9137 {
9138 /* no need to use i_setMachineState() during init() */
9139 mData->mMachineState = MachineState_Saved;
9140 }
9141
9142 // after loading settings, we are no longer different from the XML on disk
9143 mData->flModifications = 0;
9144
9145 return S_OK;
9146}
9147
9148/**
9149 * Loads all snapshots starting from the given settings.
9150 *
9151 * @param data snapshot settings.
9152 * @param aCurSnapshotId Current snapshot ID from the settings file.
9153 */
9154HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
9155 const Guid &aCurSnapshotId)
9156{
9157 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
9158 AssertReturn(!i_isSessionMachine(), E_FAIL);
9159
9160 HRESULT rc = S_OK;
9161
9162 std::list<const settings::Snapshot *> llSettingsTodo;
9163 llSettingsTodo.push_back(&data);
9164 std::list<Snapshot *> llParentsTodo;
9165 llParentsTodo.push_back(NULL);
9166
9167 while (llSettingsTodo.size() > 0)
9168 {
9169 const settings::Snapshot *current = llSettingsTodo.front();
9170 llSettingsTodo.pop_front();
9171 Snapshot *pParent = llParentsTodo.front();
9172 llParentsTodo.pop_front();
9173
9174 Utf8Str strStateFile;
9175 if (!current->strStateFile.isEmpty())
9176 {
9177 /* optional */
9178 strStateFile = current->strStateFile;
9179 int vrc = i_calculateFullPath(strStateFile, strStateFile);
9180 if (RT_FAILURE(vrc))
9181 {
9182 setErrorBoth(E_FAIL, vrc,
9183 tr("Invalid saved state file path '%s' (%Rrc)"),
9184 strStateFile.c_str(), vrc);
9185 }
9186 }
9187
9188 /* create a snapshot machine object */
9189 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9190 pSnapshotMachine.createObject();
9191 rc = pSnapshotMachine->initFromSettings(this,
9192 current->hardware,
9193 &current->debugging,
9194 &current->autostart,
9195 current->recordingSettings,
9196 current->uuid.ref(),
9197 strStateFile);
9198 if (FAILED(rc)) break;
9199
9200 /* create a snapshot object */
9201 ComObjPtr<Snapshot> pSnapshot;
9202 pSnapshot.createObject();
9203 /* initialize the snapshot */
9204 rc = pSnapshot->init(mParent, // VirtualBox object
9205 current->uuid,
9206 current->strName,
9207 current->strDescription,
9208 current->timestamp,
9209 pSnapshotMachine,
9210 pParent);
9211 if (FAILED(rc)) break;
9212
9213 /* memorize the first snapshot if necessary */
9214 if (!mData->mFirstSnapshot)
9215 {
9216 Assert(pParent == NULL);
9217 mData->mFirstSnapshot = pSnapshot;
9218 }
9219
9220 /* memorize the current snapshot when appropriate */
9221 if ( !mData->mCurrentSnapshot
9222 && pSnapshot->i_getId() == aCurSnapshotId
9223 )
9224 mData->mCurrentSnapshot = pSnapshot;
9225
9226 /* create all children */
9227 std::list<settings::Snapshot>::const_iterator itBegin = current->llChildSnapshots.begin();
9228 std::list<settings::Snapshot>::const_iterator itEnd = current->llChildSnapshots.end();
9229 for (std::list<settings::Snapshot>::const_iterator it = itBegin; it != itEnd; ++it)
9230 {
9231 llSettingsTodo.push_back(&*it);
9232 llParentsTodo.push_back(pSnapshot);
9233 }
9234 }
9235
9236 return rc;
9237}
9238
9239/**
9240 * Loads settings into mHWData.
9241 *
9242 * @param puuidRegistry Registry ID.
9243 * @param puuidSnapshot Snapshot ID
9244 * @param data Reference to the hardware settings.
9245 * @param pDbg Pointer to the debugging settings.
9246 * @param pAutostart Pointer to the autostart settings
9247 * @param recording Reference to recording settings.
9248 */
9249HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
9250 const Guid *puuidSnapshot,
9251 const settings::Hardware &data,
9252 const settings::Debugging *pDbg,
9253 const settings::Autostart *pAutostart,
9254 const settings::RecordingSettings &recording)
9255{
9256 AssertReturn(!i_isSessionMachine(), E_FAIL);
9257
9258 HRESULT rc = S_OK;
9259
9260 try
9261 {
9262 ComObjPtr<GuestOSType> pGuestOSType;
9263 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9264
9265 /* The hardware version attribute (optional). */
9266 mHWData->mHWVersion = data.strVersion;
9267 mHWData->mHardwareUUID = data.uuid;
9268
9269 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9270 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9271 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9272 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9273 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9274 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9275 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
9276 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
9277 mHWData->mPAEEnabled = data.fPAE;
9278 mHWData->mLongMode = data.enmLongMode;
9279 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9280 mHWData->mAPIC = data.fAPIC;
9281 mHWData->mX2APIC = data.fX2APIC;
9282 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9283 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9284 mHWData->mSpecCtrl = data.fSpecCtrl;
9285 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
9286 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
9287 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
9288 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
9289 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
9290 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9291 mHWData->mCPUCount = data.cCPUs;
9292 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9293 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9294 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9295 mHWData->mCpuProfile = data.strCpuProfile;
9296
9297 // cpu
9298 if (mHWData->mCPUHotPlugEnabled)
9299 {
9300 for (settings::CpuList::const_iterator
9301 it = data.llCpus.begin();
9302 it != data.llCpus.end();
9303 ++it)
9304 {
9305 const settings::Cpu &cpu = *it;
9306
9307 mHWData->mCPUAttached[cpu.ulId] = true;
9308 }
9309 }
9310
9311 // cpuid leafs
9312 for (settings::CpuIdLeafsList::const_iterator
9313 it = data.llCpuIdLeafs.begin();
9314 it != data.llCpuIdLeafs.end();
9315 ++it)
9316 {
9317 const settings::CpuIdLeaf &rLeaf= *it;
9318 if ( rLeaf.idx < UINT32_C(0x20)
9319 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9320 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9321 mHWData->mCpuIdLeafList.push_back(rLeaf);
9322 /* else: just ignore */
9323 }
9324
9325 mHWData->mMemorySize = data.ulMemorySizeMB;
9326 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9327
9328 // boot order
9329 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9330 {
9331 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9332 if (it == data.mapBootOrder.end())
9333 mHWData->mBootOrder[i] = DeviceType_Null;
9334 else
9335 mHWData->mBootOrder[i] = it->second;
9336 }
9337
9338 mHWData->mFirmwareType = data.firmwareType;
9339 mHWData->mPointingHIDType = data.pointingHIDType;
9340 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9341 mHWData->mChipsetType = data.chipsetType;
9342 mHWData->mIommuType = data.iommuType;
9343 mHWData->mParavirtProvider = data.paravirtProvider;
9344 mHWData->mParavirtDebug = data.strParavirtDebug;
9345 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9346 mHWData->mHPETEnabled = data.fHPETEnabled;
9347
9348 /* GraphicsAdapter */
9349 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
9350 if (FAILED(rc)) return rc;
9351
9352 /* VRDEServer */
9353 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9354 if (FAILED(rc)) return rc;
9355
9356 /* BIOS */
9357 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9358 if (FAILED(rc)) return rc;
9359
9360 /* Recording */
9361 rc = mRecordingSettings->i_loadSettings(recording);
9362 if (FAILED(rc)) return rc;
9363
9364 /* Trusted Platform Module */
9365 rc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
9366 if (FAILED(rc)) return rc;
9367
9368 rc = mNvramStore->i_loadSettings(data.nvramSettings);
9369 if (FAILED(rc)) return rc;
9370
9371 // Bandwidth control (must come before network adapters)
9372 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9373 if (FAILED(rc)) return rc;
9374
9375 /* USB controllers */
9376 for (settings::USBControllerList::const_iterator
9377 it = data.usbSettings.llUSBControllers.begin();
9378 it != data.usbSettings.llUSBControllers.end();
9379 ++it)
9380 {
9381 const settings::USBController &settingsCtrl = *it;
9382 ComObjPtr<USBController> newCtrl;
9383
9384 newCtrl.createObject();
9385 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9386 mUSBControllers->push_back(newCtrl);
9387 }
9388
9389 /* USB device filters */
9390 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9391 if (FAILED(rc)) return rc;
9392
9393 // network adapters (establish array size first and apply defaults, to
9394 // ensure reading the same settings as we saved, since the list skips
9395 // adapters having defaults)
9396 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9397 size_t oldCount = mNetworkAdapters.size();
9398 if (newCount > oldCount)
9399 {
9400 mNetworkAdapters.resize(newCount);
9401 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9402 {
9403 unconst(mNetworkAdapters[slot]).createObject();
9404 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9405 }
9406 }
9407 else if (newCount < oldCount)
9408 mNetworkAdapters.resize(newCount);
9409 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9410 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9411 for (settings::NetworkAdaptersList::const_iterator
9412 it = data.llNetworkAdapters.begin();
9413 it != data.llNetworkAdapters.end();
9414 ++it)
9415 {
9416 const settings::NetworkAdapter &nic = *it;
9417
9418 /* slot uniqueness is guaranteed by XML Schema */
9419 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9420 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9421 if (FAILED(rc)) return rc;
9422 }
9423
9424 // serial ports (establish defaults first, to ensure reading the same
9425 // settings as we saved, since the list skips ports having defaults)
9426 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9427 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9428 for (settings::SerialPortsList::const_iterator
9429 it = data.llSerialPorts.begin();
9430 it != data.llSerialPorts.end();
9431 ++it)
9432 {
9433 const settings::SerialPort &s = *it;
9434
9435 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9436 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9437 if (FAILED(rc)) return rc;
9438 }
9439
9440 // parallel ports (establish defaults first, to ensure reading the same
9441 // settings as we saved, since the list skips ports having defaults)
9442 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9443 mParallelPorts[i]->i_applyDefaults();
9444 for (settings::ParallelPortsList::const_iterator
9445 it = data.llParallelPorts.begin();
9446 it != data.llParallelPorts.end();
9447 ++it)
9448 {
9449 const settings::ParallelPort &p = *it;
9450
9451 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9452 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9453 if (FAILED(rc)) return rc;
9454 }
9455
9456 /* Audio settings */
9457 rc = mAudioSettings->i_loadSettings(data.audioAdapter);
9458 if (FAILED(rc)) return rc;
9459
9460 /* storage controllers */
9461 rc = i_loadStorageControllers(data.storage,
9462 puuidRegistry,
9463 puuidSnapshot);
9464 if (FAILED(rc)) return rc;
9465
9466 /* Shared folders */
9467 for (settings::SharedFoldersList::const_iterator
9468 it = data.llSharedFolders.begin();
9469 it != data.llSharedFolders.end();
9470 ++it)
9471 {
9472 const settings::SharedFolder &sf = *it;
9473
9474 ComObjPtr<SharedFolder> sharedFolder;
9475 /* Check for double entries. Not allowed! */
9476 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9477 if (SUCCEEDED(rc))
9478 return setError(VBOX_E_OBJECT_IN_USE,
9479 tr("Shared folder named '%s' already exists"),
9480 sf.strName.c_str());
9481
9482 /* Create the new shared folder. Don't break on error. This will be
9483 * reported when the machine starts. */
9484 sharedFolder.createObject();
9485 rc = sharedFolder->init(i_getMachine(),
9486 sf.strName,
9487 sf.strHostPath,
9488 RT_BOOL(sf.fWritable),
9489 RT_BOOL(sf.fAutoMount),
9490 sf.strAutoMountPoint,
9491 false /* fFailOnError */);
9492 if (FAILED(rc)) return rc;
9493 mHWData->mSharedFolders.push_back(sharedFolder);
9494 }
9495
9496 // Clipboard
9497 mHWData->mClipboardMode = data.clipboardMode;
9498 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9499
9500 // drag'n'drop
9501 mHWData->mDnDMode = data.dndMode;
9502
9503 // guest settings
9504 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9505
9506 // IO settings
9507 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9508 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9509
9510 // Host PCI devices
9511 for (settings::HostPCIDeviceAttachmentList::const_iterator
9512 it = data.pciAttachments.begin();
9513 it != data.pciAttachments.end();
9514 ++it)
9515 {
9516 const settings::HostPCIDeviceAttachment &hpda = *it;
9517 ComObjPtr<PCIDeviceAttachment> pda;
9518
9519 pda.createObject();
9520 pda->i_loadSettings(this, hpda);
9521 mHWData->mPCIDeviceAssignments.push_back(pda);
9522 }
9523
9524 /*
9525 * (The following isn't really real hardware, but it lives in HWData
9526 * for reasons of convenience.)
9527 */
9528
9529#ifdef VBOX_WITH_GUEST_PROPS
9530 /* Guest properties (optional) */
9531
9532 /* Only load transient guest properties for configs which have saved
9533 * state, because there shouldn't be any for powered off VMs. The same
9534 * logic applies for snapshots, as offline snapshots shouldn't have
9535 * any such properties. They confuse the code in various places.
9536 * Note: can't rely on the machine state, as it isn't set yet. */
9537 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9538 /* apologies for the hacky unconst() usage, but this needs hacking
9539 * actually inconsistent settings into consistency, otherwise there
9540 * will be some corner cases where the inconsistency survives
9541 * surprisingly long without getting fixed, especially for snapshots
9542 * as there are no config changes. */
9543 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9544 for (settings::GuestPropertiesList::iterator
9545 it = llGuestProperties.begin();
9546 it != llGuestProperties.end();
9547 /*nothing*/)
9548 {
9549 const settings::GuestProperty &prop = *it;
9550 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9551 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9552 if ( fSkipTransientGuestProperties
9553 && ( fFlags & GUEST_PROP_F_TRANSIENT
9554 || fFlags & GUEST_PROP_F_TRANSRESET))
9555 {
9556 it = llGuestProperties.erase(it);
9557 continue;
9558 }
9559 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9560 mHWData->mGuestProperties[prop.strName] = property;
9561 ++it;
9562 }
9563#endif /* VBOX_WITH_GUEST_PROPS defined */
9564
9565 rc = i_loadDebugging(pDbg);
9566 if (FAILED(rc))
9567 return rc;
9568
9569 mHWData->mAutostart = *pAutostart;
9570
9571 /* default frontend */
9572 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9573 }
9574 catch (std::bad_alloc &)
9575 {
9576 return E_OUTOFMEMORY;
9577 }
9578
9579 AssertComRC(rc);
9580 return rc;
9581}
9582
9583/**
9584 * Called from i_loadHardware() to load the debugging settings of the
9585 * machine.
9586 *
9587 * @param pDbg Pointer to the settings.
9588 */
9589HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9590{
9591 mHWData->mDebugging = *pDbg;
9592 /* no more processing currently required, this will probably change. */
9593
9594 HRESULT rc = mGuestDebugControl->i_loadSettings(*pDbg);
9595 if (FAILED(rc)) return rc;
9596
9597 return S_OK;
9598}
9599
9600/**
9601 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9602 *
9603 * @param data storage settings.
9604 * @param puuidRegistry media registry ID to set media to or NULL;
9605 * see Machine::i_loadMachineDataFromSettings()
9606 * @param puuidSnapshot snapshot ID
9607 * @return
9608 */
9609HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9610 const Guid *puuidRegistry,
9611 const Guid *puuidSnapshot)
9612{
9613 AssertReturn(!i_isSessionMachine(), E_FAIL);
9614
9615 HRESULT rc = S_OK;
9616
9617 for (settings::StorageControllersList::const_iterator
9618 it = data.llStorageControllers.begin();
9619 it != data.llStorageControllers.end();
9620 ++it)
9621 {
9622 const settings::StorageController &ctlData = *it;
9623
9624 ComObjPtr<StorageController> pCtl;
9625 /* Try to find one with the name first. */
9626 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9627 if (SUCCEEDED(rc))
9628 return setError(VBOX_E_OBJECT_IN_USE,
9629 tr("Storage controller named '%s' already exists"),
9630 ctlData.strName.c_str());
9631
9632 pCtl.createObject();
9633 rc = pCtl->init(this,
9634 ctlData.strName,
9635 ctlData.storageBus,
9636 ctlData.ulInstance,
9637 ctlData.fBootable);
9638 if (FAILED(rc)) return rc;
9639
9640 mStorageControllers->push_back(pCtl);
9641
9642 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9643 if (FAILED(rc)) return rc;
9644
9645 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9646 if (FAILED(rc)) return rc;
9647
9648 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9649 if (FAILED(rc)) return rc;
9650
9651 /* Load the attached devices now. */
9652 rc = i_loadStorageDevices(pCtl,
9653 ctlData,
9654 puuidRegistry,
9655 puuidSnapshot);
9656 if (FAILED(rc)) return rc;
9657 }
9658
9659 return S_OK;
9660}
9661
9662/**
9663 * Called from i_loadStorageControllers for a controller's devices.
9664 *
9665 * @param aStorageController
9666 * @param data
9667 * @param puuidRegistry media registry ID to set media to or NULL; see
9668 * Machine::i_loadMachineDataFromSettings()
9669 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9670 * @return
9671 */
9672HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9673 const settings::StorageController &data,
9674 const Guid *puuidRegistry,
9675 const Guid *puuidSnapshot)
9676{
9677 HRESULT rc = S_OK;
9678
9679 /* paranoia: detect duplicate attachments */
9680 for (settings::AttachedDevicesList::const_iterator
9681 it = data.llAttachedDevices.begin();
9682 it != data.llAttachedDevices.end();
9683 ++it)
9684 {
9685 const settings::AttachedDevice &ad = *it;
9686
9687 for (settings::AttachedDevicesList::const_iterator it2 = it;
9688 it2 != data.llAttachedDevices.end();
9689 ++it2)
9690 {
9691 if (it == it2)
9692 continue;
9693
9694 const settings::AttachedDevice &ad2 = *it2;
9695
9696 if ( ad.lPort == ad2.lPort
9697 && ad.lDevice == ad2.lDevice)
9698 {
9699 return setError(E_FAIL,
9700 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9701 aStorageController->i_getName().c_str(),
9702 ad.lPort,
9703 ad.lDevice,
9704 mUserData->s.strName.c_str());
9705 }
9706 }
9707 }
9708
9709 for (settings::AttachedDevicesList::const_iterator
9710 it = data.llAttachedDevices.begin();
9711 it != data.llAttachedDevices.end();
9712 ++it)
9713 {
9714 const settings::AttachedDevice &dev = *it;
9715 ComObjPtr<Medium> medium;
9716
9717 switch (dev.deviceType)
9718 {
9719 case DeviceType_Floppy:
9720 case DeviceType_DVD:
9721 if (dev.strHostDriveSrc.isNotEmpty())
9722 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9723 false /* fRefresh */, medium);
9724 else
9725 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9726 dev.uuid,
9727 false /* fRefresh */,
9728 false /* aSetError */,
9729 medium);
9730 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9731 // This is not an error. The host drive or UUID might have vanished, so just go
9732 // ahead without this removeable medium attachment
9733 rc = S_OK;
9734 break;
9735
9736 case DeviceType_HardDisk:
9737 {
9738 /* find a hard disk by UUID */
9739 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9740 if (FAILED(rc))
9741 {
9742 if (i_isSnapshotMachine())
9743 {
9744 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9745 // so the user knows that the bad disk is in a snapshot somewhere
9746 com::ErrorInfo info;
9747 return setError(E_FAIL,
9748 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9749 puuidSnapshot->raw(),
9750 info.getText().raw());
9751 }
9752 else
9753 return rc;
9754 }
9755
9756 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9757
9758 if (medium->i_getType() == MediumType_Immutable)
9759 {
9760 if (i_isSnapshotMachine())
9761 return setError(E_FAIL,
9762 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9763 "of the virtual machine '%s' ('%s')"),
9764 medium->i_getLocationFull().c_str(),
9765 dev.uuid.raw(),
9766 puuidSnapshot->raw(),
9767 mUserData->s.strName.c_str(),
9768 mData->m_strConfigFileFull.c_str());
9769
9770 return setError(E_FAIL,
9771 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9772 medium->i_getLocationFull().c_str(),
9773 dev.uuid.raw(),
9774 mUserData->s.strName.c_str(),
9775 mData->m_strConfigFileFull.c_str());
9776 }
9777
9778 if (medium->i_getType() == MediumType_MultiAttach)
9779 {
9780 if (i_isSnapshotMachine())
9781 return setError(E_FAIL,
9782 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9783 "of the virtual machine '%s' ('%s')"),
9784 medium->i_getLocationFull().c_str(),
9785 dev.uuid.raw(),
9786 puuidSnapshot->raw(),
9787 mUserData->s.strName.c_str(),
9788 mData->m_strConfigFileFull.c_str());
9789
9790 return setError(E_FAIL,
9791 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9792 medium->i_getLocationFull().c_str(),
9793 dev.uuid.raw(),
9794 mUserData->s.strName.c_str(),
9795 mData->m_strConfigFileFull.c_str());
9796 }
9797
9798 if ( !i_isSnapshotMachine()
9799 && medium->i_getChildren().size() != 0
9800 )
9801 return setError(E_FAIL,
9802 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9803 "because it has %d differencing child hard disks"),
9804 medium->i_getLocationFull().c_str(),
9805 dev.uuid.raw(),
9806 mUserData->s.strName.c_str(),
9807 mData->m_strConfigFileFull.c_str(),
9808 medium->i_getChildren().size());
9809
9810 if (i_findAttachment(*mMediumAttachments.data(),
9811 medium))
9812 return setError(E_FAIL,
9813 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9814 medium->i_getLocationFull().c_str(),
9815 dev.uuid.raw(),
9816 mUserData->s.strName.c_str(),
9817 mData->m_strConfigFileFull.c_str());
9818
9819 break;
9820 }
9821
9822 default:
9823 return setError(E_FAIL,
9824 tr("Controller '%s' port %u unit %u has device with unknown type (%d) - virtual machine '%s' ('%s')"),
9825 data.strName.c_str(), dev.lPort, dev.lDevice, dev.deviceType,
9826 mUserData->s.strName.c_str(), mData->m_strConfigFileFull.c_str());
9827 }
9828
9829 if (FAILED(rc))
9830 break;
9831
9832 /* Bandwidth groups are loaded at this point. */
9833 ComObjPtr<BandwidthGroup> pBwGroup;
9834
9835 if (!dev.strBwGroup.isEmpty())
9836 {
9837 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9838 if (FAILED(rc))
9839 return setError(E_FAIL,
9840 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9841 medium->i_getLocationFull().c_str(),
9842 dev.strBwGroup.c_str(),
9843 mUserData->s.strName.c_str(),
9844 mData->m_strConfigFileFull.c_str());
9845 pBwGroup->i_reference();
9846 }
9847
9848 const Utf8Str controllerName = aStorageController->i_getName();
9849 ComObjPtr<MediumAttachment> pAttachment;
9850 pAttachment.createObject();
9851 rc = pAttachment->init(this,
9852 medium,
9853 controllerName,
9854 dev.lPort,
9855 dev.lDevice,
9856 dev.deviceType,
9857 false,
9858 dev.fPassThrough,
9859 dev.fTempEject,
9860 dev.fNonRotational,
9861 dev.fDiscard,
9862 dev.fHotPluggable,
9863 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9864 if (FAILED(rc)) break;
9865
9866 /* associate the medium with this machine and snapshot */
9867 if (!medium.isNull())
9868 {
9869 AutoCaller medCaller(medium);
9870 if (FAILED(medCaller.rc())) return medCaller.rc();
9871 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9872
9873 if (i_isSnapshotMachine())
9874 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9875 else
9876 rc = medium->i_addBackReference(mData->mUuid);
9877 /* If the medium->addBackReference fails it sets an appropriate
9878 * error message, so no need to do any guesswork here. */
9879
9880 if (puuidRegistry)
9881 // caller wants registry ID to be set on all attached media (OVF import case)
9882 medium->i_addRegistry(*puuidRegistry);
9883 }
9884
9885 if (FAILED(rc))
9886 break;
9887
9888 /* back up mMediumAttachments to let registeredInit() properly rollback
9889 * on failure (= limited accessibility) */
9890 i_setModified(IsModified_Storage);
9891 mMediumAttachments.backup();
9892 mMediumAttachments->push_back(pAttachment);
9893 }
9894
9895 return rc;
9896}
9897
9898/**
9899 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9900 *
9901 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9902 * @param aSnapshot where to return the found snapshot
9903 * @param aSetError true to set extended error info on failure
9904 */
9905HRESULT Machine::i_findSnapshotById(const Guid &aId,
9906 ComObjPtr<Snapshot> &aSnapshot,
9907 bool aSetError /* = false */)
9908{
9909 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9910
9911 if (!mData->mFirstSnapshot)
9912 {
9913 if (aSetError)
9914 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9915 return E_FAIL;
9916 }
9917
9918 if (aId.isZero())
9919 aSnapshot = mData->mFirstSnapshot;
9920 else
9921 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9922
9923 if (!aSnapshot)
9924 {
9925 if (aSetError)
9926 return setError(E_FAIL,
9927 tr("Could not find a snapshot with UUID {%s}"),
9928 aId.toString().c_str());
9929 return E_FAIL;
9930 }
9931
9932 return S_OK;
9933}
9934
9935/**
9936 * Returns the snapshot with the given name or fails of no such snapshot.
9937 *
9938 * @param strName snapshot name to find
9939 * @param aSnapshot where to return the found snapshot
9940 * @param aSetError true to set extended error info on failure
9941 */
9942HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9943 ComObjPtr<Snapshot> &aSnapshot,
9944 bool aSetError /* = false */)
9945{
9946 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9947
9948 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9949
9950 if (!mData->mFirstSnapshot)
9951 {
9952 if (aSetError)
9953 return setError(VBOX_E_OBJECT_NOT_FOUND,
9954 tr("This machine does not have any snapshots"));
9955 return VBOX_E_OBJECT_NOT_FOUND;
9956 }
9957
9958 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9959
9960 if (!aSnapshot)
9961 {
9962 if (aSetError)
9963 return setError(VBOX_E_OBJECT_NOT_FOUND,
9964 tr("Could not find a snapshot named '%s'"), strName.c_str());
9965 return VBOX_E_OBJECT_NOT_FOUND;
9966 }
9967
9968 return S_OK;
9969}
9970
9971/**
9972 * Returns a storage controller object with the given name.
9973 *
9974 * @param aName storage controller name to find
9975 * @param aStorageController where to return the found storage controller
9976 * @param aSetError true to set extended error info on failure
9977 */
9978HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9979 ComObjPtr<StorageController> &aStorageController,
9980 bool aSetError /* = false */)
9981{
9982 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9983
9984 for (StorageControllerList::const_iterator
9985 it = mStorageControllers->begin();
9986 it != mStorageControllers->end();
9987 ++it)
9988 {
9989 if ((*it)->i_getName() == aName)
9990 {
9991 aStorageController = (*it);
9992 return S_OK;
9993 }
9994 }
9995
9996 if (aSetError)
9997 return setError(VBOX_E_OBJECT_NOT_FOUND,
9998 tr("Could not find a storage controller named '%s'"),
9999 aName.c_str());
10000 return VBOX_E_OBJECT_NOT_FOUND;
10001}
10002
10003/**
10004 * Returns a USB controller object with the given name.
10005 *
10006 * @param aName USB controller name to find
10007 * @param aUSBController where to return the found USB controller
10008 * @param aSetError true to set extended error info on failure
10009 */
10010HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
10011 ComObjPtr<USBController> &aUSBController,
10012 bool aSetError /* = false */)
10013{
10014 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
10015
10016 for (USBControllerList::const_iterator
10017 it = mUSBControllers->begin();
10018 it != mUSBControllers->end();
10019 ++it)
10020 {
10021 if ((*it)->i_getName() == aName)
10022 {
10023 aUSBController = (*it);
10024 return S_OK;
10025 }
10026 }
10027
10028 if (aSetError)
10029 return setError(VBOX_E_OBJECT_NOT_FOUND,
10030 tr("Could not find a storage controller named '%s'"),
10031 aName.c_str());
10032 return VBOX_E_OBJECT_NOT_FOUND;
10033}
10034
10035/**
10036 * Returns the number of USB controller instance of the given type.
10037 *
10038 * @param enmType USB controller type.
10039 */
10040ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
10041{
10042 ULONG cCtrls = 0;
10043
10044 for (USBControllerList::const_iterator
10045 it = mUSBControllers->begin();
10046 it != mUSBControllers->end();
10047 ++it)
10048 {
10049 if ((*it)->i_getControllerType() == enmType)
10050 cCtrls++;
10051 }
10052
10053 return cCtrls;
10054}
10055
10056HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
10057 MediumAttachmentList &atts)
10058{
10059 AutoCaller autoCaller(this);
10060 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10061
10062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10063
10064 for (MediumAttachmentList::const_iterator
10065 it = mMediumAttachments->begin();
10066 it != mMediumAttachments->end();
10067 ++it)
10068 {
10069 const ComObjPtr<MediumAttachment> &pAtt = *it;
10070 // should never happen, but deal with NULL pointers in the list.
10071 AssertContinue(!pAtt.isNull());
10072
10073 // getControllerName() needs caller+read lock
10074 AutoCaller autoAttCaller(pAtt);
10075 if (FAILED(autoAttCaller.rc()))
10076 {
10077 atts.clear();
10078 return autoAttCaller.rc();
10079 }
10080 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
10081
10082 if (pAtt->i_getControllerName() == aName)
10083 atts.push_back(pAtt);
10084 }
10085
10086 return S_OK;
10087}
10088
10089
10090/**
10091 * Helper for #i_saveSettings. Cares about renaming the settings directory and
10092 * file if the machine name was changed and about creating a new settings file
10093 * if this is a new machine.
10094 *
10095 * @note Must be never called directly but only from #saveSettings().
10096 */
10097HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
10098 bool *pfSettingsFileIsNew)
10099{
10100 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10101
10102 HRESULT rc = S_OK;
10103
10104 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
10105 /// @todo need to handle primary group change, too
10106
10107 /* attempt to rename the settings file if machine name is changed */
10108 if ( mUserData->s.fNameSync
10109 && mUserData.isBackedUp()
10110 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
10111 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
10112 )
10113 {
10114 bool dirRenamed = false;
10115 bool fileRenamed = false;
10116
10117 Utf8Str configFile, newConfigFile;
10118 Utf8Str configFilePrev, newConfigFilePrev;
10119 Utf8Str NVRAMFile, newNVRAMFile;
10120 Utf8Str configDir, newConfigDir;
10121
10122 do
10123 {
10124 int vrc = VINF_SUCCESS;
10125
10126 Utf8Str name = mUserData.backedUpData()->s.strName;
10127 Utf8Str newName = mUserData->s.strName;
10128 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
10129 if (group == "/")
10130 group.setNull();
10131 Utf8Str newGroup = mUserData->s.llGroups.front();
10132 if (newGroup == "/")
10133 newGroup.setNull();
10134
10135 configFile = mData->m_strConfigFileFull;
10136
10137 /* first, rename the directory if it matches the group and machine name */
10138 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
10139 /** @todo hack, make somehow use of ComposeMachineFilename */
10140 if (mUserData->s.fDirectoryIncludesUUID)
10141 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10142 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
10143 /** @todo hack, make somehow use of ComposeMachineFilename */
10144 if (mUserData->s.fDirectoryIncludesUUID)
10145 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10146 configDir = configFile;
10147 configDir.stripFilename();
10148 newConfigDir = configDir;
10149 if ( configDir.length() >= groupPlusName.length()
10150 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
10151 groupPlusName.c_str()))
10152 {
10153 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10154 Utf8Str newConfigBaseDir(newConfigDir);
10155 newConfigDir.append(newGroupPlusName);
10156 /* consistency: use \ if appropriate on the platform */
10157 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10158 /* new dir and old dir cannot be equal here because of 'if'
10159 * above and because name != newName */
10160 Assert(configDir != newConfigDir);
10161 if (!fSettingsFileIsNew)
10162 {
10163 /* perform real rename only if the machine is not new */
10164 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10165 if ( vrc == VERR_FILE_NOT_FOUND
10166 || vrc == VERR_PATH_NOT_FOUND)
10167 {
10168 /* create the parent directory, then retry renaming */
10169 Utf8Str parent(newConfigDir);
10170 parent.stripFilename();
10171 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10172 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10173 }
10174 if (RT_FAILURE(vrc))
10175 {
10176 rc = setErrorBoth(E_FAIL, vrc,
10177 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10178 configDir.c_str(),
10179 newConfigDir.c_str(),
10180 vrc);
10181 break;
10182 }
10183 /* delete subdirectories which are no longer needed */
10184 Utf8Str dir(configDir);
10185 dir.stripFilename();
10186 while (dir != newConfigBaseDir && dir != ".")
10187 {
10188 vrc = RTDirRemove(dir.c_str());
10189 if (RT_FAILURE(vrc))
10190 break;
10191 dir.stripFilename();
10192 }
10193 dirRenamed = true;
10194 }
10195 }
10196
10197 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10198
10199 /* then try to rename the settings file itself */
10200 if (newConfigFile != configFile)
10201 {
10202 /* get the path to old settings file in renamed directory */
10203 Assert(mData->m_strConfigFileFull == configFile);
10204 configFile.printf("%s%c%s",
10205 newConfigDir.c_str(),
10206 RTPATH_DELIMITER,
10207 RTPathFilename(mData->m_strConfigFileFull.c_str()));
10208 if (!fSettingsFileIsNew)
10209 {
10210 /* perform real rename only if the machine is not new */
10211 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10212 if (RT_FAILURE(vrc))
10213 {
10214 rc = setErrorBoth(E_FAIL, vrc,
10215 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10216 configFile.c_str(),
10217 newConfigFile.c_str(),
10218 vrc);
10219 break;
10220 }
10221 fileRenamed = true;
10222 configFilePrev = configFile;
10223 configFilePrev += "-prev";
10224 newConfigFilePrev = newConfigFile;
10225 newConfigFilePrev += "-prev";
10226 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10227 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
10228 if (NVRAMFile.isNotEmpty())
10229 {
10230 // in the NVRAM file path, replace the old directory with the new directory
10231 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
10232 {
10233 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
10234 NVRAMFile = newConfigDir + strNVRAMFile;
10235 }
10236 newNVRAMFile = newConfigFile;
10237 newNVRAMFile.stripSuffix();
10238 newNVRAMFile += ".nvram";
10239 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
10240 }
10241 }
10242 }
10243
10244 // update m_strConfigFileFull amd mConfigFile
10245 mData->m_strConfigFileFull = newConfigFile;
10246 // compute the relative path too
10247 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10248
10249 // store the old and new so that VirtualBox::i_saveSettings() can update
10250 // the media registry
10251 if ( mData->mRegistered
10252 && (configDir != newConfigDir || configFile != newConfigFile))
10253 {
10254 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10255
10256 if (pfNeedsGlobalSaveSettings)
10257 *pfNeedsGlobalSaveSettings = true;
10258 }
10259
10260 // in the saved state file path, replace the old directory with the new directory
10261 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10262 {
10263 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
10264 mSSData->strStateFilePath = newConfigDir + strStateFileName;
10265 }
10266 if (newNVRAMFile.isNotEmpty())
10267 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
10268
10269 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
10270 if (mData->mFirstSnapshot)
10271 {
10272 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10273 newConfigDir.c_str());
10274 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
10275 newConfigDir.c_str());
10276 }
10277 }
10278 while (0);
10279
10280 if (FAILED(rc))
10281 {
10282 /* silently try to rename everything back */
10283 if (fileRenamed)
10284 {
10285 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10286 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10287 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
10288 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
10289 }
10290 if (dirRenamed)
10291 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10292 }
10293
10294 if (FAILED(rc)) return rc;
10295 }
10296
10297 if (fSettingsFileIsNew)
10298 {
10299 /* create a virgin config file */
10300 int vrc = VINF_SUCCESS;
10301
10302 /* ensure the settings directory exists */
10303 Utf8Str path(mData->m_strConfigFileFull);
10304 path.stripFilename();
10305 if (!RTDirExists(path.c_str()))
10306 {
10307 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10308 if (RT_FAILURE(vrc))
10309 {
10310 return setErrorBoth(E_FAIL, vrc,
10311 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10312 path.c_str(),
10313 vrc);
10314 }
10315 }
10316
10317 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10318 path = mData->m_strConfigFileFull;
10319 RTFILE f = NIL_RTFILE;
10320 vrc = RTFileOpen(&f, path.c_str(),
10321 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10322 if (RT_FAILURE(vrc))
10323 return setErrorBoth(E_FAIL, vrc,
10324 tr("Could not create the settings file '%s' (%Rrc)"),
10325 path.c_str(),
10326 vrc);
10327 RTFileClose(f);
10328 }
10329 if (pfSettingsFileIsNew)
10330 *pfSettingsFileIsNew = fSettingsFileIsNew;
10331
10332 return rc;
10333}
10334
10335/**
10336 * Saves and commits machine data, user data and hardware data.
10337 *
10338 * Note that on failure, the data remains uncommitted.
10339 *
10340 * @a aFlags may combine the following flags:
10341 *
10342 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10343 * Used when saving settings after an operation that makes them 100%
10344 * correspond to the settings from the current snapshot.
10345 * - SaveS_Force: settings will be saved without doing a deep compare of the
10346 * settings structures. This is used when this is called because snapshots
10347 * have changed to avoid the overhead of the deep compare.
10348 *
10349 * @note Must be called from under this object's write lock. Locks children for
10350 * writing.
10351 *
10352 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10353 * initialized to false and that will be set to true by this function if
10354 * the caller must invoke VirtualBox::i_saveSettings() because the global
10355 * settings have changed. This will happen if a machine rename has been
10356 * saved and the global machine and media registries will therefore need
10357 * updating.
10358 * @param alock Reference to the lock for this machine object.
10359 * @param aFlags Flags.
10360 */
10361HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10362 AutoWriteLock &alock,
10363 int aFlags /*= 0*/)
10364{
10365 LogFlowThisFuncEnter();
10366
10367 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10368
10369 /* make sure child objects are unable to modify the settings while we are
10370 * saving them */
10371 i_ensureNoStateDependencies(alock);
10372
10373 AssertReturn(!i_isSnapshotMachine(),
10374 E_FAIL);
10375
10376 if (!mData->mAccessible)
10377 return setError(VBOX_E_INVALID_VM_STATE,
10378 tr("The machine is not accessible, so cannot save settings"));
10379
10380 HRESULT rc = S_OK;
10381 PCVBOXCRYPTOIF pCryptoIf = NULL;
10382 const char *pszPassword = NULL;
10383 SecretKey *pKey = NULL;
10384
10385#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10386 if (mData->mstrKeyId.isNotEmpty())
10387 {
10388 /* VM is going to be encrypted. */
10389 alock.release(); /** @todo Revise the locking. */
10390 rc = mParent->i_retainCryptoIf(&pCryptoIf);
10391 alock.acquire();
10392 if (FAILED(rc)) return rc; /* Error is set. */
10393
10394 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
10395 if (RT_SUCCESS(vrc))
10396 pszPassword = (const char *)pKey->getKeyBuffer();
10397 else
10398 {
10399 mParent->i_releaseCryptoIf(pCryptoIf);
10400 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
10401 tr("Failed to retain VM encryption password using ID '%s' with %Rrc"),
10402 mData->mstrKeyId.c_str(), vrc);
10403 }
10404 }
10405#else
10406 RT_NOREF(pKey);
10407#endif
10408
10409 bool fNeedsWrite = false;
10410 bool fSettingsFileIsNew = false;
10411
10412 /* First, prepare to save settings. It will care about renaming the
10413 * settings directory and file if the machine name was changed and about
10414 * creating a new settings file if this is a new machine. */
10415 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings,
10416 &fSettingsFileIsNew);
10417 if (FAILED(rc))
10418 {
10419#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10420 if (pCryptoIf)
10421 {
10422 alock.release(); /** @todo Revise the locking. */
10423 mParent->i_releaseCryptoIf(pCryptoIf);
10424 alock.acquire();
10425 }
10426 if (pKey)
10427 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10428#endif
10429 return rc;
10430 }
10431
10432 // keep a pointer to the current settings structures
10433 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10434 settings::MachineConfigFile *pNewConfig = NULL;
10435
10436 try
10437 {
10438 // make a fresh one to have everyone write stuff into
10439 pNewConfig = new settings::MachineConfigFile(NULL);
10440 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10441#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10442 pNewConfig->strKeyId = mData->mstrKeyId;
10443 pNewConfig->strKeyStore = mData->mstrKeyStore;
10444#endif
10445
10446 // now go and copy all the settings data from COM to the settings structures
10447 // (this calls i_saveSettings() on all the COM objects in the machine)
10448 i_copyMachineDataToSettings(*pNewConfig);
10449
10450 if (aFlags & SaveS_ResetCurStateModified)
10451 {
10452 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10453 mData->mCurrentStateModified = FALSE;
10454 fNeedsWrite = true; // always, no need to compare
10455 }
10456 else if (aFlags & SaveS_Force)
10457 {
10458 fNeedsWrite = true; // always, no need to compare
10459 }
10460 else
10461 {
10462 if (!mData->mCurrentStateModified)
10463 {
10464 // do a deep compare of the settings that we just saved with the settings
10465 // previously stored in the config file; this invokes MachineConfigFile::operator==
10466 // which does a deep compare of all the settings, which is expensive but less expensive
10467 // than writing out XML in vain
10468 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10469
10470 // could still be modified if any settings changed
10471 mData->mCurrentStateModified = fAnySettingsChanged;
10472
10473 fNeedsWrite = fAnySettingsChanged;
10474 }
10475 else
10476 fNeedsWrite = true;
10477 }
10478
10479 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10480
10481 if (fNeedsWrite)
10482 {
10483 // now spit it all out!
10484 pNewConfig->write(mData->m_strConfigFileFull, pCryptoIf, pszPassword);
10485 if (aFlags & SaveS_RemoveBackup)
10486 i_deleteFile(mData->m_strConfigFileFull + "-prev", true /* fIgnoreFailures */);
10487 }
10488
10489 mData->pMachineConfigFile = pNewConfig;
10490 delete pOldConfig;
10491 i_commit();
10492
10493 // after saving settings, we are no longer different from the XML on disk
10494 mData->flModifications = 0;
10495 }
10496 catch (HRESULT err)
10497 {
10498 // we assume that error info is set by the thrower
10499 rc = err;
10500
10501 // delete any newly created settings file
10502 if (fSettingsFileIsNew)
10503 i_deleteFile(mData->m_strConfigFileFull, true /* fIgnoreFailures */);
10504
10505 // restore old config
10506 delete pNewConfig;
10507 mData->pMachineConfigFile = pOldConfig;
10508 }
10509 catch (...)
10510 {
10511 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10512 }
10513
10514#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10515 if (pCryptoIf)
10516 {
10517 alock.release(); /** @todo Revise the locking. */
10518 mParent->i_releaseCryptoIf(pCryptoIf);
10519 alock.acquire();
10520 }
10521 if (pKey)
10522 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10523#endif
10524
10525 if (fNeedsWrite)
10526 {
10527 /* Fire the data change event, even on failure (since we've already
10528 * committed all data). This is done only for SessionMachines because
10529 * mutable Machine instances are always not registered (i.e. private
10530 * to the client process that creates them) and thus don't need to
10531 * inform callbacks. */
10532 if (i_isSessionMachine())
10533 mParent->i_onMachineDataChanged(mData->mUuid);
10534 }
10535
10536 LogFlowThisFunc(("rc=%08X\n", rc));
10537 LogFlowThisFuncLeave();
10538 return rc;
10539}
10540
10541/**
10542 * Implementation for saving the machine settings into the given
10543 * settings::MachineConfigFile instance. This copies machine extradata
10544 * from the previous machine config file in the instance data, if any.
10545 *
10546 * This gets called from two locations:
10547 *
10548 * -- Machine::i_saveSettings(), during the regular XML writing;
10549 *
10550 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10551 * exported to OVF and we write the VirtualBox proprietary XML
10552 * into a <vbox:Machine> tag.
10553 *
10554 * This routine fills all the fields in there, including snapshots, *except*
10555 * for the following:
10556 *
10557 * -- fCurrentStateModified. There is some special logic associated with that.
10558 *
10559 * The caller can then call MachineConfigFile::write() or do something else
10560 * with it.
10561 *
10562 * Caller must hold the machine lock!
10563 *
10564 * This throws XML errors and HRESULT, so the caller must have a catch block!
10565 */
10566void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10567{
10568 // deep copy extradata, being extra careful with self assignment (the STL
10569 // map assignment on Mac OS X clang based Xcode isn't checking)
10570 if (&config != mData->pMachineConfigFile)
10571 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10572
10573 config.uuid = mData->mUuid;
10574
10575 // copy name, description, OS type, teleport, UTC etc.
10576 config.machineUserData = mUserData->s;
10577
10578#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10579 config.strStateKeyId = mSSData->strStateKeyId;
10580 config.strStateKeyStore = mSSData->strStateKeyStore;
10581 config.strLogKeyId = mData->mstrLogKeyId;
10582 config.strLogKeyStore = mData->mstrLogKeyStore;
10583#endif
10584
10585 if ( mData->mMachineState == MachineState_Saved
10586 || mData->mMachineState == MachineState_AbortedSaved
10587 || mData->mMachineState == MachineState_Restoring
10588 // when doing certain snapshot operations we may or may not have
10589 // a saved state in the current state, so keep everything as is
10590 || ( ( mData->mMachineState == MachineState_Snapshotting
10591 || mData->mMachineState == MachineState_DeletingSnapshot
10592 || mData->mMachineState == MachineState_RestoringSnapshot)
10593 && (!mSSData->strStateFilePath.isEmpty())
10594 )
10595 )
10596 {
10597 Assert(!mSSData->strStateFilePath.isEmpty());
10598 /* try to make the file name relative to the settings file dir */
10599 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10600 }
10601 else
10602 {
10603 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10604 config.strStateFile.setNull();
10605 }
10606
10607 if (mData->mCurrentSnapshot)
10608 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10609 else
10610 config.uuidCurrentSnapshot.clear();
10611
10612 config.timeLastStateChange = mData->mLastStateChange;
10613 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10614 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10615
10616 HRESULT hrc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart, config.recordingSettings);
10617 if (FAILED(hrc)) throw hrc;
10618
10619 // save machine's media registry if this is VirtualBox 4.0 or later
10620 if (config.canHaveOwnMediaRegistry())
10621 {
10622 // determine machine folder
10623 Utf8Str strMachineFolder = i_getSettingsFileFull();
10624 strMachineFolder.stripFilename();
10625 mParent->i_saveMediaRegistry(config.mediaRegistry,
10626 i_getId(), // only media with registry ID == machine UUID
10627 strMachineFolder);
10628 // this throws HRESULT
10629 }
10630
10631 // save snapshots
10632 hrc = i_saveAllSnapshots(config);
10633 if (FAILED(hrc)) throw hrc;
10634}
10635
10636/**
10637 * Saves all snapshots of the machine into the given machine config file. Called
10638 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10639 * @param config
10640 * @return
10641 */
10642HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10643{
10644 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10645
10646 HRESULT rc = S_OK;
10647
10648 try
10649 {
10650 config.llFirstSnapshot.clear();
10651
10652 if (mData->mFirstSnapshot)
10653 {
10654 // the settings use a list for "the first snapshot"
10655 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10656
10657 // get reference to the snapshot on the list and work on that
10658 // element straight in the list to avoid excessive copying later
10659 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10660 if (FAILED(rc)) throw rc;
10661 }
10662
10663// if (mType == IsSessionMachine)
10664// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10665
10666 }
10667 catch (HRESULT err)
10668 {
10669 /* we assume that error info is set by the thrower */
10670 rc = err;
10671 }
10672 catch (...)
10673 {
10674 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10675 }
10676
10677 return rc;
10678}
10679
10680/**
10681 * Saves the VM hardware configuration. It is assumed that the
10682 * given node is empty.
10683 *
10684 * @param data Reference to the settings object for the hardware config.
10685 * @param pDbg Pointer to the settings object for the debugging config
10686 * which happens to live in mHWData.
10687 * @param pAutostart Pointer to the settings object for the autostart config
10688 * which happens to live in mHWData.
10689 * @param recording Reference to reecording settings.
10690 */
10691HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10692 settings::Autostart *pAutostart, settings::RecordingSettings &recording)
10693{
10694 HRESULT rc = S_OK;
10695
10696 try
10697 {
10698 /* The hardware version attribute (optional).
10699 Automatically upgrade from 1 to current default hardware version
10700 when there is no saved state. (ugly!) */
10701 if ( mHWData->mHWVersion == "1"
10702 && mSSData->strStateFilePath.isEmpty()
10703 )
10704 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10705
10706 data.strVersion = mHWData->mHWVersion;
10707 data.uuid = mHWData->mHardwareUUID;
10708
10709 // CPU
10710 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10711 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10712 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10713 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10714 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10715 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10716 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10717 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10718 data.fPAE = !!mHWData->mPAEEnabled;
10719 data.enmLongMode = mHWData->mLongMode;
10720 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10721 data.fAPIC = !!mHWData->mAPIC;
10722 data.fX2APIC = !!mHWData->mX2APIC;
10723 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10724 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10725 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10726 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10727 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10728 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10729 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10730 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10731 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10732 data.cCPUs = mHWData->mCPUCount;
10733 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10734 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10735 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10736 data.strCpuProfile = mHWData->mCpuProfile;
10737
10738 data.llCpus.clear();
10739 if (data.fCpuHotPlug)
10740 {
10741 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10742 {
10743 if (mHWData->mCPUAttached[idx])
10744 {
10745 settings::Cpu cpu;
10746 cpu.ulId = idx;
10747 data.llCpus.push_back(cpu);
10748 }
10749 }
10750 }
10751
10752 /* Standard and Extended CPUID leafs. */
10753 data.llCpuIdLeafs.clear();
10754 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10755
10756 // memory
10757 data.ulMemorySizeMB = mHWData->mMemorySize;
10758 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10759
10760 // firmware
10761 data.firmwareType = mHWData->mFirmwareType;
10762
10763 // HID
10764 data.pointingHIDType = mHWData->mPointingHIDType;
10765 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10766
10767 // chipset
10768 data.chipsetType = mHWData->mChipsetType;
10769
10770 // iommu
10771 data.iommuType = mHWData->mIommuType;
10772
10773 // paravirt
10774 data.paravirtProvider = mHWData->mParavirtProvider;
10775 data.strParavirtDebug = mHWData->mParavirtDebug;
10776
10777 // emulated USB card reader
10778 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10779
10780 // HPET
10781 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10782
10783 // boot order
10784 data.mapBootOrder.clear();
10785 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10786 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10787
10788 /* VRDEServer settings (optional) */
10789 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10790 if (FAILED(rc)) throw rc;
10791
10792 /* BIOS settings (required) */
10793 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10794 if (FAILED(rc)) throw rc;
10795
10796 /* Recording settings. */
10797 rc = mRecordingSettings->i_saveSettings(recording);
10798 if (FAILED(rc)) throw rc;
10799
10800 /* Trusted Platform Module settings (required) */
10801 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10802 if (FAILED(rc)) throw rc;
10803
10804 /* NVRAM settings (required) */
10805 rc = mNvramStore->i_saveSettings(data.nvramSettings);
10806 if (FAILED(rc)) throw rc;
10807
10808 /* GraphicsAdapter settings (required) */
10809 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10810 if (FAILED(rc)) throw rc;
10811
10812 /* USB Controller (required) */
10813 data.usbSettings.llUSBControllers.clear();
10814 for (USBControllerList::const_iterator
10815 it = mUSBControllers->begin();
10816 it != mUSBControllers->end();
10817 ++it)
10818 {
10819 ComObjPtr<USBController> ctrl = *it;
10820 settings::USBController settingsCtrl;
10821
10822 settingsCtrl.strName = ctrl->i_getName();
10823 settingsCtrl.enmType = ctrl->i_getControllerType();
10824
10825 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10826 }
10827
10828 /* USB device filters (required) */
10829 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10830 if (FAILED(rc)) throw rc;
10831
10832 /* Network adapters (required) */
10833 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10834 data.llNetworkAdapters.clear();
10835 /* Write out only the nominal number of network adapters for this
10836 * chipset type. Since Machine::commit() hasn't been called there
10837 * may be extra NIC settings in the vector. */
10838 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10839 {
10840 settings::NetworkAdapter nic;
10841 nic.ulSlot = (uint32_t)slot;
10842 /* paranoia check... must not be NULL, but must not crash either. */
10843 if (mNetworkAdapters[slot])
10844 {
10845 if (mNetworkAdapters[slot]->i_hasDefaults())
10846 continue;
10847
10848 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10849 if (FAILED(rc)) throw rc;
10850
10851 data.llNetworkAdapters.push_back(nic);
10852 }
10853 }
10854
10855 /* Serial ports */
10856 data.llSerialPorts.clear();
10857 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10858 {
10859 if (mSerialPorts[slot]->i_hasDefaults())
10860 continue;
10861
10862 settings::SerialPort s;
10863 s.ulSlot = slot;
10864 rc = mSerialPorts[slot]->i_saveSettings(s);
10865 if (FAILED(rc)) return rc;
10866
10867 data.llSerialPorts.push_back(s);
10868 }
10869
10870 /* Parallel ports */
10871 data.llParallelPorts.clear();
10872 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10873 {
10874 if (mParallelPorts[slot]->i_hasDefaults())
10875 continue;
10876
10877 settings::ParallelPort p;
10878 p.ulSlot = slot;
10879 rc = mParallelPorts[slot]->i_saveSettings(p);
10880 if (FAILED(rc)) return rc;
10881
10882 data.llParallelPorts.push_back(p);
10883 }
10884
10885 /* Audio settings */
10886 rc = mAudioSettings->i_saveSettings(data.audioAdapter);
10887 if (FAILED(rc)) return rc;
10888
10889 rc = i_saveStorageControllers(data.storage);
10890 if (FAILED(rc)) return rc;
10891
10892 /* Shared folders */
10893 data.llSharedFolders.clear();
10894 for (HWData::SharedFolderList::const_iterator
10895 it = mHWData->mSharedFolders.begin();
10896 it != mHWData->mSharedFolders.end();
10897 ++it)
10898 {
10899 SharedFolder *pSF = *it;
10900 AutoCaller sfCaller(pSF);
10901 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10902 settings::SharedFolder sf;
10903 sf.strName = pSF->i_getName();
10904 sf.strHostPath = pSF->i_getHostPath();
10905 sf.fWritable = !!pSF->i_isWritable();
10906 sf.fAutoMount = !!pSF->i_isAutoMounted();
10907 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10908
10909 data.llSharedFolders.push_back(sf);
10910 }
10911
10912 // clipboard
10913 data.clipboardMode = mHWData->mClipboardMode;
10914 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10915
10916 // drag'n'drop
10917 data.dndMode = mHWData->mDnDMode;
10918
10919 /* Guest */
10920 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10921
10922 // IO settings
10923 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10924 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10925
10926 /* BandwidthControl (required) */
10927 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10928 if (FAILED(rc)) throw rc;
10929
10930 /* Host PCI devices */
10931 data.pciAttachments.clear();
10932 for (HWData::PCIDeviceAssignmentList::const_iterator
10933 it = mHWData->mPCIDeviceAssignments.begin();
10934 it != mHWData->mPCIDeviceAssignments.end();
10935 ++it)
10936 {
10937 ComObjPtr<PCIDeviceAttachment> pda = *it;
10938 settings::HostPCIDeviceAttachment hpda;
10939
10940 rc = pda->i_saveSettings(hpda);
10941 if (FAILED(rc)) throw rc;
10942
10943 data.pciAttachments.push_back(hpda);
10944 }
10945
10946 // guest properties
10947 data.llGuestProperties.clear();
10948#ifdef VBOX_WITH_GUEST_PROPS
10949 for (HWData::GuestPropertyMap::const_iterator
10950 it = mHWData->mGuestProperties.begin();
10951 it != mHWData->mGuestProperties.end();
10952 ++it)
10953 {
10954 HWData::GuestProperty property = it->second;
10955
10956 /* Remove transient guest properties at shutdown unless we
10957 * are saving state. Note that restoring snapshot intentionally
10958 * keeps them, they will be removed if appropriate once the final
10959 * machine state is set (as crashes etc. need to work). */
10960 if ( ( mData->mMachineState == MachineState_PoweredOff
10961 || mData->mMachineState == MachineState_Aborted
10962 || mData->mMachineState == MachineState_Teleported)
10963 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10964 continue;
10965 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10966 prop.strName = it->first;
10967 prop.strValue = property.strValue;
10968 prop.timestamp = (uint64_t)property.mTimestamp;
10969 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10970 GuestPropWriteFlags(property.mFlags, szFlags);
10971 prop.strFlags = szFlags;
10972
10973 data.llGuestProperties.push_back(prop);
10974 }
10975
10976 /* I presume this doesn't require a backup(). */
10977 mData->mGuestPropertiesModified = FALSE;
10978#endif /* VBOX_WITH_GUEST_PROPS defined */
10979
10980 rc = mGuestDebugControl->i_saveSettings(mHWData->mDebugging);
10981 if (FAILED(rc)) throw rc;
10982
10983 *pDbg = mHWData->mDebugging; /// @todo r=aeichner: Move this to guest debug control. */
10984 *pAutostart = mHWData->mAutostart;
10985
10986 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10987 }
10988 catch (std::bad_alloc &)
10989 {
10990 return E_OUTOFMEMORY;
10991 }
10992
10993 AssertComRC(rc);
10994 return rc;
10995}
10996
10997/**
10998 * Saves the storage controller configuration.
10999 *
11000 * @param data storage settings.
11001 */
11002HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
11003{
11004 data.llStorageControllers.clear();
11005
11006 for (StorageControllerList::const_iterator
11007 it = mStorageControllers->begin();
11008 it != mStorageControllers->end();
11009 ++it)
11010 {
11011 HRESULT rc;
11012 ComObjPtr<StorageController> pCtl = *it;
11013
11014 settings::StorageController ctl;
11015 ctl.strName = pCtl->i_getName();
11016 ctl.controllerType = pCtl->i_getControllerType();
11017 ctl.storageBus = pCtl->i_getStorageBus();
11018 ctl.ulInstance = pCtl->i_getInstance();
11019 ctl.fBootable = pCtl->i_getBootable();
11020
11021 /* Save the port count. */
11022 ULONG portCount;
11023 rc = pCtl->COMGETTER(PortCount)(&portCount);
11024 ComAssertComRCRet(rc, rc);
11025 ctl.ulPortCount = portCount;
11026
11027 /* Save fUseHostIOCache */
11028 BOOL fUseHostIOCache;
11029 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
11030 ComAssertComRCRet(rc, rc);
11031 ctl.fUseHostIOCache = !!fUseHostIOCache;
11032
11033 /* save the devices now. */
11034 rc = i_saveStorageDevices(pCtl, ctl);
11035 ComAssertComRCRet(rc, rc);
11036
11037 data.llStorageControllers.push_back(ctl);
11038 }
11039
11040 return S_OK;
11041}
11042
11043/**
11044 * Saves the hard disk configuration.
11045 */
11046HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
11047 settings::StorageController &data)
11048{
11049 MediumAttachmentList atts;
11050
11051 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
11052 if (FAILED(rc)) return rc;
11053
11054 data.llAttachedDevices.clear();
11055 for (MediumAttachmentList::const_iterator
11056 it = atts.begin();
11057 it != atts.end();
11058 ++it)
11059 {
11060 settings::AttachedDevice dev;
11061 IMediumAttachment *iA = *it;
11062 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
11063 Medium *pMedium = pAttach->i_getMedium();
11064
11065 dev.deviceType = pAttach->i_getType();
11066 dev.lPort = pAttach->i_getPort();
11067 dev.lDevice = pAttach->i_getDevice();
11068 dev.fPassThrough = pAttach->i_getPassthrough();
11069 dev.fHotPluggable = pAttach->i_getHotPluggable();
11070 if (pMedium)
11071 {
11072 if (pMedium->i_isHostDrive())
11073 dev.strHostDriveSrc = pMedium->i_getLocationFull();
11074 else
11075 dev.uuid = pMedium->i_getId();
11076 dev.fTempEject = pAttach->i_getTempEject();
11077 dev.fNonRotational = pAttach->i_getNonRotational();
11078 dev.fDiscard = pAttach->i_getDiscard();
11079 }
11080
11081 dev.strBwGroup = pAttach->i_getBandwidthGroup();
11082
11083 data.llAttachedDevices.push_back(dev);
11084 }
11085
11086 return S_OK;
11087}
11088
11089/**
11090 * Saves machine state settings as defined by aFlags
11091 * (SaveSTS_* values).
11092 *
11093 * @param aFlags Combination of SaveSTS_* flags.
11094 *
11095 * @note Locks objects for writing.
11096 */
11097HRESULT Machine::i_saveStateSettings(int aFlags)
11098{
11099 if (aFlags == 0)
11100 return S_OK;
11101
11102 AutoCaller autoCaller(this);
11103 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11104
11105 /* This object's write lock is also necessary to serialize file access
11106 * (prevent concurrent reads and writes) */
11107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11108
11109 HRESULT rc = S_OK;
11110
11111 Assert(mData->pMachineConfigFile);
11112
11113 try
11114 {
11115 if (aFlags & SaveSTS_CurStateModified)
11116 mData->pMachineConfigFile->fCurrentStateModified = true;
11117
11118 if (aFlags & SaveSTS_StateFilePath)
11119 {
11120 if (!mSSData->strStateFilePath.isEmpty())
11121 /* try to make the file name relative to the settings file dir */
11122 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
11123 else
11124 mData->pMachineConfigFile->strStateFile.setNull();
11125 }
11126
11127 if (aFlags & SaveSTS_StateTimeStamp)
11128 {
11129 Assert( mData->mMachineState != MachineState_Aborted
11130 || mSSData->strStateFilePath.isEmpty());
11131
11132 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
11133
11134 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
11135 || mData->mMachineState == MachineState_AbortedSaved);
11136/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
11137 }
11138
11139 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
11140 }
11141 catch (...)
11142 {
11143 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
11144 }
11145
11146 return rc;
11147}
11148
11149/**
11150 * Ensures that the given medium is added to a media registry. If this machine
11151 * was created with 4.0 or later, then the machine registry is used. Otherwise
11152 * the global VirtualBox media registry is used.
11153 *
11154 * Caller must NOT hold machine lock, media tree or any medium locks!
11155 *
11156 * @param pMedium
11157 */
11158void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
11159{
11160 /* Paranoia checks: do not hold machine or media tree locks. */
11161 AssertReturnVoid(!isWriteLockOnCurrentThread());
11162 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
11163
11164 ComObjPtr<Medium> pBase;
11165 {
11166 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11167 pBase = pMedium->i_getBase();
11168 }
11169
11170 /* Paranoia checks: do not hold medium locks. */
11171 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
11172 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
11173
11174 // decide which medium registry to use now that the medium is attached:
11175 Guid uuid;
11176 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
11177 if (fCanHaveOwnMediaRegistry)
11178 // machine XML is VirtualBox 4.0 or higher:
11179 uuid = i_getId(); // machine UUID
11180 else
11181 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
11182
11183 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
11184 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11185 if (pMedium->i_addRegistry(uuid))
11186 mParent->i_markRegistryModified(uuid);
11187
11188 /* For more complex hard disk structures it can happen that the base
11189 * medium isn't yet associated with any medium registry. Do that now. */
11190 if (pMedium != pBase)
11191 {
11192 /* Tree lock needed by Medium::addRegistryAll. */
11193 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11194 if (fCanHaveOwnMediaRegistry && pBase->i_removeRegistryAll(mParent->i_getGlobalRegistryId()))
11195 {
11196 treeLock.release();
11197 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11198 treeLock.acquire();
11199 }
11200 if (pBase->i_addRegistryAll(uuid))
11201 {
11202 treeLock.release();
11203 mParent->i_markRegistryModified(uuid);
11204 }
11205 }
11206}
11207
11208/**
11209 * Physically deletes a file belonging to a machine.
11210 *
11211 * @returns HRESULT
11212 * @retval VBOX_E_FILE_ERROR on failure.
11213 * @param strFile File to delete.
11214 * @param fIgnoreFailures Whether to ignore deletion failures. Defaults to \c false.
11215 * VERR_FILE_NOT_FOUND and VERR_PATH_NOT_FOUND always will be ignored.
11216 * @param strWhat File hint which will be used when setting an error. Optional.
11217 * @param prc Where to return IPRT's error code on failure. Optional and can be NULL.
11218 */
11219HRESULT Machine::i_deleteFile(const Utf8Str &strFile, bool fIgnoreFailures /* = false */,
11220 const Utf8Str &strWhat /* = "" */, int *prc /* = NULL */)
11221{
11222 AssertReturn(strFile.isNotEmpty(), E_INVALIDARG);
11223
11224 HRESULT hrc = S_OK;
11225
11226 LogFunc(("Deleting file '%s'\n", strFile.c_str()));
11227
11228 int vrc = RTFileDelete(strFile.c_str());
11229 if (RT_FAILURE(vrc))
11230 {
11231 if ( !fIgnoreFailures
11232 /* Don't (externally) bitch about stuff which doesn't exist. */
11233 && ( vrc != VERR_FILE_NOT_FOUND
11234 && vrc != VERR_PATH_NOT_FOUND
11235 )
11236 )
11237 {
11238 LogRel(("Deleting file '%s' failed: %Rrc\n", strFile.c_str(), vrc));
11239
11240 Utf8StrFmt strError("Error deleting %s '%s' (%Rrc)",
11241 strWhat.isEmpty() ? tr("file") : strWhat.c_str(), strFile.c_str(), vrc);
11242 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, strError.c_str(),
11243 strFile.c_str(), vrc);
11244 }
11245
11246 if (prc)
11247 *prc = vrc;
11248 }
11249
11250 return hrc;
11251}
11252
11253/**
11254 * Creates differencing hard disks for all normal hard disks attached to this
11255 * machine and a new set of attachments to refer to created disks.
11256 *
11257 * Used when taking a snapshot or when deleting the current state. Gets called
11258 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
11259 *
11260 * This method assumes that mMediumAttachments contains the original hard disk
11261 * attachments it needs to create diffs for. On success, these attachments will
11262 * be replaced with the created diffs.
11263 *
11264 * Attachments with non-normal hard disks are left as is.
11265 *
11266 * If @a aOnline is @c false then the original hard disks that require implicit
11267 * diffs will be locked for reading. Otherwise it is assumed that they are
11268 * already locked for writing (when the VM was started). Note that in the latter
11269 * case it is responsibility of the caller to lock the newly created diffs for
11270 * writing if this method succeeds.
11271 *
11272 * @param aProgress Progress object to run (must contain at least as
11273 * many operations left as the number of hard disks
11274 * attached).
11275 * @param aWeight Weight of this operation.
11276 * @param aOnline Whether the VM was online prior to this operation.
11277 *
11278 * @note The progress object is not marked as completed, neither on success nor
11279 * on failure. This is a responsibility of the caller.
11280 *
11281 * @note Locks this object and the media tree for writing.
11282 */
11283HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
11284 ULONG aWeight,
11285 bool aOnline)
11286{
11287 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11288
11289 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
11290 AssertReturn(!!pProgressControl, E_INVALIDARG);
11291
11292 AutoCaller autoCaller(this);
11293 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11294
11295 AutoMultiWriteLock2 alock(this->lockHandle(),
11296 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11297
11298 /* must be in a protective state because we release the lock below */
11299 AssertReturn( mData->mMachineState == MachineState_Snapshotting
11300 || mData->mMachineState == MachineState_OnlineSnapshotting
11301 || mData->mMachineState == MachineState_LiveSnapshotting
11302 || mData->mMachineState == MachineState_RestoringSnapshot
11303 || mData->mMachineState == MachineState_DeletingSnapshot
11304 , E_FAIL);
11305
11306 HRESULT rc = S_OK;
11307
11308 // use appropriate locked media map (online or offline)
11309 MediumLockListMap lockedMediaOffline;
11310 MediumLockListMap *lockedMediaMap;
11311 if (aOnline)
11312 lockedMediaMap = &mData->mSession.mLockedMedia;
11313 else
11314 lockedMediaMap = &lockedMediaOffline;
11315
11316 try
11317 {
11318 if (!aOnline)
11319 {
11320 /* lock all attached hard disks early to detect "in use"
11321 * situations before creating actual diffs */
11322 for (MediumAttachmentList::const_iterator
11323 it = mMediumAttachments->begin();
11324 it != mMediumAttachments->end();
11325 ++it)
11326 {
11327 MediumAttachment *pAtt = *it;
11328 if (pAtt->i_getType() == DeviceType_HardDisk)
11329 {
11330 Medium *pMedium = pAtt->i_getMedium();
11331 Assert(pMedium);
11332
11333 MediumLockList *pMediumLockList(new MediumLockList());
11334 alock.release();
11335 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11336 NULL /* pToLockWrite */,
11337 false /* fMediumLockWriteAll */,
11338 NULL,
11339 *pMediumLockList);
11340 alock.acquire();
11341 if (FAILED(rc))
11342 {
11343 delete pMediumLockList;
11344 throw rc;
11345 }
11346 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11347 if (FAILED(rc))
11348 {
11349 throw setError(rc,
11350 tr("Collecting locking information for all attached media failed"));
11351 }
11352 }
11353 }
11354
11355 /* Now lock all media. If this fails, nothing is locked. */
11356 alock.release();
11357 rc = lockedMediaMap->Lock();
11358 alock.acquire();
11359 if (FAILED(rc))
11360 {
11361 throw setError(rc,
11362 tr("Locking of attached media failed"));
11363 }
11364 }
11365
11366 /* remember the current list (note that we don't use backup() since
11367 * mMediumAttachments may be already backed up) */
11368 MediumAttachmentList atts = *mMediumAttachments.data();
11369
11370 /* start from scratch */
11371 mMediumAttachments->clear();
11372
11373 /* go through remembered attachments and create diffs for normal hard
11374 * disks and attach them */
11375 for (MediumAttachmentList::const_iterator
11376 it = atts.begin();
11377 it != atts.end();
11378 ++it)
11379 {
11380 MediumAttachment *pAtt = *it;
11381
11382 DeviceType_T devType = pAtt->i_getType();
11383 Medium *pMedium = pAtt->i_getMedium();
11384
11385 if ( devType != DeviceType_HardDisk
11386 || pMedium == NULL
11387 || pMedium->i_getType() != MediumType_Normal)
11388 {
11389 /* copy the attachment as is */
11390
11391 /** @todo the progress object created in SessionMachine::TakeSnaphot
11392 * only expects operations for hard disks. Later other
11393 * device types need to show up in the progress as well. */
11394 if (devType == DeviceType_HardDisk)
11395 {
11396 if (pMedium == NULL)
11397 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11398 aWeight); // weight
11399 else
11400 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11401 pMedium->i_getBase()->i_getName().c_str()).raw(),
11402 aWeight); // weight
11403 }
11404
11405 mMediumAttachments->push_back(pAtt);
11406 continue;
11407 }
11408
11409 /* need a diff */
11410 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11411 pMedium->i_getBase()->i_getName().c_str()).raw(),
11412 aWeight); // weight
11413
11414 Utf8Str strFullSnapshotFolder;
11415 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11416
11417 ComObjPtr<Medium> diff;
11418 diff.createObject();
11419 // store the diff in the same registry as the parent
11420 // (this cannot fail here because we can't create implicit diffs for
11421 // unregistered images)
11422 Guid uuidRegistryParent;
11423 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11424 Assert(fInRegistry); NOREF(fInRegistry);
11425 rc = diff->init(mParent,
11426 pMedium->i_getPreferredDiffFormat(),
11427 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11428 uuidRegistryParent,
11429 DeviceType_HardDisk);
11430 if (FAILED(rc)) throw rc;
11431
11432 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11433 * the push_back? Looks like we're going to release medium with the
11434 * wrong kind of lock (general issue with if we fail anywhere at all)
11435 * and an orphaned VDI in the snapshots folder. */
11436
11437 /* update the appropriate lock list */
11438 MediumLockList *pMediumLockList;
11439 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11440 AssertComRCThrowRC(rc);
11441 if (aOnline)
11442 {
11443 alock.release();
11444 /* The currently attached medium will be read-only, change
11445 * the lock type to read. */
11446 rc = pMediumLockList->Update(pMedium, false);
11447 alock.acquire();
11448 AssertComRCThrowRC(rc);
11449 }
11450
11451 /* release the locks before the potentially lengthy operation */
11452 alock.release();
11453 rc = pMedium->i_createDiffStorage(diff,
11454 pMedium->i_getPreferredDiffVariant(),
11455 pMediumLockList,
11456 NULL /* aProgress */,
11457 true /* aWait */,
11458 false /* aNotify */);
11459 alock.acquire();
11460 if (FAILED(rc)) throw rc;
11461
11462 /* actual lock list update is done in Machine::i_commitMedia */
11463
11464 rc = diff->i_addBackReference(mData->mUuid);
11465 AssertComRCThrowRC(rc);
11466
11467 /* add a new attachment */
11468 ComObjPtr<MediumAttachment> attachment;
11469 attachment.createObject();
11470 rc = attachment->init(this,
11471 diff,
11472 pAtt->i_getControllerName(),
11473 pAtt->i_getPort(),
11474 pAtt->i_getDevice(),
11475 DeviceType_HardDisk,
11476 true /* aImplicit */,
11477 false /* aPassthrough */,
11478 false /* aTempEject */,
11479 pAtt->i_getNonRotational(),
11480 pAtt->i_getDiscard(),
11481 pAtt->i_getHotPluggable(),
11482 pAtt->i_getBandwidthGroup());
11483 if (FAILED(rc)) throw rc;
11484
11485 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11486 AssertComRCThrowRC(rc);
11487 mMediumAttachments->push_back(attachment);
11488 }
11489 }
11490 catch (HRESULT aRC) { rc = aRC; }
11491
11492 /* unlock all hard disks we locked when there is no VM */
11493 if (!aOnline)
11494 {
11495 ErrorInfoKeeper eik;
11496
11497 HRESULT rc1 = lockedMediaMap->Clear();
11498 AssertComRC(rc1);
11499 }
11500
11501 return rc;
11502}
11503
11504/**
11505 * Deletes implicit differencing hard disks created either by
11506 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11507 * mMediumAttachments.
11508 *
11509 * Note that to delete hard disks created by #attachDevice() this method is
11510 * called from #i_rollbackMedia() when the changes are rolled back.
11511 *
11512 * @note Locks this object and the media tree for writing.
11513 */
11514HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11515{
11516 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11517
11518 AutoCaller autoCaller(this);
11519 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11520
11521 AutoMultiWriteLock2 alock(this->lockHandle(),
11522 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11523
11524 /* We absolutely must have backed up state. */
11525 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11526
11527 /* Check if there are any implicitly created diff images. */
11528 bool fImplicitDiffs = false;
11529 for (MediumAttachmentList::const_iterator
11530 it = mMediumAttachments->begin();
11531 it != mMediumAttachments->end();
11532 ++it)
11533 {
11534 const ComObjPtr<MediumAttachment> &pAtt = *it;
11535 if (pAtt->i_isImplicit())
11536 {
11537 fImplicitDiffs = true;
11538 break;
11539 }
11540 }
11541 /* If there is nothing to do, leave early. This saves lots of image locking
11542 * effort. It also avoids a MachineStateChanged event without real reason.
11543 * This is important e.g. when loading a VM config, because there should be
11544 * no events. Otherwise API clients can become thoroughly confused for
11545 * inaccessible VMs (the code for loading VM configs uses this method for
11546 * cleanup if the config makes no sense), as they take such events as an
11547 * indication that the VM is alive, and they would force the VM config to
11548 * be reread, leading to an endless loop. */
11549 if (!fImplicitDiffs)
11550 return S_OK;
11551
11552 HRESULT rc = S_OK;
11553 MachineState_T oldState = mData->mMachineState;
11554
11555 /* will release the lock before the potentially lengthy operation,
11556 * so protect with the special state (unless already protected) */
11557 if ( oldState != MachineState_Snapshotting
11558 && oldState != MachineState_OnlineSnapshotting
11559 && oldState != MachineState_LiveSnapshotting
11560 && oldState != MachineState_RestoringSnapshot
11561 && oldState != MachineState_DeletingSnapshot
11562 && oldState != MachineState_DeletingSnapshotOnline
11563 && oldState != MachineState_DeletingSnapshotPaused
11564 )
11565 i_setMachineState(MachineState_SettingUp);
11566
11567 // use appropriate locked media map (online or offline)
11568 MediumLockListMap lockedMediaOffline;
11569 MediumLockListMap *lockedMediaMap;
11570 if (aOnline)
11571 lockedMediaMap = &mData->mSession.mLockedMedia;
11572 else
11573 lockedMediaMap = &lockedMediaOffline;
11574
11575 try
11576 {
11577 if (!aOnline)
11578 {
11579 /* lock all attached hard disks early to detect "in use"
11580 * situations before deleting actual diffs */
11581 for (MediumAttachmentList::const_iterator
11582 it = mMediumAttachments->begin();
11583 it != mMediumAttachments->end();
11584 ++it)
11585 {
11586 MediumAttachment *pAtt = *it;
11587 if (pAtt->i_getType() == DeviceType_HardDisk)
11588 {
11589 Medium *pMedium = pAtt->i_getMedium();
11590 Assert(pMedium);
11591
11592 MediumLockList *pMediumLockList(new MediumLockList());
11593 alock.release();
11594 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11595 NULL /* pToLockWrite */,
11596 false /* fMediumLockWriteAll */,
11597 NULL,
11598 *pMediumLockList);
11599 alock.acquire();
11600
11601 if (FAILED(rc))
11602 {
11603 delete pMediumLockList;
11604 throw rc;
11605 }
11606
11607 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11608 if (FAILED(rc))
11609 throw rc;
11610 }
11611 }
11612
11613 if (FAILED(rc))
11614 throw rc;
11615 } // end of offline
11616
11617 /* Lock lists are now up to date and include implicitly created media */
11618
11619 /* Go through remembered attachments and delete all implicitly created
11620 * diffs and fix up the attachment information */
11621 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11622 MediumAttachmentList implicitAtts;
11623 for (MediumAttachmentList::const_iterator
11624 it = mMediumAttachments->begin();
11625 it != mMediumAttachments->end();
11626 ++it)
11627 {
11628 ComObjPtr<MediumAttachment> pAtt = *it;
11629 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11630 if (pMedium.isNull())
11631 continue;
11632
11633 // Implicit attachments go on the list for deletion and back references are removed.
11634 if (pAtt->i_isImplicit())
11635 {
11636 /* Deassociate and mark for deletion */
11637 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11638 rc = pMedium->i_removeBackReference(mData->mUuid);
11639 if (FAILED(rc))
11640 throw rc;
11641 implicitAtts.push_back(pAtt);
11642 continue;
11643 }
11644
11645 /* Was this medium attached before? */
11646 if (!i_findAttachment(oldAtts, pMedium))
11647 {
11648 /* no: de-associate */
11649 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11650 rc = pMedium->i_removeBackReference(mData->mUuid);
11651 if (FAILED(rc))
11652 throw rc;
11653 continue;
11654 }
11655 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11656 }
11657
11658 /* If there are implicit attachments to delete, throw away the lock
11659 * map contents (which will unlock all media) since the medium
11660 * attachments will be rolled back. Below we need to completely
11661 * recreate the lock map anyway since it is infinitely complex to
11662 * do this incrementally (would need reconstructing each attachment
11663 * change, which would be extremely hairy). */
11664 if (implicitAtts.size() != 0)
11665 {
11666 ErrorInfoKeeper eik;
11667
11668 HRESULT rc1 = lockedMediaMap->Clear();
11669 AssertComRC(rc1);
11670 }
11671
11672 /* rollback hard disk changes */
11673 mMediumAttachments.rollback();
11674
11675 MultiResult mrc(S_OK);
11676
11677 // Delete unused implicit diffs.
11678 if (implicitAtts.size() != 0)
11679 {
11680 alock.release();
11681
11682 for (MediumAttachmentList::const_iterator
11683 it = implicitAtts.begin();
11684 it != implicitAtts.end();
11685 ++it)
11686 {
11687 // Remove medium associated with this attachment.
11688 ComObjPtr<MediumAttachment> pAtt = *it;
11689 Assert(pAtt);
11690 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11691 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11692 Assert(pMedium);
11693
11694 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11695 // continue on delete failure, just collect error messages
11696 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11697 pMedium->i_getLocationFull().c_str() ));
11698 mrc = rc;
11699 }
11700 // Clear the list of deleted implicit attachments now, while not
11701 // holding the lock, as it will ultimately trigger Medium::uninit()
11702 // calls which assume that the media tree lock isn't held.
11703 implicitAtts.clear();
11704
11705 alock.acquire();
11706
11707 /* if there is a VM recreate media lock map as mentioned above,
11708 * otherwise it is a waste of time and we leave things unlocked */
11709 if (aOnline)
11710 {
11711 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11712 /* must never be NULL, but better safe than sorry */
11713 if (!pMachine.isNull())
11714 {
11715 alock.release();
11716 rc = mData->mSession.mMachine->i_lockMedia();
11717 alock.acquire();
11718 if (FAILED(rc))
11719 throw rc;
11720 }
11721 }
11722 }
11723 }
11724 catch (HRESULT aRC) {rc = aRC;}
11725
11726 if (mData->mMachineState == MachineState_SettingUp)
11727 i_setMachineState(oldState);
11728
11729 /* unlock all hard disks we locked when there is no VM */
11730 if (!aOnline)
11731 {
11732 ErrorInfoKeeper eik;
11733
11734 HRESULT rc1 = lockedMediaMap->Clear();
11735 AssertComRC(rc1);
11736 }
11737
11738 return rc;
11739}
11740
11741
11742/**
11743 * Looks through the given list of media attachments for one with the given parameters
11744 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11745 * can be searched as well if needed.
11746 *
11747 * @param ll
11748 * @param aControllerName
11749 * @param aControllerPort
11750 * @param aDevice
11751 * @return
11752 */
11753MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11754 const Utf8Str &aControllerName,
11755 LONG aControllerPort,
11756 LONG aDevice)
11757{
11758 for (MediumAttachmentList::const_iterator
11759 it = ll.begin();
11760 it != ll.end();
11761 ++it)
11762 {
11763 MediumAttachment *pAttach = *it;
11764 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11765 return pAttach;
11766 }
11767
11768 return NULL;
11769}
11770
11771/**
11772 * Looks through the given list of media attachments for one with the given parameters
11773 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11774 * can be searched as well if needed.
11775 *
11776 * @param ll
11777 * @param pMedium
11778 * @return
11779 */
11780MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11781 ComObjPtr<Medium> pMedium)
11782{
11783 for (MediumAttachmentList::const_iterator
11784 it = ll.begin();
11785 it != ll.end();
11786 ++it)
11787 {
11788 MediumAttachment *pAttach = *it;
11789 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11790 if (pMediumThis == pMedium)
11791 return pAttach;
11792 }
11793
11794 return NULL;
11795}
11796
11797/**
11798 * Looks through the given list of media attachments for one with the given parameters
11799 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11800 * can be searched as well if needed.
11801 *
11802 * @param ll
11803 * @param id
11804 * @return
11805 */
11806MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11807 Guid &id)
11808{
11809 for (MediumAttachmentList::const_iterator
11810 it = ll.begin();
11811 it != ll.end();
11812 ++it)
11813 {
11814 MediumAttachment *pAttach = *it;
11815 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11816 if (pMediumThis->i_getId() == id)
11817 return pAttach;
11818 }
11819
11820 return NULL;
11821}
11822
11823/**
11824 * Main implementation for Machine::DetachDevice. This also gets called
11825 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11826 *
11827 * @param pAttach Medium attachment to detach.
11828 * @param writeLock Machine write lock which the caller must have locked once.
11829 * This may be released temporarily in here.
11830 * @param pSnapshot If NULL, then the detachment is for the current machine.
11831 * Otherwise this is for a SnapshotMachine, and this must be
11832 * its snapshot.
11833 * @return
11834 */
11835HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11836 AutoWriteLock &writeLock,
11837 Snapshot *pSnapshot)
11838{
11839 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11840 DeviceType_T mediumType = pAttach->i_getType();
11841
11842 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11843
11844 if (pAttach->i_isImplicit())
11845 {
11846 /* attempt to implicitly delete the implicitly created diff */
11847
11848 /// @todo move the implicit flag from MediumAttachment to Medium
11849 /// and forbid any hard disk operation when it is implicit. Or maybe
11850 /// a special media state for it to make it even more simple.
11851
11852 Assert(mMediumAttachments.isBackedUp());
11853
11854 /* will release the lock before the potentially lengthy operation, so
11855 * protect with the special state */
11856 MachineState_T oldState = mData->mMachineState;
11857 i_setMachineState(MachineState_SettingUp);
11858
11859 writeLock.release();
11860
11861 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11862 true /*aWait*/,
11863 false /*aNotify*/);
11864
11865 writeLock.acquire();
11866
11867 i_setMachineState(oldState);
11868
11869 if (FAILED(rc)) return rc;
11870 }
11871
11872 i_setModified(IsModified_Storage);
11873 mMediumAttachments.backup();
11874 mMediumAttachments->remove(pAttach);
11875
11876 if (!oldmedium.isNull())
11877 {
11878 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11879 if (pSnapshot)
11880 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11881 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11882 else if (mediumType != DeviceType_HardDisk)
11883 oldmedium->i_removeBackReference(mData->mUuid);
11884 }
11885
11886 return S_OK;
11887}
11888
11889/**
11890 * Goes thru all media of the given list and
11891 *
11892 * 1) calls i_detachDevice() on each of them for this machine and
11893 * 2) adds all Medium objects found in the process to the given list,
11894 * depending on cleanupMode.
11895 *
11896 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11897 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11898 * media to the list.
11899 *
11900 * This gets called from Machine::Unregister, both for the actual Machine and
11901 * the SnapshotMachine objects that might be found in the snapshots.
11902 *
11903 * Requires caller and locking. The machine lock must be passed in because it
11904 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11905 *
11906 * @param writeLock Machine lock from top-level caller; this gets passed to
11907 * i_detachDevice.
11908 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11909 * object if called for a SnapshotMachine.
11910 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11911 * added to llMedia; if Full, then all media get added;
11912 * otherwise no media get added.
11913 * @param llMedia Caller's list to receive Medium objects which got detached so
11914 * caller can close() them, depending on cleanupMode.
11915 * @return
11916 */
11917HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11918 Snapshot *pSnapshot,
11919 CleanupMode_T cleanupMode,
11920 MediaList &llMedia)
11921{
11922 Assert(isWriteLockOnCurrentThread());
11923
11924 HRESULT rc;
11925
11926 // make a temporary list because i_detachDevice invalidates iterators into
11927 // mMediumAttachments
11928 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11929
11930 for (MediumAttachmentList::iterator
11931 it = llAttachments2.begin();
11932 it != llAttachments2.end();
11933 ++it)
11934 {
11935 ComObjPtr<MediumAttachment> &pAttach = *it;
11936 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11937
11938 if (!pMedium.isNull())
11939 {
11940 AutoCaller mac(pMedium);
11941 if (FAILED(mac.rc())) return mac.rc();
11942 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11943 DeviceType_T devType = pMedium->i_getDeviceType();
11944 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11945 && devType == DeviceType_HardDisk)
11946 || (cleanupMode == CleanupMode_Full)
11947 )
11948 {
11949 llMedia.push_back(pMedium);
11950 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11951 /* Not allowed to keep this lock as below we need the parent
11952 * medium lock, and the lock order is parent to child. */
11953 lock.release();
11954 /*
11955 * Search for medias which are not attached to any machine, but
11956 * in the chain to an attached disk. Mediums are only consided
11957 * if they are:
11958 * - have only one child
11959 * - no references to any machines
11960 * - are of normal medium type
11961 */
11962 while (!pParent.isNull())
11963 {
11964 AutoCaller mac1(pParent);
11965 if (FAILED(mac1.rc())) return mac1.rc();
11966 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11967 if (pParent->i_getChildren().size() == 1)
11968 {
11969 if ( pParent->i_getMachineBackRefCount() == 0
11970 && pParent->i_getType() == MediumType_Normal
11971 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11972 llMedia.push_back(pParent);
11973 }
11974 else
11975 break;
11976 pParent = pParent->i_getParent();
11977 }
11978 }
11979 }
11980
11981 // real machine: then we need to use the proper method
11982 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11983
11984 if (FAILED(rc))
11985 return rc;
11986 }
11987
11988 return S_OK;
11989}
11990
11991/**
11992 * Perform deferred hard disk detachments.
11993 *
11994 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11995 * changed (not backed up).
11996 *
11997 * If @a aOnline is @c true then this method will also unlock the old hard
11998 * disks for which the new implicit diffs were created and will lock these new
11999 * diffs for writing.
12000 *
12001 * @param aOnline Whether the VM was online prior to this operation.
12002 *
12003 * @note Locks this object for writing!
12004 */
12005void Machine::i_commitMedia(bool aOnline /*= false*/)
12006{
12007 AutoCaller autoCaller(this);
12008 AssertComRCReturnVoid(autoCaller.rc());
12009
12010 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12011
12012 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
12013
12014 HRESULT rc = S_OK;
12015
12016 /* no attach/detach operations -- nothing to do */
12017 if (!mMediumAttachments.isBackedUp())
12018 return;
12019
12020 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
12021 bool fMediaNeedsLocking = false;
12022
12023 /* enumerate new attachments */
12024 for (MediumAttachmentList::const_iterator
12025 it = mMediumAttachments->begin();
12026 it != mMediumAttachments->end();
12027 ++it)
12028 {
12029 MediumAttachment *pAttach = *it;
12030
12031 pAttach->i_commit();
12032
12033 Medium *pMedium = pAttach->i_getMedium();
12034 bool fImplicit = pAttach->i_isImplicit();
12035
12036 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
12037 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
12038 fImplicit));
12039
12040 /** @todo convert all this Machine-based voodoo to MediumAttachment
12041 * based commit logic. */
12042 if (fImplicit)
12043 {
12044 /* convert implicit attachment to normal */
12045 pAttach->i_setImplicit(false);
12046
12047 if ( aOnline
12048 && pMedium
12049 && pAttach->i_getType() == DeviceType_HardDisk
12050 )
12051 {
12052 /* update the appropriate lock list */
12053 MediumLockList *pMediumLockList;
12054 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
12055 AssertComRC(rc);
12056 if (pMediumLockList)
12057 {
12058 /* unlock if there's a need to change the locking */
12059 if (!fMediaNeedsLocking)
12060 {
12061 rc = mData->mSession.mLockedMedia.Unlock();
12062 AssertComRC(rc);
12063 fMediaNeedsLocking = true;
12064 }
12065 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
12066 AssertComRC(rc);
12067 rc = pMediumLockList->Append(pMedium, true);
12068 AssertComRC(rc);
12069 }
12070 }
12071
12072 continue;
12073 }
12074
12075 if (pMedium)
12076 {
12077 /* was this medium attached before? */
12078 for (MediumAttachmentList::iterator
12079 oldIt = oldAtts.begin();
12080 oldIt != oldAtts.end();
12081 ++oldIt)
12082 {
12083 MediumAttachment *pOldAttach = *oldIt;
12084 if (pOldAttach->i_getMedium() == pMedium)
12085 {
12086 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
12087
12088 /* yes: remove from old to avoid de-association */
12089 oldAtts.erase(oldIt);
12090 break;
12091 }
12092 }
12093 }
12094 }
12095
12096 /* enumerate remaining old attachments and de-associate from the
12097 * current machine state */
12098 for (MediumAttachmentList::const_iterator
12099 it = oldAtts.begin();
12100 it != oldAtts.end();
12101 ++it)
12102 {
12103 MediumAttachment *pAttach = *it;
12104 Medium *pMedium = pAttach->i_getMedium();
12105
12106 /* Detach only hard disks, since DVD/floppy media is detached
12107 * instantly in MountMedium. */
12108 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
12109 {
12110 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
12111
12112 /* now de-associate from the current machine state */
12113 rc = pMedium->i_removeBackReference(mData->mUuid);
12114 AssertComRC(rc);
12115
12116 if (aOnline)
12117 {
12118 /* unlock since medium is not used anymore */
12119 MediumLockList *pMediumLockList;
12120 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
12121 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
12122 {
12123 /* this happens for online snapshots, there the attachment
12124 * is changing, but only to a diff image created under
12125 * the old one, so there is no separate lock list */
12126 Assert(!pMediumLockList);
12127 }
12128 else
12129 {
12130 AssertComRC(rc);
12131 if (pMediumLockList)
12132 {
12133 rc = mData->mSession.mLockedMedia.Remove(pAttach);
12134 AssertComRC(rc);
12135 }
12136 }
12137 }
12138 }
12139 }
12140
12141 /* take media locks again so that the locking state is consistent */
12142 if (fMediaNeedsLocking)
12143 {
12144 Assert(aOnline);
12145 rc = mData->mSession.mLockedMedia.Lock();
12146 AssertComRC(rc);
12147 }
12148
12149 /* commit the hard disk changes */
12150 mMediumAttachments.commit();
12151
12152 if (i_isSessionMachine())
12153 {
12154 /*
12155 * Update the parent machine to point to the new owner.
12156 * This is necessary because the stored parent will point to the
12157 * session machine otherwise and cause crashes or errors later
12158 * when the session machine gets invalid.
12159 */
12160 /** @todo Change the MediumAttachment class to behave like any other
12161 * class in this regard by creating peer MediumAttachment
12162 * objects for session machines and share the data with the peer
12163 * machine.
12164 */
12165 for (MediumAttachmentList::const_iterator
12166 it = mMediumAttachments->begin();
12167 it != mMediumAttachments->end();
12168 ++it)
12169 (*it)->i_updateParentMachine(mPeer);
12170
12171 /* attach new data to the primary machine and reshare it */
12172 mPeer->mMediumAttachments.attach(mMediumAttachments);
12173 }
12174
12175 return;
12176}
12177
12178/**
12179 * Perform deferred deletion of implicitly created diffs.
12180 *
12181 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
12182 * changed (not backed up).
12183 *
12184 * @note Locks this object for writing!
12185 */
12186void Machine::i_rollbackMedia()
12187{
12188 AutoCaller autoCaller(this);
12189 AssertComRCReturnVoid(autoCaller.rc());
12190
12191 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12192 LogFlowThisFunc(("Entering rollbackMedia\n"));
12193
12194 HRESULT rc = S_OK;
12195
12196 /* no attach/detach operations -- nothing to do */
12197 if (!mMediumAttachments.isBackedUp())
12198 return;
12199
12200 /* enumerate new attachments */
12201 for (MediumAttachmentList::const_iterator
12202 it = mMediumAttachments->begin();
12203 it != mMediumAttachments->end();
12204 ++it)
12205 {
12206 MediumAttachment *pAttach = *it;
12207 /* Fix up the backrefs for DVD/floppy media. */
12208 if (pAttach->i_getType() != DeviceType_HardDisk)
12209 {
12210 Medium *pMedium = pAttach->i_getMedium();
12211 if (pMedium)
12212 {
12213 rc = pMedium->i_removeBackReference(mData->mUuid);
12214 AssertComRC(rc);
12215 }
12216 }
12217
12218 (*it)->i_rollback();
12219
12220 pAttach = *it;
12221 /* Fix up the backrefs for DVD/floppy media. */
12222 if (pAttach->i_getType() != DeviceType_HardDisk)
12223 {
12224 Medium *pMedium = pAttach->i_getMedium();
12225 if (pMedium)
12226 {
12227 rc = pMedium->i_addBackReference(mData->mUuid);
12228 AssertComRC(rc);
12229 }
12230 }
12231 }
12232
12233 /** @todo convert all this Machine-based voodoo to MediumAttachment
12234 * based rollback logic. */
12235 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
12236
12237 return;
12238}
12239
12240/**
12241 * Returns true if the settings file is located in the directory named exactly
12242 * as the machine; this means, among other things, that the machine directory
12243 * should be auto-renamed.
12244 *
12245 * @param aSettingsDir if not NULL, the full machine settings file directory
12246 * name will be assigned there.
12247 *
12248 * @note Doesn't lock anything.
12249 * @note Not thread safe (must be called from this object's lock).
12250 */
12251bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
12252{
12253 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12254 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
12255 if (aSettingsDir)
12256 *aSettingsDir = strMachineDirName;
12257 strMachineDirName.stripPath(); // vmname
12258 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12259 strConfigFileOnly.stripPath() // vmname.vbox
12260 .stripSuffix(); // vmname
12261 /** @todo hack, make somehow use of ComposeMachineFilename */
12262 if (mUserData->s.fDirectoryIncludesUUID)
12263 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
12264
12265 AssertReturn(!strMachineDirName.isEmpty(), false);
12266 AssertReturn(!strConfigFileOnly.isEmpty(), false);
12267
12268 return strMachineDirName == strConfigFileOnly;
12269}
12270
12271/**
12272 * Discards all changes to machine settings.
12273 *
12274 * @param aNotify Whether to notify the direct session about changes or not.
12275 *
12276 * @note Locks objects for writing!
12277 */
12278void Machine::i_rollback(bool aNotify)
12279{
12280 AutoCaller autoCaller(this);
12281 AssertComRCReturn(autoCaller.rc(), (void)0);
12282
12283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12284
12285 if (!mStorageControllers.isNull())
12286 {
12287 if (mStorageControllers.isBackedUp())
12288 {
12289 /* unitialize all new devices (absent in the backed up list). */
12290 StorageControllerList *backedList = mStorageControllers.backedUpData();
12291 for (StorageControllerList::const_iterator
12292 it = mStorageControllers->begin();
12293 it != mStorageControllers->end();
12294 ++it)
12295 {
12296 if ( std::find(backedList->begin(), backedList->end(), *it)
12297 == backedList->end()
12298 )
12299 {
12300 (*it)->uninit();
12301 }
12302 }
12303
12304 /* restore the list */
12305 mStorageControllers.rollback();
12306 }
12307
12308 /* rollback any changes to devices after restoring the list */
12309 if (mData->flModifications & IsModified_Storage)
12310 {
12311 for (StorageControllerList::const_iterator
12312 it = mStorageControllers->begin();
12313 it != mStorageControllers->end();
12314 ++it)
12315 {
12316 (*it)->i_rollback();
12317 }
12318 }
12319 }
12320
12321 if (!mUSBControllers.isNull())
12322 {
12323 if (mUSBControllers.isBackedUp())
12324 {
12325 /* unitialize all new devices (absent in the backed up list). */
12326 USBControllerList *backedList = mUSBControllers.backedUpData();
12327 for (USBControllerList::const_iterator
12328 it = mUSBControllers->begin();
12329 it != mUSBControllers->end();
12330 ++it)
12331 {
12332 if ( std::find(backedList->begin(), backedList->end(), *it)
12333 == backedList->end()
12334 )
12335 {
12336 (*it)->uninit();
12337 }
12338 }
12339
12340 /* restore the list */
12341 mUSBControllers.rollback();
12342 }
12343
12344 /* rollback any changes to devices after restoring the list */
12345 if (mData->flModifications & IsModified_USB)
12346 {
12347 for (USBControllerList::const_iterator
12348 it = mUSBControllers->begin();
12349 it != mUSBControllers->end();
12350 ++it)
12351 {
12352 (*it)->i_rollback();
12353 }
12354 }
12355 }
12356
12357 mUserData.rollback();
12358
12359 mHWData.rollback();
12360
12361 if (mData->flModifications & IsModified_Storage)
12362 i_rollbackMedia();
12363
12364 if (mBIOSSettings)
12365 mBIOSSettings->i_rollback();
12366
12367 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
12368 mRecordingSettings->i_rollback();
12369
12370 if (mTrustedPlatformModule)
12371 mTrustedPlatformModule->i_rollback();
12372
12373 if (mNvramStore)
12374 mNvramStore->i_rollback();
12375
12376 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
12377 mGraphicsAdapter->i_rollback();
12378
12379 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12380 mVRDEServer->i_rollback();
12381
12382 if (mAudioSettings && (mData->flModifications & IsModified_AudioSettings))
12383 mAudioSettings->i_rollback();
12384
12385 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12386 mUSBDeviceFilters->i_rollback();
12387
12388 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12389 mBandwidthControl->i_rollback();
12390
12391 if (mGuestDebugControl && (mData->flModifications & IsModified_GuestDebugControl))
12392 mGuestDebugControl->i_rollback();
12393
12394 if (!mHWData.isNull())
12395 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12396 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12397 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12398 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12399
12400 if (mData->flModifications & IsModified_NetworkAdapters)
12401 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12402 if ( mNetworkAdapters[slot]
12403 && mNetworkAdapters[slot]->i_isModified())
12404 {
12405 mNetworkAdapters[slot]->i_rollback();
12406 networkAdapters[slot] = mNetworkAdapters[slot];
12407 }
12408
12409 if (mData->flModifications & IsModified_SerialPorts)
12410 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12411 if ( mSerialPorts[slot]
12412 && mSerialPorts[slot]->i_isModified())
12413 {
12414 mSerialPorts[slot]->i_rollback();
12415 serialPorts[slot] = mSerialPorts[slot];
12416 }
12417
12418 if (mData->flModifications & IsModified_ParallelPorts)
12419 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12420 if ( mParallelPorts[slot]
12421 && mParallelPorts[slot]->i_isModified())
12422 {
12423 mParallelPorts[slot]->i_rollback();
12424 parallelPorts[slot] = mParallelPorts[slot];
12425 }
12426
12427 if (aNotify)
12428 {
12429 /* inform the direct session about changes */
12430
12431 ComObjPtr<Machine> that = this;
12432 uint32_t flModifications = mData->flModifications;
12433 alock.release();
12434
12435 if (flModifications & IsModified_SharedFolders)
12436 that->i_onSharedFolderChange();
12437
12438 if (flModifications & IsModified_VRDEServer)
12439 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12440 if (flModifications & IsModified_USB)
12441 that->i_onUSBControllerChange();
12442
12443 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12444 if (networkAdapters[slot])
12445 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12446 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12447 if (serialPorts[slot])
12448 that->i_onSerialPortChange(serialPorts[slot]);
12449 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12450 if (parallelPorts[slot])
12451 that->i_onParallelPortChange(parallelPorts[slot]);
12452
12453 if (flModifications & IsModified_Storage)
12454 {
12455 for (StorageControllerList::const_iterator
12456 it = mStorageControllers->begin();
12457 it != mStorageControllers->end();
12458 ++it)
12459 {
12460 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
12461 }
12462 }
12463
12464 if (flModifications & IsModified_GuestDebugControl)
12465 that->i_onGuestDebugControlChange(mGuestDebugControl);
12466
12467#if 0
12468 if (flModifications & IsModified_BandwidthControl)
12469 that->onBandwidthControlChange();
12470#endif
12471 }
12472}
12473
12474/**
12475 * Commits all the changes to machine settings.
12476 *
12477 * Note that this operation is supposed to never fail.
12478 *
12479 * @note Locks this object and children for writing.
12480 */
12481void Machine::i_commit()
12482{
12483 AutoCaller autoCaller(this);
12484 AssertComRCReturnVoid(autoCaller.rc());
12485
12486 AutoCaller peerCaller(mPeer);
12487 AssertComRCReturnVoid(peerCaller.rc());
12488
12489 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12490
12491 /*
12492 * use safe commit to ensure Snapshot machines (that share mUserData)
12493 * will still refer to a valid memory location
12494 */
12495 mUserData.commitCopy();
12496
12497 mHWData.commit();
12498
12499 if (mMediumAttachments.isBackedUp())
12500 i_commitMedia(Global::IsOnline(mData->mMachineState));
12501
12502 mBIOSSettings->i_commit();
12503 mRecordingSettings->i_commit();
12504 mTrustedPlatformModule->i_commit();
12505 mNvramStore->i_commit();
12506 mGraphicsAdapter->i_commit();
12507 mVRDEServer->i_commit();
12508 mAudioSettings->i_commit();
12509 mUSBDeviceFilters->i_commit();
12510 mBandwidthControl->i_commit();
12511 mGuestDebugControl->i_commit();
12512
12513 /* Since mNetworkAdapters is a list which might have been changed (resized)
12514 * without using the Backupable<> template we need to handle the copying
12515 * of the list entries manually, including the creation of peers for the
12516 * new objects. */
12517 bool commitNetworkAdapters = false;
12518 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12519 if (mPeer)
12520 {
12521 /* commit everything, even the ones which will go away */
12522 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12523 mNetworkAdapters[slot]->i_commit();
12524 /* copy over the new entries, creating a peer and uninit the original */
12525 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12526 for (size_t slot = 0; slot < newSize; slot++)
12527 {
12528 /* look if this adapter has a peer device */
12529 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12530 if (!peer)
12531 {
12532 /* no peer means the adapter is a newly created one;
12533 * create a peer owning data this data share it with */
12534 peer.createObject();
12535 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12536 }
12537 mPeer->mNetworkAdapters[slot] = peer;
12538 }
12539 /* uninit any no longer needed network adapters */
12540 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12541 mNetworkAdapters[slot]->uninit();
12542 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12543 {
12544 if (mPeer->mNetworkAdapters[slot])
12545 mPeer->mNetworkAdapters[slot]->uninit();
12546 }
12547 /* Keep the original network adapter count until this point, so that
12548 * discarding a chipset type change will not lose settings. */
12549 mNetworkAdapters.resize(newSize);
12550 mPeer->mNetworkAdapters.resize(newSize);
12551 }
12552 else
12553 {
12554 /* we have no peer (our parent is the newly created machine);
12555 * just commit changes to the network adapters */
12556 commitNetworkAdapters = true;
12557 }
12558 if (commitNetworkAdapters)
12559 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12560 mNetworkAdapters[slot]->i_commit();
12561
12562 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12563 mSerialPorts[slot]->i_commit();
12564 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12565 mParallelPorts[slot]->i_commit();
12566
12567 bool commitStorageControllers = false;
12568
12569 if (mStorageControllers.isBackedUp())
12570 {
12571 mStorageControllers.commit();
12572
12573 if (mPeer)
12574 {
12575 /* Commit all changes to new controllers (this will reshare data with
12576 * peers for those who have peers) */
12577 StorageControllerList *newList = new StorageControllerList();
12578 for (StorageControllerList::const_iterator
12579 it = mStorageControllers->begin();
12580 it != mStorageControllers->end();
12581 ++it)
12582 {
12583 (*it)->i_commit();
12584
12585 /* look if this controller has a peer device */
12586 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12587 if (!peer)
12588 {
12589 /* no peer means the device is a newly created one;
12590 * create a peer owning data this device share it with */
12591 peer.createObject();
12592 peer->init(mPeer, *it, true /* aReshare */);
12593 }
12594 else
12595 {
12596 /* remove peer from the old list */
12597 mPeer->mStorageControllers->remove(peer);
12598 }
12599 /* and add it to the new list */
12600 newList->push_back(peer);
12601 }
12602
12603 /* uninit old peer's controllers that are left */
12604 for (StorageControllerList::const_iterator
12605 it = mPeer->mStorageControllers->begin();
12606 it != mPeer->mStorageControllers->end();
12607 ++it)
12608 {
12609 (*it)->uninit();
12610 }
12611
12612 /* attach new list of controllers to our peer */
12613 mPeer->mStorageControllers.attach(newList);
12614 }
12615 else
12616 {
12617 /* we have no peer (our parent is the newly created machine);
12618 * just commit changes to devices */
12619 commitStorageControllers = true;
12620 }
12621 }
12622 else
12623 {
12624 /* the list of controllers itself is not changed,
12625 * just commit changes to controllers themselves */
12626 commitStorageControllers = true;
12627 }
12628
12629 if (commitStorageControllers)
12630 {
12631 for (StorageControllerList::const_iterator
12632 it = mStorageControllers->begin();
12633 it != mStorageControllers->end();
12634 ++it)
12635 {
12636 (*it)->i_commit();
12637 }
12638 }
12639
12640 bool commitUSBControllers = false;
12641
12642 if (mUSBControllers.isBackedUp())
12643 {
12644 mUSBControllers.commit();
12645
12646 if (mPeer)
12647 {
12648 /* Commit all changes to new controllers (this will reshare data with
12649 * peers for those who have peers) */
12650 USBControllerList *newList = new USBControllerList();
12651 for (USBControllerList::const_iterator
12652 it = mUSBControllers->begin();
12653 it != mUSBControllers->end();
12654 ++it)
12655 {
12656 (*it)->i_commit();
12657
12658 /* look if this controller has a peer device */
12659 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12660 if (!peer)
12661 {
12662 /* no peer means the device is a newly created one;
12663 * create a peer owning data this device share it with */
12664 peer.createObject();
12665 peer->init(mPeer, *it, true /* aReshare */);
12666 }
12667 else
12668 {
12669 /* remove peer from the old list */
12670 mPeer->mUSBControllers->remove(peer);
12671 }
12672 /* and add it to the new list */
12673 newList->push_back(peer);
12674 }
12675
12676 /* uninit old peer's controllers that are left */
12677 for (USBControllerList::const_iterator
12678 it = mPeer->mUSBControllers->begin();
12679 it != mPeer->mUSBControllers->end();
12680 ++it)
12681 {
12682 (*it)->uninit();
12683 }
12684
12685 /* attach new list of controllers to our peer */
12686 mPeer->mUSBControllers.attach(newList);
12687 }
12688 else
12689 {
12690 /* we have no peer (our parent is the newly created machine);
12691 * just commit changes to devices */
12692 commitUSBControllers = true;
12693 }
12694 }
12695 else
12696 {
12697 /* the list of controllers itself is not changed,
12698 * just commit changes to controllers themselves */
12699 commitUSBControllers = true;
12700 }
12701
12702 if (commitUSBControllers)
12703 {
12704 for (USBControllerList::const_iterator
12705 it = mUSBControllers->begin();
12706 it != mUSBControllers->end();
12707 ++it)
12708 {
12709 (*it)->i_commit();
12710 }
12711 }
12712
12713 if (i_isSessionMachine())
12714 {
12715 /* attach new data to the primary machine and reshare it */
12716 mPeer->mUserData.attach(mUserData);
12717 mPeer->mHWData.attach(mHWData);
12718 /* mmMediumAttachments is reshared by fixupMedia */
12719 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12720 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12721 }
12722}
12723
12724/**
12725 * Copies all the hardware data from the given machine.
12726 *
12727 * Currently, only called when the VM is being restored from a snapshot. In
12728 * particular, this implies that the VM is not running during this method's
12729 * call.
12730 *
12731 * @note This method must be called from under this object's lock.
12732 *
12733 * @note This method doesn't call #i_commit(), so all data remains backed up and
12734 * unsaved.
12735 */
12736void Machine::i_copyFrom(Machine *aThat)
12737{
12738 AssertReturnVoid(!i_isSnapshotMachine());
12739 AssertReturnVoid(aThat->i_isSnapshotMachine());
12740
12741 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12742
12743 mHWData.assignCopy(aThat->mHWData);
12744
12745 // create copies of all shared folders (mHWData after attaching a copy
12746 // contains just references to original objects)
12747 for (HWData::SharedFolderList::iterator
12748 it = mHWData->mSharedFolders.begin();
12749 it != mHWData->mSharedFolders.end();
12750 ++it)
12751 {
12752 ComObjPtr<SharedFolder> folder;
12753 folder.createObject();
12754 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12755 AssertComRC(rc);
12756 *it = folder;
12757 }
12758
12759 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12760 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12761 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12762 mNvramStore->i_copyFrom(aThat->mNvramStore);
12763 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12764 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12765 mAudioSettings->i_copyFrom(aThat->mAudioSettings);
12766 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12767 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12768 mGuestDebugControl->i_copyFrom(aThat->mGuestDebugControl);
12769
12770 /* create private copies of all controllers */
12771 mStorageControllers.backup();
12772 mStorageControllers->clear();
12773 for (StorageControllerList::const_iterator
12774 it = aThat->mStorageControllers->begin();
12775 it != aThat->mStorageControllers->end();
12776 ++it)
12777 {
12778 ComObjPtr<StorageController> ctrl;
12779 ctrl.createObject();
12780 ctrl->initCopy(this, *it);
12781 mStorageControllers->push_back(ctrl);
12782 }
12783
12784 /* create private copies of all USB controllers */
12785 mUSBControllers.backup();
12786 mUSBControllers->clear();
12787 for (USBControllerList::const_iterator
12788 it = aThat->mUSBControllers->begin();
12789 it != aThat->mUSBControllers->end();
12790 ++it)
12791 {
12792 ComObjPtr<USBController> ctrl;
12793 ctrl.createObject();
12794 ctrl->initCopy(this, *it);
12795 mUSBControllers->push_back(ctrl);
12796 }
12797
12798 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12799 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12800 {
12801 if (mNetworkAdapters[slot].isNotNull())
12802 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12803 else
12804 {
12805 unconst(mNetworkAdapters[slot]).createObject();
12806 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12807 }
12808 }
12809 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12810 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12811 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12812 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12813}
12814
12815/**
12816 * Returns whether the given storage controller is hotplug capable.
12817 *
12818 * @returns true if the controller supports hotplugging
12819 * false otherwise.
12820 * @param enmCtrlType The controller type to check for.
12821 */
12822bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12823{
12824 ComPtr<ISystemProperties> systemProperties;
12825 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12826 if (FAILED(rc))
12827 return false;
12828
12829 BOOL aHotplugCapable = FALSE;
12830 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12831
12832 return RT_BOOL(aHotplugCapable);
12833}
12834
12835#ifdef VBOX_WITH_RESOURCE_USAGE_API
12836
12837void Machine::i_getDiskList(MediaList &list)
12838{
12839 for (MediumAttachmentList::const_iterator
12840 it = mMediumAttachments->begin();
12841 it != mMediumAttachments->end();
12842 ++it)
12843 {
12844 MediumAttachment *pAttach = *it;
12845 /* just in case */
12846 AssertContinue(pAttach);
12847
12848 AutoCaller localAutoCallerA(pAttach);
12849 if (FAILED(localAutoCallerA.rc())) continue;
12850
12851 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12852
12853 if (pAttach->i_getType() == DeviceType_HardDisk)
12854 list.push_back(pAttach->i_getMedium());
12855 }
12856}
12857
12858void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12859{
12860 AssertReturnVoid(isWriteLockOnCurrentThread());
12861 AssertPtrReturnVoid(aCollector);
12862
12863 pm::CollectorHAL *hal = aCollector->getHAL();
12864 /* Create sub metrics */
12865 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12866 "Percentage of processor time spent in user mode by the VM process.");
12867 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12868 "Percentage of processor time spent in kernel mode by the VM process.");
12869 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12870 "Size of resident portion of VM process in memory.");
12871 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12872 "Actual size of all VM disks combined.");
12873 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12874 "Network receive rate.");
12875 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12876 "Network transmit rate.");
12877 /* Create and register base metrics */
12878 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12879 cpuLoadUser, cpuLoadKernel);
12880 aCollector->registerBaseMetric(cpuLoad);
12881 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12882 ramUsageUsed);
12883 aCollector->registerBaseMetric(ramUsage);
12884 MediaList disks;
12885 i_getDiskList(disks);
12886 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12887 diskUsageUsed);
12888 aCollector->registerBaseMetric(diskUsage);
12889
12890 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12891 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12892 new pm::AggregateAvg()));
12893 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12894 new pm::AggregateMin()));
12895 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12896 new pm::AggregateMax()));
12897 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12898 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12899 new pm::AggregateAvg()));
12900 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12901 new pm::AggregateMin()));
12902 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12903 new pm::AggregateMax()));
12904
12905 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12906 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12907 new pm::AggregateAvg()));
12908 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12909 new pm::AggregateMin()));
12910 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12911 new pm::AggregateMax()));
12912
12913 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12914 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12915 new pm::AggregateAvg()));
12916 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12917 new pm::AggregateMin()));
12918 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12919 new pm::AggregateMax()));
12920
12921
12922 /* Guest metrics collector */
12923 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12924 aCollector->registerGuest(mCollectorGuest);
12925 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12926
12927 /* Create sub metrics */
12928 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12929 "Percentage of processor time spent in user mode as seen by the guest.");
12930 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12931 "Percentage of processor time spent in kernel mode as seen by the guest.");
12932 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12933 "Percentage of processor time spent idling as seen by the guest.");
12934
12935 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12936 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12937 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12938 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12939 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12940 pm::SubMetric *guestMemCache = new pm::SubMetric(
12941 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12942
12943 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12944 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12945
12946 /* Create and register base metrics */
12947 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12948 machineNetRx, machineNetTx);
12949 aCollector->registerBaseMetric(machineNetRate);
12950
12951 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12952 guestLoadUser, guestLoadKernel, guestLoadIdle);
12953 aCollector->registerBaseMetric(guestCpuLoad);
12954
12955 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12956 guestMemTotal, guestMemFree,
12957 guestMemBalloon, guestMemShared,
12958 guestMemCache, guestPagedTotal);
12959 aCollector->registerBaseMetric(guestCpuMem);
12960
12961 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12962 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12963 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12964 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12965
12966 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12967 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12968 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12969 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12970
12971 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12972 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12973 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12974 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12975
12976 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12977 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12978 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12979 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12980
12981 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12982 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12983 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12984 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12985
12986 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12987 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12988 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12989 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12990
12991 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12992 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12993 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12994 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12995
12996 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12997 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12998 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12999 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
13000
13001 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
13002 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
13003 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
13004 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
13005
13006 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
13007 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
13008 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
13009 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
13010
13011 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
13012 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
13013 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
13014 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
13015}
13016
13017void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
13018{
13019 AssertReturnVoid(isWriteLockOnCurrentThread());
13020
13021 if (aCollector)
13022 {
13023 aCollector->unregisterMetricsFor(aMachine);
13024 aCollector->unregisterBaseMetricsFor(aMachine);
13025 }
13026}
13027
13028#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13029
13030
13031////////////////////////////////////////////////////////////////////////////////
13032
13033DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
13034
13035HRESULT SessionMachine::FinalConstruct()
13036{
13037 LogFlowThisFunc(("\n"));
13038
13039 mClientToken = NULL;
13040
13041 return BaseFinalConstruct();
13042}
13043
13044void SessionMachine::FinalRelease()
13045{
13046 LogFlowThisFunc(("\n"));
13047
13048 Assert(!mClientToken);
13049 /* paranoia, should not hang around any more */
13050 if (mClientToken)
13051 {
13052 delete mClientToken;
13053 mClientToken = NULL;
13054 }
13055
13056 uninit(Uninit::Unexpected);
13057
13058 BaseFinalRelease();
13059}
13060
13061/**
13062 * @note Must be called only by Machine::LockMachine() from its own write lock.
13063 */
13064HRESULT SessionMachine::init(Machine *aMachine)
13065{
13066 LogFlowThisFuncEnter();
13067 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
13068
13069 AssertReturn(aMachine, E_INVALIDARG);
13070
13071 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
13072
13073 /* Enclose the state transition NotReady->InInit->Ready */
13074 AutoInitSpan autoInitSpan(this);
13075 AssertReturn(autoInitSpan.isOk(), E_FAIL);
13076
13077 HRESULT rc = S_OK;
13078
13079 RT_ZERO(mAuthLibCtx);
13080
13081 /* create the machine client token */
13082 try
13083 {
13084 mClientToken = new ClientToken(aMachine, this);
13085 if (!mClientToken->isReady())
13086 {
13087 delete mClientToken;
13088 mClientToken = NULL;
13089 rc = E_FAIL;
13090 }
13091 }
13092 catch (std::bad_alloc &)
13093 {
13094 rc = E_OUTOFMEMORY;
13095 }
13096 if (FAILED(rc))
13097 return rc;
13098
13099 /* memorize the peer Machine */
13100 unconst(mPeer) = aMachine;
13101 /* share the parent pointer */
13102 unconst(mParent) = aMachine->mParent;
13103
13104 /* take the pointers to data to share */
13105 mData.share(aMachine->mData);
13106 mSSData.share(aMachine->mSSData);
13107
13108 mUserData.share(aMachine->mUserData);
13109 mHWData.share(aMachine->mHWData);
13110 mMediumAttachments.share(aMachine->mMediumAttachments);
13111
13112 mStorageControllers.allocate();
13113 for (StorageControllerList::const_iterator
13114 it = aMachine->mStorageControllers->begin();
13115 it != aMachine->mStorageControllers->end();
13116 ++it)
13117 {
13118 ComObjPtr<StorageController> ctl;
13119 ctl.createObject();
13120 ctl->init(this, *it);
13121 mStorageControllers->push_back(ctl);
13122 }
13123
13124 mUSBControllers.allocate();
13125 for (USBControllerList::const_iterator
13126 it = aMachine->mUSBControllers->begin();
13127 it != aMachine->mUSBControllers->end();
13128 ++it)
13129 {
13130 ComObjPtr<USBController> ctl;
13131 ctl.createObject();
13132 ctl->init(this, *it);
13133 mUSBControllers->push_back(ctl);
13134 }
13135
13136 unconst(mBIOSSettings).createObject();
13137 mBIOSSettings->init(this, aMachine->mBIOSSettings);
13138
13139 unconst(mRecordingSettings).createObject();
13140 mRecordingSettings->init(this, aMachine->mRecordingSettings);
13141
13142 unconst(mTrustedPlatformModule).createObject();
13143 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
13144
13145 unconst(mNvramStore).createObject();
13146 mNvramStore->init(this, aMachine->mNvramStore);
13147
13148 /* create another GraphicsAdapter object that will be mutable */
13149 unconst(mGraphicsAdapter).createObject();
13150 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
13151 /* create another VRDEServer object that will be mutable */
13152 unconst(mVRDEServer).createObject();
13153 mVRDEServer->init(this, aMachine->mVRDEServer);
13154 /* create another audio settings object that will be mutable */
13155 unconst(mAudioSettings).createObject();
13156 mAudioSettings->init(this, aMachine->mAudioSettings);
13157 /* create a list of serial ports that will be mutable */
13158 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
13159 {
13160 unconst(mSerialPorts[slot]).createObject();
13161 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
13162 }
13163 /* create a list of parallel ports that will be mutable */
13164 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
13165 {
13166 unconst(mParallelPorts[slot]).createObject();
13167 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
13168 }
13169
13170 /* create another USB device filters object that will be mutable */
13171 unconst(mUSBDeviceFilters).createObject();
13172 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
13173
13174 /* create a list of network adapters that will be mutable */
13175 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
13176 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13177 {
13178 unconst(mNetworkAdapters[slot]).createObject();
13179 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
13180 }
13181
13182 /* create another bandwidth control object that will be mutable */
13183 unconst(mBandwidthControl).createObject();
13184 mBandwidthControl->init(this, aMachine->mBandwidthControl);
13185
13186 unconst(mGuestDebugControl).createObject();
13187 mGuestDebugControl->init(this, aMachine->mGuestDebugControl);
13188
13189 /* default is to delete saved state on Saved -> PoweredOff transition */
13190 mRemoveSavedState = true;
13191
13192 /* Confirm a successful initialization when it's the case */
13193 autoInitSpan.setSucceeded();
13194
13195 miNATNetworksStarted = 0;
13196
13197 LogFlowThisFuncLeave();
13198 return rc;
13199}
13200
13201/**
13202 * Uninitializes this session object. If the reason is other than
13203 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
13204 * or the client watcher code.
13205 *
13206 * @param aReason uninitialization reason
13207 *
13208 * @note Locks mParent + this object for writing.
13209 */
13210void SessionMachine::uninit(Uninit::Reason aReason)
13211{
13212 LogFlowThisFuncEnter();
13213 LogFlowThisFunc(("reason=%d\n", aReason));
13214
13215 /*
13216 * Strongly reference ourselves to prevent this object deletion after
13217 * mData->mSession.mMachine.setNull() below (which can release the last
13218 * reference and call the destructor). Important: this must be done before
13219 * accessing any members (and before AutoUninitSpan that does it as well).
13220 * This self reference will be released as the very last step on return.
13221 */
13222 ComObjPtr<SessionMachine> selfRef;
13223 if (aReason != Uninit::Unexpected)
13224 selfRef = this;
13225
13226 /* Enclose the state transition Ready->InUninit->NotReady */
13227 AutoUninitSpan autoUninitSpan(this);
13228 if (autoUninitSpan.uninitDone())
13229 {
13230 LogFlowThisFunc(("Already uninitialized\n"));
13231 LogFlowThisFuncLeave();
13232 return;
13233 }
13234
13235 if (autoUninitSpan.initFailed())
13236 {
13237 /* We've been called by init() because it's failed. It's not really
13238 * necessary (nor it's safe) to perform the regular uninit sequence
13239 * below, the following is enough.
13240 */
13241 LogFlowThisFunc(("Initialization failed.\n"));
13242 /* destroy the machine client token */
13243 if (mClientToken)
13244 {
13245 delete mClientToken;
13246 mClientToken = NULL;
13247 }
13248 uninitDataAndChildObjects();
13249 mData.free();
13250 unconst(mParent) = NULL;
13251 unconst(mPeer) = NULL;
13252 LogFlowThisFuncLeave();
13253 return;
13254 }
13255
13256 MachineState_T lastState;
13257 {
13258 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
13259 lastState = mData->mMachineState;
13260 }
13261 NOREF(lastState);
13262
13263#ifdef VBOX_WITH_USB
13264 // release all captured USB devices, but do this before requesting the locks below
13265 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
13266 {
13267 /* Console::captureUSBDevices() is called in the VM process only after
13268 * setting the machine state to Starting or Restoring.
13269 * Console::detachAllUSBDevices() will be called upon successful
13270 * termination. So, we need to release USB devices only if there was
13271 * an abnormal termination of a running VM.
13272 *
13273 * This is identical to SessionMachine::DetachAllUSBDevices except
13274 * for the aAbnormal argument. */
13275 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13276 AssertComRC(rc);
13277 NOREF(rc);
13278
13279 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13280 if (service)
13281 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
13282 }
13283#endif /* VBOX_WITH_USB */
13284
13285 // we need to lock this object in uninit() because the lock is shared
13286 // with mPeer (as well as data we modify below). mParent lock is needed
13287 // by several calls to it.
13288 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
13289
13290#ifdef VBOX_WITH_RESOURCE_USAGE_API
13291 /*
13292 * It is safe to call Machine::i_unregisterMetrics() here because
13293 * PerformanceCollector::samplerCallback no longer accesses guest methods
13294 * holding the lock.
13295 */
13296 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
13297 /* The guest must be unregistered after its metrics (@bugref{5949}). */
13298 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
13299 if (mCollectorGuest)
13300 {
13301 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
13302 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
13303 mCollectorGuest = NULL;
13304 }
13305#endif
13306
13307 if (aReason == Uninit::Abnormal)
13308 {
13309 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
13310
13311 /*
13312 * Move the VM to the 'Aborted' machine state unless we are restoring a
13313 * VM that was in the 'Saved' machine state. In that case, if the VM
13314 * fails before reaching either the 'Restoring' machine state or the
13315 * 'Running' machine state then we set the machine state to
13316 * 'AbortedSaved' in order to preserve the saved state file so that the
13317 * VM can be restored in the future.
13318 */
13319 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
13320 i_setMachineState(MachineState_AbortedSaved);
13321 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
13322 i_setMachineState(MachineState_Aborted);
13323 }
13324
13325 // any machine settings modified?
13326 if (mData->flModifications)
13327 {
13328 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
13329 i_rollback(false /* aNotify */);
13330 }
13331
13332 mData->mSession.mPID = NIL_RTPROCESS;
13333
13334 if (aReason == Uninit::Unexpected)
13335 {
13336 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
13337 * client watcher thread to update the set of machines that have open
13338 * sessions. */
13339 mParent->i_updateClientWatcher();
13340 }
13341
13342 /* uninitialize all remote controls */
13343 if (mData->mSession.mRemoteControls.size())
13344 {
13345 LogFlowThisFunc(("Closing remote sessions (%d):\n",
13346 mData->mSession.mRemoteControls.size()));
13347
13348 /* Always restart a the beginning, since the iterator is invalidated
13349 * by using erase(). */
13350 for (Data::Session::RemoteControlList::iterator
13351 it = mData->mSession.mRemoteControls.begin();
13352 it != mData->mSession.mRemoteControls.end();
13353 it = mData->mSession.mRemoteControls.begin())
13354 {
13355 ComPtr<IInternalSessionControl> pControl = *it;
13356 mData->mSession.mRemoteControls.erase(it);
13357 multilock.release();
13358 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
13359 HRESULT rc = pControl->Uninitialize();
13360 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
13361 if (FAILED(rc))
13362 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
13363 multilock.acquire();
13364 }
13365 mData->mSession.mRemoteControls.clear();
13366 }
13367
13368 /* Remove all references to the NAT network service. The service will stop
13369 * if all references (also from other VMs) are removed. */
13370 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
13371 {
13372 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13373 {
13374 BOOL enabled;
13375 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13376 if ( FAILED(hrc)
13377 || !enabled)
13378 continue;
13379
13380 NetworkAttachmentType_T type;
13381 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13382 if ( SUCCEEDED(hrc)
13383 && type == NetworkAttachmentType_NATNetwork)
13384 {
13385 Bstr name;
13386 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13387 if (SUCCEEDED(hrc))
13388 {
13389 multilock.release();
13390 Utf8Str strName(name);
13391 LogRel(("VM '%s' stops using NAT network '%s'\n",
13392 mUserData->s.strName.c_str(), strName.c_str()));
13393 mParent->i_natNetworkRefDec(strName);
13394 multilock.acquire();
13395 }
13396 }
13397 }
13398 }
13399
13400 /*
13401 * An expected uninitialization can come only from #i_checkForDeath().
13402 * Otherwise it means that something's gone really wrong (for example,
13403 * the Session implementation has released the VirtualBox reference
13404 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
13405 * etc). However, it's also possible, that the client releases the IPC
13406 * semaphore correctly (i.e. before it releases the VirtualBox reference),
13407 * but the VirtualBox release event comes first to the server process.
13408 * This case is practically possible, so we should not assert on an
13409 * unexpected uninit, just log a warning.
13410 */
13411
13412 if (aReason == Uninit::Unexpected)
13413 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
13414
13415 if (aReason != Uninit::Normal)
13416 {
13417 mData->mSession.mDirectControl.setNull();
13418 }
13419 else
13420 {
13421 /* this must be null here (see #OnSessionEnd()) */
13422 Assert(mData->mSession.mDirectControl.isNull());
13423 Assert(mData->mSession.mState == SessionState_Unlocking);
13424 Assert(!mData->mSession.mProgress.isNull());
13425 }
13426 if (mData->mSession.mProgress)
13427 {
13428 if (aReason == Uninit::Normal)
13429 mData->mSession.mProgress->i_notifyComplete(S_OK);
13430 else
13431 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
13432 COM_IIDOF(ISession),
13433 getComponentName(),
13434 tr("The VM session was aborted"));
13435 mData->mSession.mProgress.setNull();
13436 }
13437
13438 if (mConsoleTaskData.mProgress)
13439 {
13440 Assert(aReason == Uninit::Abnormal);
13441 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
13442 COM_IIDOF(ISession),
13443 getComponentName(),
13444 tr("The VM session was aborted"));
13445 mConsoleTaskData.mProgress.setNull();
13446 }
13447
13448 /* remove the association between the peer machine and this session machine */
13449 Assert( (SessionMachine*)mData->mSession.mMachine == this
13450 || aReason == Uninit::Unexpected);
13451
13452 /* reset the rest of session data */
13453 mData->mSession.mLockType = LockType_Null;
13454 mData->mSession.mMachine.setNull();
13455 mData->mSession.mState = SessionState_Unlocked;
13456 mData->mSession.mName.setNull();
13457
13458 /* destroy the machine client token before leaving the exclusive lock */
13459 if (mClientToken)
13460 {
13461 delete mClientToken;
13462 mClientToken = NULL;
13463 }
13464
13465 /* fire an event */
13466 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
13467
13468 uninitDataAndChildObjects();
13469
13470 /* free the essential data structure last */
13471 mData.free();
13472
13473 /* release the exclusive lock before setting the below two to NULL */
13474 multilock.release();
13475
13476 unconst(mParent) = NULL;
13477 unconst(mPeer) = NULL;
13478
13479 AuthLibUnload(&mAuthLibCtx);
13480
13481 LogFlowThisFuncLeave();
13482}
13483
13484// util::Lockable interface
13485////////////////////////////////////////////////////////////////////////////////
13486
13487/**
13488 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13489 * with the primary Machine instance (mPeer).
13490 */
13491RWLockHandle *SessionMachine::lockHandle() const
13492{
13493 AssertReturn(mPeer != NULL, NULL);
13494 return mPeer->lockHandle();
13495}
13496
13497// IInternalMachineControl methods
13498////////////////////////////////////////////////////////////////////////////////
13499
13500/**
13501 * Passes collected guest statistics to performance collector object
13502 */
13503HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13504 ULONG aCpuKernel, ULONG aCpuIdle,
13505 ULONG aMemTotal, ULONG aMemFree,
13506 ULONG aMemBalloon, ULONG aMemShared,
13507 ULONG aMemCache, ULONG aPageTotal,
13508 ULONG aAllocVMM, ULONG aFreeVMM,
13509 ULONG aBalloonedVMM, ULONG aSharedVMM,
13510 ULONG aVmNetRx, ULONG aVmNetTx)
13511{
13512#ifdef VBOX_WITH_RESOURCE_USAGE_API
13513 if (mCollectorGuest)
13514 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13515 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13516 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13517 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13518
13519 return S_OK;
13520#else
13521 NOREF(aValidStats);
13522 NOREF(aCpuUser);
13523 NOREF(aCpuKernel);
13524 NOREF(aCpuIdle);
13525 NOREF(aMemTotal);
13526 NOREF(aMemFree);
13527 NOREF(aMemBalloon);
13528 NOREF(aMemShared);
13529 NOREF(aMemCache);
13530 NOREF(aPageTotal);
13531 NOREF(aAllocVMM);
13532 NOREF(aFreeVMM);
13533 NOREF(aBalloonedVMM);
13534 NOREF(aSharedVMM);
13535 NOREF(aVmNetRx);
13536 NOREF(aVmNetTx);
13537 return E_NOTIMPL;
13538#endif
13539}
13540
13541////////////////////////////////////////////////////////////////////////////////
13542//
13543// SessionMachine task records
13544//
13545////////////////////////////////////////////////////////////////////////////////
13546
13547/**
13548 * Task record for saving the machine state.
13549 */
13550class SessionMachine::SaveStateTask
13551 : public Machine::Task
13552{
13553public:
13554 SaveStateTask(SessionMachine *m,
13555 Progress *p,
13556 const Utf8Str &t,
13557 Reason_T enmReason,
13558 const Utf8Str &strStateFilePath)
13559 : Task(m, p, t),
13560 m_enmReason(enmReason),
13561 m_strStateFilePath(strStateFilePath)
13562 {}
13563
13564private:
13565 void handler()
13566 {
13567 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13568 }
13569
13570 Reason_T m_enmReason;
13571 Utf8Str m_strStateFilePath;
13572
13573 friend class SessionMachine;
13574};
13575
13576/**
13577 * Task thread implementation for SessionMachine::SaveState(), called from
13578 * SessionMachine::taskHandler().
13579 *
13580 * @note Locks this object for writing.
13581 *
13582 * @param task
13583 * @return
13584 */
13585void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13586{
13587 LogFlowThisFuncEnter();
13588
13589 AutoCaller autoCaller(this);
13590 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13591 if (FAILED(autoCaller.rc()))
13592 {
13593 /* we might have been uninitialized because the session was accidentally
13594 * closed by the client, so don't assert */
13595 HRESULT rc = setError(E_FAIL,
13596 tr("The session has been accidentally closed"));
13597 task.m_pProgress->i_notifyComplete(rc);
13598 LogFlowThisFuncLeave();
13599 return;
13600 }
13601
13602 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13603
13604 HRESULT rc = S_OK;
13605
13606 try
13607 {
13608 ComPtr<IInternalSessionControl> directControl;
13609 if (mData->mSession.mLockType == LockType_VM)
13610 directControl = mData->mSession.mDirectControl;
13611 if (directControl.isNull())
13612 throw setError(VBOX_E_INVALID_VM_STATE,
13613 tr("Trying to save state without a running VM"));
13614 alock.release();
13615 BOOL fSuspendedBySave;
13616 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13617 Assert(!fSuspendedBySave);
13618 alock.acquire();
13619
13620 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13621 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13622 throw E_FAIL);
13623
13624 if (SUCCEEDED(rc))
13625 {
13626 mSSData->strStateFilePath = task.m_strStateFilePath;
13627
13628 /* save all VM settings */
13629 rc = i_saveSettings(NULL, alock);
13630 // no need to check whether VirtualBox.xml needs saving also since
13631 // we can't have a name change pending at this point
13632 }
13633 else
13634 {
13635 // On failure, set the state to the state we had at the beginning.
13636 i_setMachineState(task.m_machineStateBackup);
13637 i_updateMachineStateOnClient();
13638
13639 // Delete the saved state file (might have been already created).
13640 // No need to check whether this is shared with a snapshot here
13641 // because we certainly created a fresh saved state file here.
13642 i_deleteFile(task.m_strStateFilePath, true /* fIgnoreFailures */);
13643 }
13644 }
13645 catch (HRESULT aRC) { rc = aRC; }
13646
13647 task.m_pProgress->i_notifyComplete(rc);
13648
13649 LogFlowThisFuncLeave();
13650}
13651
13652/**
13653 * @note Locks this object for writing.
13654 */
13655HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13656{
13657 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13658}
13659
13660HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13661{
13662 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13663
13664 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13665 if (FAILED(rc)) return rc;
13666
13667 if ( mData->mMachineState != MachineState_Running
13668 && mData->mMachineState != MachineState_Paused
13669 )
13670 return setError(VBOX_E_INVALID_VM_STATE,
13671 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13672 Global::stringifyMachineState(mData->mMachineState));
13673
13674 ComObjPtr<Progress> pProgress;
13675 pProgress.createObject();
13676 rc = pProgress->init(i_getVirtualBox(),
13677 static_cast<IMachine *>(this) /* aInitiator */,
13678 tr("Saving the execution state of the virtual machine"),
13679 FALSE /* aCancelable */);
13680 if (FAILED(rc))
13681 return rc;
13682
13683 Utf8Str strStateFilePath;
13684 i_composeSavedStateFilename(strStateFilePath);
13685
13686 /* create and start the task on a separate thread (note that it will not
13687 * start working until we release alock) */
13688 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13689 rc = pTask->createThread();
13690 if (FAILED(rc))
13691 return rc;
13692
13693 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13694 i_setMachineState(MachineState_Saving);
13695 i_updateMachineStateOnClient();
13696
13697 pProgress.queryInterfaceTo(aProgress.asOutParam());
13698
13699 return S_OK;
13700}
13701
13702/**
13703 * @note Locks this object for writing.
13704 */
13705HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13706{
13707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13708
13709 HRESULT rc = i_checkStateDependency(MutableStateDep);
13710 if (FAILED(rc)) return rc;
13711
13712 if ( mData->mMachineState != MachineState_PoweredOff
13713 && mData->mMachineState != MachineState_Teleported
13714 && mData->mMachineState != MachineState_Aborted
13715 )
13716 return setError(VBOX_E_INVALID_VM_STATE,
13717 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13718 Global::stringifyMachineState(mData->mMachineState));
13719
13720 com::Utf8Str stateFilePathFull;
13721 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13722 if (RT_FAILURE(vrc))
13723 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13724 tr("Invalid saved state file path '%s' (%Rrc)"),
13725 aSavedStateFile.c_str(),
13726 vrc);
13727
13728 mSSData->strStateFilePath = stateFilePathFull;
13729
13730 /* The below i_setMachineState() will detect the state transition and will
13731 * update the settings file */
13732
13733 return i_setMachineState(MachineState_Saved);
13734}
13735
13736/**
13737 * @note Locks this object for writing.
13738 */
13739HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13740{
13741 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13742
13743 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13744 if (FAILED(rc)) return rc;
13745
13746 if ( mData->mMachineState != MachineState_Saved
13747 && mData->mMachineState != MachineState_AbortedSaved)
13748 return setError(VBOX_E_INVALID_VM_STATE,
13749 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13750 Global::stringifyMachineState(mData->mMachineState));
13751
13752 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13753
13754 /*
13755 * Saved -> PoweredOff transition will be detected in the SessionMachine
13756 * and properly handled.
13757 */
13758 rc = i_setMachineState(MachineState_PoweredOff);
13759 return rc;
13760}
13761
13762
13763/**
13764 * @note Locks the same as #i_setMachineState() does.
13765 */
13766HRESULT SessionMachine::updateState(MachineState_T aState)
13767{
13768 return i_setMachineState(aState);
13769}
13770
13771/**
13772 * @note Locks this object for writing.
13773 */
13774HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13775{
13776 IProgress *pProgress(aProgress);
13777
13778 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13779
13780 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13781
13782 if (mData->mSession.mState != SessionState_Locked)
13783 return VBOX_E_INVALID_OBJECT_STATE;
13784
13785 if (!mData->mSession.mProgress.isNull())
13786 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13787
13788 /* If we didn't reference the NAT network service yet, add a reference to
13789 * force a start */
13790 if (miNATNetworksStarted < 1)
13791 {
13792 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13793 {
13794 BOOL enabled;
13795 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13796 if ( FAILED(hrc)
13797 || !enabled)
13798 continue;
13799
13800 NetworkAttachmentType_T type;
13801 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13802 if ( SUCCEEDED(hrc)
13803 && type == NetworkAttachmentType_NATNetwork)
13804 {
13805 Bstr name;
13806 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13807 if (SUCCEEDED(hrc))
13808 {
13809 Utf8Str strName(name);
13810 LogRel(("VM '%s' starts using NAT network '%s'\n",
13811 mUserData->s.strName.c_str(), strName.c_str()));
13812 mPeer->lockHandle()->unlockWrite();
13813 mParent->i_natNetworkRefInc(strName);
13814#ifdef RT_LOCK_STRICT
13815 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13816#else
13817 mPeer->lockHandle()->lockWrite();
13818#endif
13819 }
13820 }
13821 }
13822 miNATNetworksStarted++;
13823 }
13824
13825 LogFlowThisFunc(("returns S_OK.\n"));
13826 return S_OK;
13827}
13828
13829/**
13830 * @note Locks this object for writing.
13831 */
13832HRESULT SessionMachine::endPowerUp(LONG aResult)
13833{
13834 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13835
13836 if (mData->mSession.mState != SessionState_Locked)
13837 return VBOX_E_INVALID_OBJECT_STATE;
13838
13839 /* Finalize the LaunchVMProcess progress object. */
13840 if (mData->mSession.mProgress)
13841 {
13842 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13843 mData->mSession.mProgress.setNull();
13844 }
13845
13846 if (SUCCEEDED((HRESULT)aResult))
13847 {
13848#ifdef VBOX_WITH_RESOURCE_USAGE_API
13849 /* The VM has been powered up successfully, so it makes sense
13850 * now to offer the performance metrics for a running machine
13851 * object. Doing it earlier wouldn't be safe. */
13852 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13853 mData->mSession.mPID);
13854#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13855 }
13856
13857 return S_OK;
13858}
13859
13860/**
13861 * @note Locks this object for writing.
13862 */
13863HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13864{
13865 LogFlowThisFuncEnter();
13866
13867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13868
13869 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13870 E_FAIL);
13871
13872 /* create a progress object to track operation completion */
13873 ComObjPtr<Progress> pProgress;
13874 pProgress.createObject();
13875 pProgress->init(i_getVirtualBox(),
13876 static_cast<IMachine *>(this) /* aInitiator */,
13877 tr("Stopping the virtual machine"),
13878 FALSE /* aCancelable */);
13879
13880 /* fill in the console task data */
13881 mConsoleTaskData.mLastState = mData->mMachineState;
13882 mConsoleTaskData.mProgress = pProgress;
13883
13884 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13885 i_setMachineState(MachineState_Stopping);
13886
13887 pProgress.queryInterfaceTo(aProgress.asOutParam());
13888
13889 return S_OK;
13890}
13891
13892/**
13893 * @note Locks this object for writing.
13894 */
13895HRESULT SessionMachine::endPoweringDown(LONG aResult,
13896 const com::Utf8Str &aErrMsg)
13897{
13898 HRESULT const hrcResult = (HRESULT)aResult;
13899 LogFlowThisFuncEnter();
13900
13901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13902
13903 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13904 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13905 && mConsoleTaskData.mLastState != MachineState_Null,
13906 E_FAIL);
13907
13908 /*
13909 * On failure, set the state to the state we had when BeginPoweringDown()
13910 * was called (this is expected by Console::PowerDown() and the associated
13911 * task). On success the VM process already changed the state to
13912 * MachineState_PoweredOff, so no need to do anything.
13913 */
13914 if (FAILED(hrcResult))
13915 i_setMachineState(mConsoleTaskData.mLastState);
13916
13917 /* notify the progress object about operation completion */
13918 Assert(mConsoleTaskData.mProgress);
13919 if (SUCCEEDED(hrcResult))
13920 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13921 else
13922 {
13923 if (aErrMsg.length())
13924 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13925 COM_IIDOF(ISession),
13926 getComponentName(),
13927 aErrMsg.c_str());
13928 else
13929 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13930 }
13931
13932 /* clear out the temporary saved state data */
13933 mConsoleTaskData.mLastState = MachineState_Null;
13934 mConsoleTaskData.mProgress.setNull();
13935
13936 LogFlowThisFuncLeave();
13937 return S_OK;
13938}
13939
13940
13941/**
13942 * Goes through the USB filters of the given machine to see if the given
13943 * device matches any filter or not.
13944 *
13945 * @note Locks the same as USBController::hasMatchingFilter() does.
13946 */
13947HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13948 BOOL *aMatched,
13949 ULONG *aMaskedInterfaces)
13950{
13951 LogFlowThisFunc(("\n"));
13952
13953#ifdef VBOX_WITH_USB
13954 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13955#else
13956 NOREF(aDevice);
13957 NOREF(aMaskedInterfaces);
13958 *aMatched = FALSE;
13959#endif
13960
13961 return S_OK;
13962}
13963
13964/**
13965 * @note Locks the same as Host::captureUSBDevice() does.
13966 */
13967HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13968{
13969 LogFlowThisFunc(("\n"));
13970
13971#ifdef VBOX_WITH_USB
13972 /* if captureDeviceForVM() fails, it must have set extended error info */
13973 clearError();
13974 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13975 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13976 return rc;
13977
13978 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13979 AssertReturn(service, E_FAIL);
13980 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13981#else
13982 RT_NOREF(aId, aCaptureFilename);
13983 return E_NOTIMPL;
13984#endif
13985}
13986
13987/**
13988 * @note Locks the same as Host::detachUSBDevice() does.
13989 */
13990HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13991 BOOL aDone)
13992{
13993 LogFlowThisFunc(("\n"));
13994
13995#ifdef VBOX_WITH_USB
13996 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13997 AssertReturn(service, E_FAIL);
13998 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13999#else
14000 NOREF(aId);
14001 NOREF(aDone);
14002 return E_NOTIMPL;
14003#endif
14004}
14005
14006/**
14007 * Inserts all machine filters to the USB proxy service and then calls
14008 * Host::autoCaptureUSBDevices().
14009 *
14010 * Called by Console from the VM process upon VM startup.
14011 *
14012 * @note Locks what called methods lock.
14013 */
14014HRESULT SessionMachine::autoCaptureUSBDevices()
14015{
14016 LogFlowThisFunc(("\n"));
14017
14018#ifdef VBOX_WITH_USB
14019 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
14020 AssertComRC(rc);
14021 NOREF(rc);
14022
14023 USBProxyService *service = mParent->i_host()->i_usbProxyService();
14024 AssertReturn(service, E_FAIL);
14025 return service->autoCaptureDevicesForVM(this);
14026#else
14027 return S_OK;
14028#endif
14029}
14030
14031/**
14032 * Removes all machine filters from the USB proxy service and then calls
14033 * Host::detachAllUSBDevices().
14034 *
14035 * Called by Console from the VM process upon normal VM termination or by
14036 * SessionMachine::uninit() upon abnormal VM termination (from under the
14037 * Machine/SessionMachine lock).
14038 *
14039 * @note Locks what called methods lock.
14040 */
14041HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
14042{
14043 LogFlowThisFunc(("\n"));
14044
14045#ifdef VBOX_WITH_USB
14046 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
14047 AssertComRC(rc);
14048 NOREF(rc);
14049
14050 USBProxyService *service = mParent->i_host()->i_usbProxyService();
14051 AssertReturn(service, E_FAIL);
14052 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
14053#else
14054 NOREF(aDone);
14055 return S_OK;
14056#endif
14057}
14058
14059/**
14060 * @note Locks this object for writing.
14061 */
14062HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
14063 ComPtr<IProgress> &aProgress)
14064{
14065 LogFlowThisFuncEnter();
14066
14067 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
14068 /*
14069 * We don't assert below because it might happen that a non-direct session
14070 * informs us it is closed right after we've been uninitialized -- it's ok.
14071 */
14072
14073 /* get IInternalSessionControl interface */
14074 ComPtr<IInternalSessionControl> control(aSession);
14075
14076 ComAssertRet(!control.isNull(), E_INVALIDARG);
14077
14078 /* Creating a Progress object requires the VirtualBox lock, and
14079 * thus locking it here is required by the lock order rules. */
14080 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
14081
14082 if (control == mData->mSession.mDirectControl)
14083 {
14084 /* The direct session is being normally closed by the client process
14085 * ----------------------------------------------------------------- */
14086
14087 /* go to the closing state (essential for all open*Session() calls and
14088 * for #i_checkForDeath()) */
14089 Assert(mData->mSession.mState == SessionState_Locked);
14090 mData->mSession.mState = SessionState_Unlocking;
14091
14092 /* set direct control to NULL to release the remote instance */
14093 mData->mSession.mDirectControl.setNull();
14094 LogFlowThisFunc(("Direct control is set to NULL\n"));
14095
14096 if (mData->mSession.mProgress)
14097 {
14098 /* finalize the progress, someone might wait if a frontend
14099 * closes the session before powering on the VM. */
14100 mData->mSession.mProgress->notifyComplete(E_FAIL,
14101 COM_IIDOF(ISession),
14102 getComponentName(),
14103 tr("The VM session was closed before any attempt to power it on"));
14104 mData->mSession.mProgress.setNull();
14105 }
14106
14107 /* Create the progress object the client will use to wait until
14108 * #i_checkForDeath() is called to uninitialize this session object after
14109 * it releases the IPC semaphore.
14110 * Note! Because we're "reusing" mProgress here, this must be a proxy
14111 * object just like for LaunchVMProcess. */
14112 Assert(mData->mSession.mProgress.isNull());
14113 ComObjPtr<ProgressProxy> progress;
14114 progress.createObject();
14115 ComPtr<IUnknown> pPeer(mPeer);
14116 progress->init(mParent, pPeer,
14117 Bstr(tr("Closing session")).raw(),
14118 FALSE /* aCancelable */);
14119 progress.queryInterfaceTo(aProgress.asOutParam());
14120 mData->mSession.mProgress = progress;
14121 }
14122 else
14123 {
14124 /* the remote session is being normally closed */
14125 bool found = false;
14126 for (Data::Session::RemoteControlList::iterator
14127 it = mData->mSession.mRemoteControls.begin();
14128 it != mData->mSession.mRemoteControls.end();
14129 ++it)
14130 {
14131 if (control == *it)
14132 {
14133 found = true;
14134 // This MUST be erase(it), not remove(*it) as the latter
14135 // triggers a very nasty use after free due to the place where
14136 // the value "lives".
14137 mData->mSession.mRemoteControls.erase(it);
14138 break;
14139 }
14140 }
14141 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
14142 E_INVALIDARG);
14143 }
14144
14145 /* signal the client watcher thread, because the client is going away */
14146 mParent->i_updateClientWatcher();
14147
14148 LogFlowThisFuncLeave();
14149 return S_OK;
14150}
14151
14152HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14153 std::vector<com::Utf8Str> &aValues,
14154 std::vector<LONG64> &aTimestamps,
14155 std::vector<com::Utf8Str> &aFlags)
14156{
14157 LogFlowThisFunc(("\n"));
14158
14159#ifdef VBOX_WITH_GUEST_PROPS
14160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14161
14162 size_t cEntries = mHWData->mGuestProperties.size();
14163 aNames.resize(cEntries);
14164 aValues.resize(cEntries);
14165 aTimestamps.resize(cEntries);
14166 aFlags.resize(cEntries);
14167
14168 size_t i = 0;
14169 for (HWData::GuestPropertyMap::const_iterator
14170 it = mHWData->mGuestProperties.begin();
14171 it != mHWData->mGuestProperties.end();
14172 ++it, ++i)
14173 {
14174 aNames[i] = it->first;
14175 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
14176 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
14177
14178 aValues[i] = it->second.strValue;
14179 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
14180 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
14181
14182 aTimestamps[i] = it->second.mTimestamp;
14183
14184 /* If it is NULL, keep it NULL. */
14185 if (it->second.mFlags)
14186 {
14187 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
14188 GuestPropWriteFlags(it->second.mFlags, szFlags);
14189 aFlags[i] = szFlags;
14190 }
14191 else
14192 aFlags[i] = "";
14193 }
14194 return S_OK;
14195#else
14196 ReturnComNotImplemented();
14197#endif
14198}
14199
14200HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
14201 const com::Utf8Str &aValue,
14202 LONG64 aTimestamp,
14203 const com::Utf8Str &aFlags,
14204 BOOL fWasDeleted)
14205{
14206 LogFlowThisFunc(("\n"));
14207
14208#ifdef VBOX_WITH_GUEST_PROPS
14209 try
14210 {
14211 /*
14212 * Convert input up front.
14213 */
14214 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
14215 if (aFlags.length())
14216 {
14217 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
14218 AssertRCReturn(vrc, E_INVALIDARG);
14219 }
14220
14221 /*
14222 * Now grab the object lock, validate the state and do the update.
14223 */
14224
14225 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14226
14227 if (!Global::IsOnline(mData->mMachineState))
14228 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
14229
14230 i_setModified(IsModified_MachineData);
14231 mHWData.backup();
14232
14233 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
14234 if (it != mHWData->mGuestProperties.end())
14235 {
14236 if (!fWasDeleted)
14237 {
14238 it->second.strValue = aValue;
14239 it->second.mTimestamp = aTimestamp;
14240 it->second.mFlags = fFlags;
14241 }
14242 else
14243 mHWData->mGuestProperties.erase(it);
14244
14245 mData->mGuestPropertiesModified = TRUE;
14246 }
14247 else if (!fWasDeleted)
14248 {
14249 HWData::GuestProperty prop;
14250 prop.strValue = aValue;
14251 prop.mTimestamp = aTimestamp;
14252 prop.mFlags = fFlags;
14253
14254 mHWData->mGuestProperties[aName] = prop;
14255 mData->mGuestPropertiesModified = TRUE;
14256 }
14257
14258 alock.release();
14259
14260 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fWasDeleted);
14261 }
14262 catch (...)
14263 {
14264 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
14265 }
14266 return S_OK;
14267#else
14268 ReturnComNotImplemented();
14269#endif
14270}
14271
14272
14273HRESULT SessionMachine::lockMedia()
14274{
14275 AutoMultiWriteLock2 alock(this->lockHandle(),
14276 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14277
14278 AssertReturn( mData->mMachineState == MachineState_Starting
14279 || mData->mMachineState == MachineState_Restoring
14280 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
14281
14282 clearError();
14283 alock.release();
14284 return i_lockMedia();
14285}
14286
14287HRESULT SessionMachine::unlockMedia()
14288{
14289 HRESULT hrc = i_unlockMedia();
14290 return hrc;
14291}
14292
14293HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14294 ComPtr<IMediumAttachment> &aNewAttachment)
14295{
14296 // request the host lock first, since might be calling Host methods for getting host drives;
14297 // next, protect the media tree all the while we're in here, as well as our member variables
14298 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
14299 this->lockHandle(),
14300 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14301
14302 IMediumAttachment *iAttach = aAttachment;
14303 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
14304
14305 Utf8Str ctrlName;
14306 LONG lPort;
14307 LONG lDevice;
14308 bool fTempEject;
14309 {
14310 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14311
14312 /* Need to query the details first, as the IMediumAttachment reference
14313 * might be to the original settings, which we are going to change. */
14314 ctrlName = pAttach->i_getControllerName();
14315 lPort = pAttach->i_getPort();
14316 lDevice = pAttach->i_getDevice();
14317 fTempEject = pAttach->i_getTempEject();
14318 }
14319
14320 if (!fTempEject)
14321 {
14322 /* Remember previously mounted medium. The medium before taking the
14323 * backup is not necessarily the same thing. */
14324 ComObjPtr<Medium> oldmedium;
14325 oldmedium = pAttach->i_getMedium();
14326
14327 i_setModified(IsModified_Storage);
14328 mMediumAttachments.backup();
14329
14330 // The backup operation makes the pAttach reference point to the
14331 // old settings. Re-get the correct reference.
14332 pAttach = i_findAttachment(*mMediumAttachments.data(),
14333 ctrlName,
14334 lPort,
14335 lDevice);
14336
14337 {
14338 AutoCaller autoAttachCaller(this);
14339 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
14340
14341 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14342 if (!oldmedium.isNull())
14343 oldmedium->i_removeBackReference(mData->mUuid);
14344
14345 pAttach->i_updateMedium(NULL);
14346 pAttach->i_updateEjected();
14347 }
14348
14349 i_setModified(IsModified_Storage);
14350 }
14351 else
14352 {
14353 {
14354 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14355 pAttach->i_updateEjected();
14356 }
14357 }
14358
14359 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
14360
14361 return S_OK;
14362}
14363
14364HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
14365 com::Utf8Str &aResult)
14366{
14367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14368
14369 HRESULT hr = S_OK;
14370
14371 if (!mAuthLibCtx.hAuthLibrary)
14372 {
14373 /* Load the external authentication library. */
14374 Bstr authLibrary;
14375 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
14376
14377 Utf8Str filename = authLibrary;
14378
14379 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
14380 if (RT_FAILURE(vrc))
14381 hr = setErrorBoth(E_FAIL, vrc,
14382 tr("Could not load the external authentication library '%s' (%Rrc)"),
14383 filename.c_str(), vrc);
14384 }
14385
14386 /* The auth library might need the machine lock. */
14387 alock.release();
14388
14389 if (FAILED(hr))
14390 return hr;
14391
14392 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
14393 {
14394 enum VRDEAuthParams
14395 {
14396 parmUuid = 1,
14397 parmGuestJudgement,
14398 parmUser,
14399 parmPassword,
14400 parmDomain,
14401 parmClientId
14402 };
14403
14404 AuthResult result = AuthResultAccessDenied;
14405
14406 Guid uuid(aAuthParams[parmUuid]);
14407 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
14408 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
14409
14410 result = AuthLibAuthenticate(&mAuthLibCtx,
14411 uuid.raw(), guestJudgement,
14412 aAuthParams[parmUser].c_str(),
14413 aAuthParams[parmPassword].c_str(),
14414 aAuthParams[parmDomain].c_str(),
14415 u32ClientId);
14416
14417 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
14418 size_t cbPassword = aAuthParams[parmPassword].length();
14419 if (cbPassword)
14420 {
14421 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
14422 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
14423 }
14424
14425 if (result == AuthResultAccessGranted)
14426 aResult = "granted";
14427 else
14428 aResult = "denied";
14429
14430 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
14431 aAuthParams[parmUser].c_str(), aResult.c_str()));
14432 }
14433 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
14434 {
14435 enum VRDEAuthDisconnectParams
14436 {
14437 parmUuid = 1,
14438 parmClientId
14439 };
14440
14441 Guid uuid(aAuthParams[parmUuid]);
14442 uint32_t u32ClientId = 0;
14443 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
14444 }
14445 else
14446 {
14447 hr = E_INVALIDARG;
14448 }
14449
14450 return hr;
14451}
14452
14453// public methods only for internal purposes
14454/////////////////////////////////////////////////////////////////////////////
14455
14456#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14457/**
14458 * Called from the client watcher thread to check for expected or unexpected
14459 * death of the client process that has a direct session to this machine.
14460 *
14461 * On Win32 and on OS/2, this method is called only when we've got the
14462 * mutex (i.e. the client has either died or terminated normally) so it always
14463 * returns @c true (the client is terminated, the session machine is
14464 * uninitialized).
14465 *
14466 * On other platforms, the method returns @c true if the client process has
14467 * terminated normally or abnormally and the session machine was uninitialized,
14468 * and @c false if the client process is still alive.
14469 *
14470 * @note Locks this object for writing.
14471 */
14472bool SessionMachine::i_checkForDeath()
14473{
14474 Uninit::Reason reason;
14475 bool terminated = false;
14476
14477 /* Enclose autoCaller with a block because calling uninit() from under it
14478 * will deadlock. */
14479 {
14480 AutoCaller autoCaller(this);
14481 if (!autoCaller.isOk())
14482 {
14483 /* return true if not ready, to cause the client watcher to exclude
14484 * the corresponding session from watching */
14485 LogFlowThisFunc(("Already uninitialized!\n"));
14486 return true;
14487 }
14488
14489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14490
14491 /* Determine the reason of death: if the session state is Closing here,
14492 * everything is fine. Otherwise it means that the client did not call
14493 * OnSessionEnd() before it released the IPC semaphore. This may happen
14494 * either because the client process has abnormally terminated, or
14495 * because it simply forgot to call ISession::Close() before exiting. We
14496 * threat the latter also as an abnormal termination (see
14497 * Session::uninit() for details). */
14498 reason = mData->mSession.mState == SessionState_Unlocking ?
14499 Uninit::Normal :
14500 Uninit::Abnormal;
14501
14502 if (mClientToken)
14503 terminated = mClientToken->release();
14504 } /* AutoCaller block */
14505
14506 if (terminated)
14507 uninit(reason);
14508
14509 return terminated;
14510}
14511
14512void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14513{
14514 LogFlowThisFunc(("\n"));
14515
14516 strTokenId.setNull();
14517
14518 AutoCaller autoCaller(this);
14519 AssertComRCReturnVoid(autoCaller.rc());
14520
14521 Assert(mClientToken);
14522 if (mClientToken)
14523 mClientToken->getId(strTokenId);
14524}
14525#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14526IToken *SessionMachine::i_getToken()
14527{
14528 LogFlowThisFunc(("\n"));
14529
14530 AutoCaller autoCaller(this);
14531 AssertComRCReturn(autoCaller.rc(), NULL);
14532
14533 Assert(mClientToken);
14534 if (mClientToken)
14535 return mClientToken->getToken();
14536 else
14537 return NULL;
14538}
14539#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14540
14541Machine::ClientToken *SessionMachine::i_getClientToken()
14542{
14543 LogFlowThisFunc(("\n"));
14544
14545 AutoCaller autoCaller(this);
14546 AssertComRCReturn(autoCaller.rc(), NULL);
14547
14548 return mClientToken;
14549}
14550
14551
14552/**
14553 * @note Locks this object for reading.
14554 */
14555HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14556{
14557 LogFlowThisFunc(("\n"));
14558
14559 AutoCaller autoCaller(this);
14560 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14561
14562 ComPtr<IInternalSessionControl> directControl;
14563 {
14564 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14565 if (mData->mSession.mLockType == LockType_VM)
14566 directControl = mData->mSession.mDirectControl;
14567 }
14568
14569 /* ignore notifications sent after #OnSessionEnd() is called */
14570 if (!directControl)
14571 return S_OK;
14572
14573 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14574}
14575
14576/**
14577 * @note Locks this object for reading.
14578 */
14579HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
14580 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
14581 const Utf8Str &aGuestIp, LONG aGuestPort)
14582{
14583 LogFlowThisFunc(("\n"));
14584
14585 AutoCaller autoCaller(this);
14586 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14587
14588 ComPtr<IInternalSessionControl> directControl;
14589 {
14590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14591 if (mData->mSession.mLockType == LockType_VM)
14592 directControl = mData->mSession.mDirectControl;
14593 }
14594
14595 /* ignore notifications sent after #OnSessionEnd() is called */
14596 if (!directControl)
14597 return S_OK;
14598 /*
14599 * instead acting like callback we ask IVirtualBox deliver corresponding event
14600 */
14601
14602 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14603 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14604 return S_OK;
14605}
14606
14607/**
14608 * @note Locks this object for reading.
14609 */
14610HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14611{
14612 LogFlowThisFunc(("\n"));
14613
14614 AutoCaller autoCaller(this);
14615 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14616
14617 ComPtr<IInternalSessionControl> directControl;
14618 {
14619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14620 if (mData->mSession.mLockType == LockType_VM)
14621 directControl = mData->mSession.mDirectControl;
14622 }
14623
14624 /* ignore notifications sent after #OnSessionEnd() is called */
14625 if (!directControl)
14626 return S_OK;
14627
14628 return directControl->OnAudioAdapterChange(audioAdapter);
14629}
14630
14631/**
14632 * @note Locks this object for reading.
14633 */
14634HRESULT SessionMachine::i_onHostAudioDeviceChange(IHostAudioDevice *aDevice, BOOL aNew, AudioDeviceState_T aState, IVirtualBoxErrorInfo *aErrInfo)
14635{
14636 LogFlowThisFunc(("\n"));
14637
14638 AutoCaller autoCaller(this);
14639 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14640
14641 ComPtr<IInternalSessionControl> directControl;
14642 {
14643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14644 if (mData->mSession.mLockType == LockType_VM)
14645 directControl = mData->mSession.mDirectControl;
14646 }
14647
14648 /* ignore notifications sent after #OnSessionEnd() is called */
14649 if (!directControl)
14650 return S_OK;
14651
14652 return directControl->OnHostAudioDeviceChange(aDevice, aNew, aState, aErrInfo);
14653}
14654
14655/**
14656 * @note Locks this object for reading.
14657 */
14658HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14659{
14660 LogFlowThisFunc(("\n"));
14661
14662 AutoCaller autoCaller(this);
14663 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14664
14665 ComPtr<IInternalSessionControl> directControl;
14666 {
14667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14668 if (mData->mSession.mLockType == LockType_VM)
14669 directControl = mData->mSession.mDirectControl;
14670 }
14671
14672 /* ignore notifications sent after #OnSessionEnd() is called */
14673 if (!directControl)
14674 return S_OK;
14675
14676 return directControl->OnSerialPortChange(serialPort);
14677}
14678
14679/**
14680 * @note Locks this object for reading.
14681 */
14682HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14683{
14684 LogFlowThisFunc(("\n"));
14685
14686 AutoCaller autoCaller(this);
14687 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14688
14689 ComPtr<IInternalSessionControl> directControl;
14690 {
14691 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14692 if (mData->mSession.mLockType == LockType_VM)
14693 directControl = mData->mSession.mDirectControl;
14694 }
14695
14696 /* ignore notifications sent after #OnSessionEnd() is called */
14697 if (!directControl)
14698 return S_OK;
14699
14700 return directControl->OnParallelPortChange(parallelPort);
14701}
14702
14703/**
14704 * @note Locks this object for reading.
14705 */
14706HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14707{
14708 LogFlowThisFunc(("\n"));
14709
14710 AutoCaller autoCaller(this);
14711 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14712
14713 ComPtr<IInternalSessionControl> directControl;
14714 {
14715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14716 if (mData->mSession.mLockType == LockType_VM)
14717 directControl = mData->mSession.mDirectControl;
14718 }
14719
14720 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14721
14722 /* ignore notifications sent after #OnSessionEnd() is called */
14723 if (!directControl)
14724 return S_OK;
14725
14726 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14727}
14728
14729/**
14730 * @note Locks this object for reading.
14731 */
14732HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14733{
14734 LogFlowThisFunc(("\n"));
14735
14736 AutoCaller autoCaller(this);
14737 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14738
14739 ComPtr<IInternalSessionControl> directControl;
14740 {
14741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14742 if (mData->mSession.mLockType == LockType_VM)
14743 directControl = mData->mSession.mDirectControl;
14744 }
14745
14746 mParent->i_onMediumChanged(aAttachment);
14747
14748 /* ignore notifications sent after #OnSessionEnd() is called */
14749 if (!directControl)
14750 return S_OK;
14751
14752 return directControl->OnMediumChange(aAttachment, aForce);
14753}
14754
14755HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14756{
14757 LogFlowThisFunc(("\n"));
14758
14759 AutoCaller autoCaller(this);
14760 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14761
14762 ComPtr<IInternalSessionControl> directControl;
14763 {
14764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14765 if (mData->mSession.mLockType == LockType_VM)
14766 directControl = mData->mSession.mDirectControl;
14767 }
14768
14769 /* ignore notifications sent after #OnSessionEnd() is called */
14770 if (!directControl)
14771 return S_OK;
14772
14773 return directControl->OnVMProcessPriorityChange(aPriority);
14774}
14775
14776/**
14777 * @note Locks this object for reading.
14778 */
14779HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14780{
14781 LogFlowThisFunc(("\n"));
14782
14783 AutoCaller autoCaller(this);
14784 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14785
14786 ComPtr<IInternalSessionControl> directControl;
14787 {
14788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14789 if (mData->mSession.mLockType == LockType_VM)
14790 directControl = mData->mSession.mDirectControl;
14791 }
14792
14793 /* ignore notifications sent after #OnSessionEnd() is called */
14794 if (!directControl)
14795 return S_OK;
14796
14797 return directControl->OnCPUChange(aCPU, aRemove);
14798}
14799
14800HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14801{
14802 LogFlowThisFunc(("\n"));
14803
14804 AutoCaller autoCaller(this);
14805 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14806
14807 ComPtr<IInternalSessionControl> directControl;
14808 {
14809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14810 if (mData->mSession.mLockType == LockType_VM)
14811 directControl = mData->mSession.mDirectControl;
14812 }
14813
14814 /* ignore notifications sent after #OnSessionEnd() is called */
14815 if (!directControl)
14816 return S_OK;
14817
14818 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14819}
14820
14821/**
14822 * @note Locks this object for reading.
14823 */
14824HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14825{
14826 LogFlowThisFunc(("\n"));
14827
14828 AutoCaller autoCaller(this);
14829 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14830
14831 ComPtr<IInternalSessionControl> directControl;
14832 {
14833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14834 if (mData->mSession.mLockType == LockType_VM)
14835 directControl = mData->mSession.mDirectControl;
14836 }
14837
14838 /* ignore notifications sent after #OnSessionEnd() is called */
14839 if (!directControl)
14840 return S_OK;
14841
14842 return directControl->OnVRDEServerChange(aRestart);
14843}
14844
14845/**
14846 * @note Locks this object for reading.
14847 */
14848HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14849{
14850 LogFlowThisFunc(("\n"));
14851
14852 AutoCaller autoCaller(this);
14853 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14854
14855 ComPtr<IInternalSessionControl> directControl;
14856 {
14857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14858 if (mData->mSession.mLockType == LockType_VM)
14859 directControl = mData->mSession.mDirectControl;
14860 }
14861
14862 /* ignore notifications sent after #OnSessionEnd() is called */
14863 if (!directControl)
14864 return S_OK;
14865
14866 return directControl->OnRecordingChange(aEnable);
14867}
14868
14869/**
14870 * @note Locks this object for reading.
14871 */
14872HRESULT SessionMachine::i_onUSBControllerChange()
14873{
14874 LogFlowThisFunc(("\n"));
14875
14876 AutoCaller autoCaller(this);
14877 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14878
14879 ComPtr<IInternalSessionControl> directControl;
14880 {
14881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14882 if (mData->mSession.mLockType == LockType_VM)
14883 directControl = mData->mSession.mDirectControl;
14884 }
14885
14886 /* ignore notifications sent after #OnSessionEnd() is called */
14887 if (!directControl)
14888 return S_OK;
14889
14890 return directControl->OnUSBControllerChange();
14891}
14892
14893/**
14894 * @note Locks this object for reading.
14895 */
14896HRESULT SessionMachine::i_onSharedFolderChange()
14897{
14898 LogFlowThisFunc(("\n"));
14899
14900 AutoCaller autoCaller(this);
14901 AssertComRCReturnRC(autoCaller.rc());
14902
14903 ComPtr<IInternalSessionControl> directControl;
14904 {
14905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14906 if (mData->mSession.mLockType == LockType_VM)
14907 directControl = mData->mSession.mDirectControl;
14908 }
14909
14910 /* ignore notifications sent after #OnSessionEnd() is called */
14911 if (!directControl)
14912 return S_OK;
14913
14914 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14915}
14916
14917/**
14918 * @note Locks this object for reading.
14919 */
14920HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14921{
14922 LogFlowThisFunc(("\n"));
14923
14924 AutoCaller autoCaller(this);
14925 AssertComRCReturnRC(autoCaller.rc());
14926
14927 ComPtr<IInternalSessionControl> directControl;
14928 {
14929 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14930 if (mData->mSession.mLockType == LockType_VM)
14931 directControl = mData->mSession.mDirectControl;
14932 }
14933
14934 /* ignore notifications sent after #OnSessionEnd() is called */
14935 if (!directControl)
14936 return S_OK;
14937
14938 return directControl->OnClipboardModeChange(aClipboardMode);
14939}
14940
14941/**
14942 * @note Locks this object for reading.
14943 */
14944HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14945{
14946 LogFlowThisFunc(("\n"));
14947
14948 AutoCaller autoCaller(this);
14949 AssertComRCReturnRC(autoCaller.rc());
14950
14951 ComPtr<IInternalSessionControl> directControl;
14952 {
14953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14954 if (mData->mSession.mLockType == LockType_VM)
14955 directControl = mData->mSession.mDirectControl;
14956 }
14957
14958 /* ignore notifications sent after #OnSessionEnd() is called */
14959 if (!directControl)
14960 return S_OK;
14961
14962 return directControl->OnClipboardFileTransferModeChange(aEnable);
14963}
14964
14965/**
14966 * @note Locks this object for reading.
14967 */
14968HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14969{
14970 LogFlowThisFunc(("\n"));
14971
14972 AutoCaller autoCaller(this);
14973 AssertComRCReturnRC(autoCaller.rc());
14974
14975 ComPtr<IInternalSessionControl> directControl;
14976 {
14977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14978 if (mData->mSession.mLockType == LockType_VM)
14979 directControl = mData->mSession.mDirectControl;
14980 }
14981
14982 /* ignore notifications sent after #OnSessionEnd() is called */
14983 if (!directControl)
14984 return S_OK;
14985
14986 return directControl->OnDnDModeChange(aDnDMode);
14987}
14988
14989/**
14990 * @note Locks this object for reading.
14991 */
14992HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14993{
14994 LogFlowThisFunc(("\n"));
14995
14996 AutoCaller autoCaller(this);
14997 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14998
14999 ComPtr<IInternalSessionControl> directControl;
15000 {
15001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15002 if (mData->mSession.mLockType == LockType_VM)
15003 directControl = mData->mSession.mDirectControl;
15004 }
15005
15006 /* ignore notifications sent after #OnSessionEnd() is called */
15007 if (!directControl)
15008 return S_OK;
15009
15010 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
15011}
15012
15013/**
15014 * @note Locks this object for reading.
15015 */
15016HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
15017{
15018 LogFlowThisFunc(("\n"));
15019
15020 AutoCaller autoCaller(this);
15021 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15022
15023 ComPtr<IInternalSessionControl> directControl;
15024 {
15025 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15026 if (mData->mSession.mLockType == LockType_VM)
15027 directControl = mData->mSession.mDirectControl;
15028 }
15029
15030 /* ignore notifications sent after #OnSessionEnd() is called */
15031 if (!directControl)
15032 return S_OK;
15033
15034 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
15035}
15036
15037/**
15038 * @note Locks this object for reading.
15039 */
15040HRESULT SessionMachine::i_onGuestDebugControlChange(IGuestDebugControl *guestDebugControl)
15041{
15042 LogFlowThisFunc(("\n"));
15043
15044 AutoCaller autoCaller(this);
15045 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15046
15047 ComPtr<IInternalSessionControl> directControl;
15048 {
15049 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15050 if (mData->mSession.mLockType == LockType_VM)
15051 directControl = mData->mSession.mDirectControl;
15052 }
15053
15054 /* ignore notifications sent after #OnSessionEnd() is called */
15055 if (!directControl)
15056 return S_OK;
15057
15058 return directControl->OnGuestDebugControlChange(guestDebugControl);
15059}
15060
15061/**
15062 * Returns @c true if this machine's USB controller reports it has a matching
15063 * filter for the given USB device and @c false otherwise.
15064 *
15065 * @note locks this object for reading.
15066 */
15067bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
15068{
15069 AutoCaller autoCaller(this);
15070 /* silently return if not ready -- this method may be called after the
15071 * direct machine session has been called */
15072 if (!autoCaller.isOk())
15073 return false;
15074
15075#ifdef VBOX_WITH_USB
15076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15077
15078 switch (mData->mMachineState)
15079 {
15080 case MachineState_Starting:
15081 case MachineState_Restoring:
15082 case MachineState_TeleportingIn:
15083 case MachineState_Paused:
15084 case MachineState_Running:
15085 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
15086 * elsewhere... */
15087 alock.release();
15088 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
15089 default: break;
15090 }
15091#else
15092 NOREF(aDevice);
15093 NOREF(aMaskedIfs);
15094#endif
15095 return false;
15096}
15097
15098/**
15099 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
15100 */
15101HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
15102 IVirtualBoxErrorInfo *aError,
15103 ULONG aMaskedIfs,
15104 const com::Utf8Str &aCaptureFilename)
15105{
15106 LogFlowThisFunc(("\n"));
15107
15108 AutoCaller autoCaller(this);
15109
15110 /* This notification may happen after the machine object has been
15111 * uninitialized (the session was closed), so don't assert. */
15112 if (FAILED(autoCaller.rc())) return autoCaller.rc();
15113
15114 ComPtr<IInternalSessionControl> directControl;
15115 {
15116 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15117 if (mData->mSession.mLockType == LockType_VM)
15118 directControl = mData->mSession.mDirectControl;
15119 }
15120
15121 /* fail on notifications sent after #OnSessionEnd() is called, it is
15122 * expected by the caller */
15123 if (!directControl)
15124 return E_FAIL;
15125
15126 /* No locks should be held at this point. */
15127 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
15128 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
15129
15130 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
15131}
15132
15133/**
15134 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
15135 */
15136HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
15137 IVirtualBoxErrorInfo *aError)
15138{
15139 LogFlowThisFunc(("\n"));
15140
15141 AutoCaller autoCaller(this);
15142
15143 /* This notification may happen after the machine object has been
15144 * uninitialized (the session was closed), so don't assert. */
15145 if (FAILED(autoCaller.rc())) return autoCaller.rc();
15146
15147 ComPtr<IInternalSessionControl> directControl;
15148 {
15149 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15150 if (mData->mSession.mLockType == LockType_VM)
15151 directControl = mData->mSession.mDirectControl;
15152 }
15153
15154 /* fail on notifications sent after #OnSessionEnd() is called, it is
15155 * expected by the caller */
15156 if (!directControl)
15157 return E_FAIL;
15158
15159 /* No locks should be held at this point. */
15160 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
15161 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
15162
15163 return directControl->OnUSBDeviceDetach(aId, aError);
15164}
15165
15166// protected methods
15167/////////////////////////////////////////////////////////////////////////////
15168
15169/**
15170 * Deletes the given file if it is no longer in use by either the current machine state
15171 * (if the machine is "saved") or any of the machine's snapshots.
15172 *
15173 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
15174 * but is different for each SnapshotMachine. When calling this, the order of calling this
15175 * function on the one hand and changing that variable OR the snapshots tree on the other hand
15176 * is therefore critical. I know, it's all rather messy.
15177 *
15178 * @param strStateFile
15179 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
15180 * the test for whether the saved state file is in use.
15181 */
15182void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
15183 Snapshot *pSnapshotToIgnore)
15184{
15185 // it is safe to delete this saved state file if it is not currently in use by the machine ...
15186 if ( (strStateFile.isNotEmpty())
15187 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
15188 )
15189 // ... and it must also not be shared with other snapshots
15190 if ( !mData->mFirstSnapshot
15191 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
15192 // this checks the SnapshotMachine's state file paths
15193 )
15194 i_deleteFile(strStateFile, true /* fIgnoreFailures */);
15195}
15196
15197/**
15198 * Locks the attached media.
15199 *
15200 * All attached hard disks are locked for writing and DVD/floppy are locked for
15201 * reading. Parents of attached hard disks (if any) are locked for reading.
15202 *
15203 * This method also performs accessibility check of all media it locks: if some
15204 * media is inaccessible, the method will return a failure and a bunch of
15205 * extended error info objects per each inaccessible medium.
15206 *
15207 * Note that this method is atomic: if it returns a success, all media are
15208 * locked as described above; on failure no media is locked at all (all
15209 * succeeded individual locks will be undone).
15210 *
15211 * The caller is responsible for doing the necessary state sanity checks.
15212 *
15213 * The locks made by this method must be undone by calling #unlockMedia() when
15214 * no more needed.
15215 */
15216HRESULT SessionMachine::i_lockMedia()
15217{
15218 AutoCaller autoCaller(this);
15219 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15220
15221 AutoMultiWriteLock2 alock(this->lockHandle(),
15222 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
15223
15224 /* bail out if trying to lock things with already set up locking */
15225 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
15226
15227 MultiResult mrc(S_OK);
15228
15229 /* Collect locking information for all medium objects attached to the VM. */
15230 for (MediumAttachmentList::const_iterator
15231 it = mMediumAttachments->begin();
15232 it != mMediumAttachments->end();
15233 ++it)
15234 {
15235 MediumAttachment *pAtt = *it;
15236 DeviceType_T devType = pAtt->i_getType();
15237 Medium *pMedium = pAtt->i_getMedium();
15238
15239 MediumLockList *pMediumLockList(new MediumLockList());
15240 // There can be attachments without a medium (floppy/dvd), and thus
15241 // it's impossible to create a medium lock list. It still makes sense
15242 // to have the empty medium lock list in the map in case a medium is
15243 // attached later.
15244 if (pMedium != NULL)
15245 {
15246 MediumType_T mediumType = pMedium->i_getType();
15247 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
15248 || mediumType == MediumType_Shareable;
15249 bool fIsVitalImage = (devType == DeviceType_HardDisk);
15250
15251 alock.release();
15252 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
15253 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
15254 false /* fMediumLockWriteAll */,
15255 NULL,
15256 *pMediumLockList);
15257 alock.acquire();
15258 if (FAILED(mrc))
15259 {
15260 delete pMediumLockList;
15261 mData->mSession.mLockedMedia.Clear();
15262 break;
15263 }
15264 }
15265
15266 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
15267 if (FAILED(rc))
15268 {
15269 mData->mSession.mLockedMedia.Clear();
15270 mrc = setError(rc,
15271 tr("Collecting locking information for all attached media failed"));
15272 break;
15273 }
15274 }
15275
15276 if (SUCCEEDED(mrc))
15277 {
15278 /* Now lock all media. If this fails, nothing is locked. */
15279 alock.release();
15280 HRESULT rc = mData->mSession.mLockedMedia.Lock();
15281 alock.acquire();
15282 if (FAILED(rc))
15283 {
15284 mrc = setError(rc,
15285 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
15286 }
15287 }
15288
15289 return mrc;
15290}
15291
15292/**
15293 * Undoes the locks made by by #lockMedia().
15294 */
15295HRESULT SessionMachine::i_unlockMedia()
15296{
15297 AutoCaller autoCaller(this);
15298 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15299
15300 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15301
15302 /* we may be holding important error info on the current thread;
15303 * preserve it */
15304 ErrorInfoKeeper eik;
15305
15306 HRESULT rc = mData->mSession.mLockedMedia.Clear();
15307 AssertComRC(rc);
15308 return rc;
15309}
15310
15311/**
15312 * Helper to change the machine state (reimplementation).
15313 *
15314 * @note Locks this object for writing.
15315 * @note This method must not call i_saveSettings or SaveSettings, otherwise
15316 * it can cause crashes in random places due to unexpectedly committing
15317 * the current settings. The caller is responsible for that. The call
15318 * to saveStateSettings is fine, because this method does not commit.
15319 */
15320HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
15321{
15322 LogFlowThisFuncEnter();
15323
15324 AutoCaller autoCaller(this);
15325 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15326
15327 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15328
15329 MachineState_T oldMachineState = mData->mMachineState;
15330
15331 AssertMsgReturn(oldMachineState != aMachineState,
15332 ("oldMachineState=%s, aMachineState=%s\n",
15333 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
15334 E_FAIL);
15335
15336 HRESULT rc = S_OK;
15337
15338 int stsFlags = 0;
15339 bool deleteSavedState = false;
15340
15341 /* detect some state transitions */
15342
15343 if ( ( ( oldMachineState == MachineState_Saved
15344 || oldMachineState == MachineState_AbortedSaved
15345 )
15346 && aMachineState == MachineState_Restoring
15347 )
15348 || ( ( oldMachineState == MachineState_PoweredOff
15349 || oldMachineState == MachineState_Teleported
15350 || oldMachineState == MachineState_Aborted
15351 )
15352 && ( aMachineState == MachineState_TeleportingIn
15353 || aMachineState == MachineState_Starting
15354 )
15355 )
15356 )
15357 {
15358 /* The EMT thread is about to start */
15359
15360 /* Nothing to do here for now... */
15361
15362 /// @todo NEWMEDIA don't let mDVDDrive and other children
15363 /// change anything when in the Starting/Restoring state
15364 }
15365 else if ( ( oldMachineState == MachineState_Running
15366 || oldMachineState == MachineState_Paused
15367 || oldMachineState == MachineState_Teleporting
15368 || oldMachineState == MachineState_OnlineSnapshotting
15369 || oldMachineState == MachineState_LiveSnapshotting
15370 || oldMachineState == MachineState_Stuck
15371 || oldMachineState == MachineState_Starting
15372 || oldMachineState == MachineState_Stopping
15373 || oldMachineState == MachineState_Saving
15374 || oldMachineState == MachineState_Restoring
15375 || oldMachineState == MachineState_TeleportingPausedVM
15376 || oldMachineState == MachineState_TeleportingIn
15377 )
15378 && ( aMachineState == MachineState_PoweredOff
15379 || aMachineState == MachineState_Saved
15380 || aMachineState == MachineState_Teleported
15381 || aMachineState == MachineState_Aborted
15382 || aMachineState == MachineState_AbortedSaved
15383 )
15384 )
15385 {
15386 /* The EMT thread has just stopped, unlock attached media. Note that as
15387 * opposed to locking that is done from Console, we do unlocking here
15388 * because the VM process may have aborted before having a chance to
15389 * properly unlock all media it locked. */
15390
15391 unlockMedia();
15392 }
15393
15394 if (oldMachineState == MachineState_Restoring)
15395 {
15396 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
15397 {
15398 /*
15399 * delete the saved state file once the machine has finished
15400 * restoring from it (note that Console sets the state from
15401 * Restoring to AbortedSaved if the VM couldn't restore successfully,
15402 * to give the user an ability to fix an error and retry --
15403 * we keep the saved state file in this case)
15404 */
15405 deleteSavedState = true;
15406 }
15407 }
15408 else if ( oldMachineState == MachineState_Saved
15409 && ( aMachineState == MachineState_PoweredOff
15410 || aMachineState == MachineState_Teleported
15411 )
15412 )
15413 {
15414 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
15415 deleteSavedState = true;
15416 mData->mCurrentStateModified = TRUE;
15417 stsFlags |= SaveSTS_CurStateModified;
15418 }
15419 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
15420 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
15421
15422 if ( aMachineState == MachineState_Starting
15423 || aMachineState == MachineState_Restoring
15424 || aMachineState == MachineState_TeleportingIn
15425 )
15426 {
15427 /* set the current state modified flag to indicate that the current
15428 * state is no more identical to the state in the
15429 * current snapshot */
15430 if (!mData->mCurrentSnapshot.isNull())
15431 {
15432 mData->mCurrentStateModified = TRUE;
15433 stsFlags |= SaveSTS_CurStateModified;
15434 }
15435 }
15436
15437 if (deleteSavedState)
15438 {
15439 if (mRemoveSavedState)
15440 {
15441 Assert(!mSSData->strStateFilePath.isEmpty());
15442
15443 // it is safe to delete the saved state file if ...
15444 if ( !mData->mFirstSnapshot // ... we have no snapshots or
15445 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
15446 // ... none of the snapshots share the saved state file
15447 )
15448 i_deleteFile(mSSData->strStateFilePath, true /* fIgnoreFailures */);
15449 }
15450
15451 mSSData->strStateFilePath.setNull();
15452 stsFlags |= SaveSTS_StateFilePath;
15453 }
15454
15455 /* redirect to the underlying peer machine */
15456 mPeer->i_setMachineState(aMachineState);
15457
15458 if ( oldMachineState != MachineState_RestoringSnapshot
15459 && ( aMachineState == MachineState_PoweredOff
15460 || aMachineState == MachineState_Teleported
15461 || aMachineState == MachineState_Aborted
15462 || aMachineState == MachineState_AbortedSaved
15463 || aMachineState == MachineState_Saved))
15464 {
15465 /* the machine has stopped execution
15466 * (or the saved state file was adopted) */
15467 stsFlags |= SaveSTS_StateTimeStamp;
15468 }
15469
15470 if ( ( oldMachineState == MachineState_PoweredOff
15471 || oldMachineState == MachineState_Aborted
15472 || oldMachineState == MachineState_Teleported
15473 )
15474 && aMachineState == MachineState_Saved)
15475 {
15476 /* the saved state file was adopted */
15477 Assert(!mSSData->strStateFilePath.isEmpty());
15478 stsFlags |= SaveSTS_StateFilePath;
15479 }
15480
15481#ifdef VBOX_WITH_GUEST_PROPS
15482 if ( aMachineState == MachineState_PoweredOff
15483 || aMachineState == MachineState_Aborted
15484 || aMachineState == MachineState_Teleported)
15485 {
15486 /* Make sure any transient guest properties get removed from the
15487 * property store on shutdown. */
15488 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
15489
15490 /* remove it from the settings representation */
15491 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
15492 for (settings::GuestPropertiesList::iterator
15493 it = llGuestProperties.begin();
15494 it != llGuestProperties.end();
15495 /*nothing*/)
15496 {
15497 const settings::GuestProperty &prop = *it;
15498 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
15499 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
15500 {
15501 it = llGuestProperties.erase(it);
15502 fNeedsSaving = true;
15503 }
15504 else
15505 {
15506 ++it;
15507 }
15508 }
15509
15510 /* Additionally remove it from the HWData representation. Required to
15511 * keep everything in sync, as this is what the API keeps using. */
15512 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
15513 for (HWData::GuestPropertyMap::iterator
15514 it = llHWGuestProperties.begin();
15515 it != llHWGuestProperties.end();
15516 /*nothing*/)
15517 {
15518 uint32_t fFlags = it->second.mFlags;
15519 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
15520 {
15521 /* iterator where we need to continue after the erase call
15522 * (C++03 is a fact still, and it doesn't return the iterator
15523 * which would allow continuing) */
15524 HWData::GuestPropertyMap::iterator it2 = it;
15525 ++it2;
15526 llHWGuestProperties.erase(it);
15527 it = it2;
15528 fNeedsSaving = true;
15529 }
15530 else
15531 {
15532 ++it;
15533 }
15534 }
15535
15536 if (fNeedsSaving)
15537 {
15538 mData->mCurrentStateModified = TRUE;
15539 stsFlags |= SaveSTS_CurStateModified;
15540 }
15541 }
15542#endif /* VBOX_WITH_GUEST_PROPS */
15543
15544 rc = i_saveStateSettings(stsFlags);
15545
15546 if ( ( oldMachineState != MachineState_PoweredOff
15547 && oldMachineState != MachineState_Aborted
15548 && oldMachineState != MachineState_Teleported
15549 )
15550 && ( aMachineState == MachineState_PoweredOff
15551 || aMachineState == MachineState_Aborted
15552 || aMachineState == MachineState_Teleported
15553 )
15554 )
15555 {
15556 /* we've been shut down for any reason */
15557 /* no special action so far */
15558 }
15559
15560 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, ::stringifyMachineState(mData->mMachineState) ));
15561 LogFlowThisFuncLeave();
15562 return rc;
15563}
15564
15565/**
15566 * Sends the current machine state value to the VM process.
15567 *
15568 * @note Locks this object for reading, then calls a client process.
15569 */
15570HRESULT SessionMachine::i_updateMachineStateOnClient()
15571{
15572 AutoCaller autoCaller(this);
15573 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15574
15575 ComPtr<IInternalSessionControl> directControl;
15576 {
15577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15578 AssertReturn(!!mData, E_FAIL);
15579 if (mData->mSession.mLockType == LockType_VM)
15580 directControl = mData->mSession.mDirectControl;
15581
15582 /* directControl may be already set to NULL here in #OnSessionEnd()
15583 * called too early by the direct session process while there is still
15584 * some operation (like deleting the snapshot) in progress. The client
15585 * process in this case is waiting inside Session::close() for the
15586 * "end session" process object to complete, while #uninit() called by
15587 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15588 * operation to complete. For now, we accept this inconsistent behavior
15589 * and simply do nothing here. */
15590
15591 if (mData->mSession.mState == SessionState_Unlocking)
15592 return S_OK;
15593 }
15594
15595 /* ignore notifications sent after #OnSessionEnd() is called */
15596 if (!directControl)
15597 return S_OK;
15598
15599 return directControl->UpdateMachineState(mData->mMachineState);
15600}
15601
15602
15603/*static*/
15604HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15605{
15606 va_list args;
15607 va_start(args, pcszMsg);
15608 HRESULT rc = setErrorInternalV(aResultCode,
15609 getStaticClassIID(),
15610 getStaticComponentName(),
15611 pcszMsg, args,
15612 false /* aWarning */,
15613 true /* aLogIt */);
15614 va_end(args);
15615 return rc;
15616}
15617
15618
15619HRESULT Machine::updateState(MachineState_T aState)
15620{
15621 NOREF(aState);
15622 ReturnComNotImplemented();
15623}
15624
15625HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15626{
15627 NOREF(aProgress);
15628 ReturnComNotImplemented();
15629}
15630
15631HRESULT Machine::endPowerUp(LONG aResult)
15632{
15633 NOREF(aResult);
15634 ReturnComNotImplemented();
15635}
15636
15637HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15638{
15639 NOREF(aProgress);
15640 ReturnComNotImplemented();
15641}
15642
15643HRESULT Machine::endPoweringDown(LONG aResult,
15644 const com::Utf8Str &aErrMsg)
15645{
15646 NOREF(aResult);
15647 NOREF(aErrMsg);
15648 ReturnComNotImplemented();
15649}
15650
15651HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15652 BOOL *aMatched,
15653 ULONG *aMaskedInterfaces)
15654{
15655 NOREF(aDevice);
15656 NOREF(aMatched);
15657 NOREF(aMaskedInterfaces);
15658 ReturnComNotImplemented();
15659
15660}
15661
15662HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15663{
15664 NOREF(aId); NOREF(aCaptureFilename);
15665 ReturnComNotImplemented();
15666}
15667
15668HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15669 BOOL aDone)
15670{
15671 NOREF(aId);
15672 NOREF(aDone);
15673 ReturnComNotImplemented();
15674}
15675
15676HRESULT Machine::autoCaptureUSBDevices()
15677{
15678 ReturnComNotImplemented();
15679}
15680
15681HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15682{
15683 NOREF(aDone);
15684 ReturnComNotImplemented();
15685}
15686
15687HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15688 ComPtr<IProgress> &aProgress)
15689{
15690 NOREF(aSession);
15691 NOREF(aProgress);
15692 ReturnComNotImplemented();
15693}
15694
15695HRESULT Machine::finishOnlineMergeMedium()
15696{
15697 ReturnComNotImplemented();
15698}
15699
15700HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15701 std::vector<com::Utf8Str> &aValues,
15702 std::vector<LONG64> &aTimestamps,
15703 std::vector<com::Utf8Str> &aFlags)
15704{
15705 NOREF(aNames);
15706 NOREF(aValues);
15707 NOREF(aTimestamps);
15708 NOREF(aFlags);
15709 ReturnComNotImplemented();
15710}
15711
15712HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15713 const com::Utf8Str &aValue,
15714 LONG64 aTimestamp,
15715 const com::Utf8Str &aFlags,
15716 BOOL fWasDeleted)
15717{
15718 NOREF(aName);
15719 NOREF(aValue);
15720 NOREF(aTimestamp);
15721 NOREF(aFlags);
15722 NOREF(fWasDeleted);
15723 ReturnComNotImplemented();
15724}
15725
15726HRESULT Machine::lockMedia()
15727{
15728 ReturnComNotImplemented();
15729}
15730
15731HRESULT Machine::unlockMedia()
15732{
15733 ReturnComNotImplemented();
15734}
15735
15736HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15737 ComPtr<IMediumAttachment> &aNewAttachment)
15738{
15739 NOREF(aAttachment);
15740 NOREF(aNewAttachment);
15741 ReturnComNotImplemented();
15742}
15743
15744HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15745 ULONG aCpuUser,
15746 ULONG aCpuKernel,
15747 ULONG aCpuIdle,
15748 ULONG aMemTotal,
15749 ULONG aMemFree,
15750 ULONG aMemBalloon,
15751 ULONG aMemShared,
15752 ULONG aMemCache,
15753 ULONG aPagedTotal,
15754 ULONG aMemAllocTotal,
15755 ULONG aMemFreeTotal,
15756 ULONG aMemBalloonTotal,
15757 ULONG aMemSharedTotal,
15758 ULONG aVmNetRx,
15759 ULONG aVmNetTx)
15760{
15761 NOREF(aValidStats);
15762 NOREF(aCpuUser);
15763 NOREF(aCpuKernel);
15764 NOREF(aCpuIdle);
15765 NOREF(aMemTotal);
15766 NOREF(aMemFree);
15767 NOREF(aMemBalloon);
15768 NOREF(aMemShared);
15769 NOREF(aMemCache);
15770 NOREF(aPagedTotal);
15771 NOREF(aMemAllocTotal);
15772 NOREF(aMemFreeTotal);
15773 NOREF(aMemBalloonTotal);
15774 NOREF(aMemSharedTotal);
15775 NOREF(aVmNetRx);
15776 NOREF(aVmNetTx);
15777 ReturnComNotImplemented();
15778}
15779
15780HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15781 com::Utf8Str &aResult)
15782{
15783 NOREF(aAuthParams);
15784 NOREF(aResult);
15785 ReturnComNotImplemented();
15786}
15787
15788com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15789{
15790 com::Utf8Str strControllerName = "Unknown";
15791 switch (aBusType)
15792 {
15793 case StorageBus_IDE:
15794 {
15795 strControllerName = "IDE";
15796 break;
15797 }
15798 case StorageBus_SATA:
15799 {
15800 strControllerName = "SATA";
15801 break;
15802 }
15803 case StorageBus_SCSI:
15804 {
15805 strControllerName = "SCSI";
15806 break;
15807 }
15808 case StorageBus_Floppy:
15809 {
15810 strControllerName = "Floppy";
15811 break;
15812 }
15813 case StorageBus_SAS:
15814 {
15815 strControllerName = "SAS";
15816 break;
15817 }
15818 case StorageBus_USB:
15819 {
15820 strControllerName = "USB";
15821 break;
15822 }
15823 default:
15824 break;
15825 }
15826 return strControllerName;
15827}
15828
15829HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15830{
15831 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15832
15833 AutoCaller autoCaller(this);
15834 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15835
15836 HRESULT rc = S_OK;
15837
15838 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15839 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15840 rc = getUSBDeviceFilters(usbDeviceFilters);
15841 if (FAILED(rc)) return rc;
15842
15843 NOREF(aFlags);
15844 com::Utf8Str osTypeId;
15845 ComObjPtr<GuestOSType> osType = NULL;
15846
15847 /* Get the guest os type as a string from the VB. */
15848 rc = getOSTypeId(osTypeId);
15849 if (FAILED(rc)) return rc;
15850
15851 /* Get the os type obj that coresponds, can be used to get
15852 * the defaults for this guest OS. */
15853 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15854 if (FAILED(rc)) return rc;
15855
15856 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15857
15858 /* Let the OS type select 64-bit ness. */
15859 mHWData->mLongMode = osType->i_is64Bit()
15860 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15861
15862 /* Let the OS type enable the X2APIC */
15863 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15864
15865 /* This one covers IOAPICEnabled. */
15866 mBIOSSettings->i_applyDefaults(osType);
15867
15868 /* Initialize default record settings. */
15869 mRecordingSettings->i_applyDefaults();
15870
15871 /* Initialize default BIOS settings here */
15872 /* Hardware virtualization must be ON by default */
15873 mHWData->mAPIC = true;
15874 mHWData->mHWVirtExEnabled = true;
15875
15876 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15877 if (FAILED(rc)) return rc;
15878
15879 rc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15880 if (FAILED(rc)) return rc;
15881
15882 /* Graphics stuff. */
15883 GraphicsControllerType_T graphicsController;
15884 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15885 if (FAILED(rc)) return rc;
15886
15887 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15888 if (FAILED(rc)) return rc;
15889
15890 ULONG vramSize;
15891 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15892 if (FAILED(rc)) return rc;
15893
15894 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15895 if (FAILED(rc)) return rc;
15896
15897 BOOL fAccelerate2DVideoEnabled;
15898 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15899 if (FAILED(rc)) return rc;
15900
15901 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15902 if (FAILED(rc)) return rc;
15903
15904 BOOL fAccelerate3DEnabled;
15905 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15906 if (FAILED(rc)) return rc;
15907
15908 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15909 if (FAILED(rc)) return rc;
15910
15911 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15912 if (FAILED(rc)) return rc;
15913
15914 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15915 if (FAILED(rc)) return rc;
15916
15917 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15918 if (FAILED(rc)) return rc;
15919
15920 BOOL mRTCUseUTC;
15921 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15922 if (FAILED(rc)) return rc;
15923
15924 setRTCUseUTC(mRTCUseUTC);
15925 if (FAILED(rc)) return rc;
15926
15927 /* the setter does more than just the assignment, so use it */
15928 ChipsetType_T enmChipsetType;
15929 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15930 if (FAILED(rc)) return rc;
15931
15932 rc = COMSETTER(ChipsetType)(enmChipsetType);
15933 if (FAILED(rc)) return rc;
15934
15935 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15936 if (FAILED(rc)) return rc;
15937
15938 /* Apply IOMMU defaults. */
15939 IommuType_T enmIommuType;
15940 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15941 if (FAILED(rc)) return rc;
15942
15943 rc = COMSETTER(IommuType)(enmIommuType);
15944 if (FAILED(rc)) return rc;
15945
15946 /* Apply network adapters defaults */
15947 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15948 mNetworkAdapters[slot]->i_applyDefaults(osType);
15949
15950 /* Apply serial port defaults */
15951 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15952 mSerialPorts[slot]->i_applyDefaults(osType);
15953
15954 /* Apply parallel port defaults - not OS dependent*/
15955 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15956 mParallelPorts[slot]->i_applyDefaults();
15957
15958 /* This one covers the TPM type. */
15959 mTrustedPlatformModule->i_applyDefaults(osType);
15960
15961 /* This one covers secure boot. */
15962 rc = mNvramStore->i_applyDefaults(osType);
15963 if (FAILED(rc)) return rc;
15964
15965 /* Audio stuff. */
15966 rc = mAudioSettings->i_applyDefaults(osType);
15967 if (FAILED(rc)) return rc;
15968
15969 /* Storage Controllers */
15970 StorageControllerType_T hdStorageControllerType;
15971 StorageBus_T hdStorageBusType;
15972 StorageControllerType_T dvdStorageControllerType;
15973 StorageBus_T dvdStorageBusType;
15974 BOOL recommendedFloppy;
15975 ComPtr<IStorageController> floppyController;
15976 ComPtr<IStorageController> hdController;
15977 ComPtr<IStorageController> dvdController;
15978 Utf8Str strFloppyName, strDVDName, strHDName;
15979
15980 /* GUI auto generates controller names using bus type. Do the same*/
15981 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15982
15983 /* Floppy recommended? add one. */
15984 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15985 if (FAILED(rc)) return rc;
15986 if (recommendedFloppy)
15987 {
15988 rc = addStorageController(strFloppyName,
15989 StorageBus_Floppy,
15990 floppyController);
15991 if (FAILED(rc)) return rc;
15992 }
15993
15994 /* Setup one DVD storage controller. */
15995 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15996 if (FAILED(rc)) return rc;
15997
15998 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15999 if (FAILED(rc)) return rc;
16000
16001 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
16002
16003 rc = addStorageController(strDVDName,
16004 dvdStorageBusType,
16005 dvdController);
16006 if (FAILED(rc)) return rc;
16007
16008 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
16009 if (FAILED(rc)) return rc;
16010
16011 /* Setup one HDD storage controller. */
16012 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
16013 if (FAILED(rc)) return rc;
16014
16015 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
16016 if (FAILED(rc)) return rc;
16017
16018 strHDName = i_controllerNameFromBusType(hdStorageBusType);
16019
16020 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
16021 {
16022 rc = addStorageController(strHDName,
16023 hdStorageBusType,
16024 hdController);
16025 if (FAILED(rc)) return rc;
16026
16027 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
16028 if (FAILED(rc)) return rc;
16029 }
16030 else
16031 {
16032 /* The HD controller is the same as DVD: */
16033 hdController = dvdController;
16034 }
16035
16036 /* Limit the AHCI port count if it's used because windows has trouble with
16037 * too many ports and other guest (OS X in particular) may take extra long
16038 * boot: */
16039
16040 // pParent = static_cast<Medium*>(aP)
16041 IStorageController *temp = hdController;
16042 ComObjPtr<StorageController> storageController;
16043 storageController = static_cast<StorageController *>(temp);
16044
16045 // tempHDController = aHDController;
16046 if (hdStorageControllerType == StorageControllerType_IntelAhci)
16047 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
16048 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
16049 storageController->COMSETTER(PortCount)(1);
16050
16051 /* USB stuff */
16052
16053 bool ohciEnabled = false;
16054
16055 ComPtr<IUSBController> usbController;
16056 BOOL recommendedUSB3;
16057 BOOL recommendedUSB;
16058 BOOL usbProxyAvailable;
16059
16060 getUSBProxyAvailable(&usbProxyAvailable);
16061 if (FAILED(rc)) return rc;
16062
16063 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
16064 if (FAILED(rc)) return rc;
16065 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
16066 if (FAILED(rc)) return rc;
16067
16068 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
16069 {
16070 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
16071 if (FAILED(rc)) return rc;
16072
16073 /* xHci includes OHCI */
16074 ohciEnabled = true;
16075 }
16076 if ( !ohciEnabled
16077 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
16078 {
16079 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16080 if (FAILED(rc)) return rc;
16081 ohciEnabled = true;
16082
16083 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
16084 if (FAILED(rc)) return rc;
16085 }
16086
16087 /* Set recommended human interface device types: */
16088 BOOL recommendedUSBHID;
16089 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
16090 if (FAILED(rc)) return rc;
16091
16092 if (recommendedUSBHID)
16093 {
16094 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
16095 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
16096 if (!ohciEnabled && !usbDeviceFilters.isNull())
16097 {
16098 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16099 if (FAILED(rc)) return rc;
16100 }
16101 }
16102
16103 BOOL recommendedUSBTablet;
16104 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
16105 if (FAILED(rc)) return rc;
16106
16107 if (recommendedUSBTablet)
16108 {
16109 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
16110 if (!ohciEnabled && !usbDeviceFilters.isNull())
16111 {
16112 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16113 if (FAILED(rc)) return rc;
16114 }
16115 }
16116
16117 /* Enable the VMMDev testing feature for bootsector VMs: */
16118 if (osTypeId == "VBoxBS_64")
16119 {
16120 rc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
16121 if (FAILED(rc))
16122 return rc;
16123 }
16124
16125 return S_OK;
16126}
16127
16128#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
16129/**
16130 * Task record for change encryption settins.
16131 */
16132class Machine::ChangeEncryptionTask
16133 : public Machine::Task
16134{
16135public:
16136 ChangeEncryptionTask(Machine *m,
16137 Progress *p,
16138 const Utf8Str &t,
16139 const com::Utf8Str &aCurrentPassword,
16140 const com::Utf8Str &aCipher,
16141 const com::Utf8Str &aNewPassword,
16142 const com::Utf8Str &aNewPasswordId,
16143 const BOOL aForce,
16144 const MediaList &llMedia)
16145 : Task(m, p, t),
16146 mstrNewPassword(aNewPassword),
16147 mstrCurrentPassword(aCurrentPassword),
16148 mstrCipher(aCipher),
16149 mstrNewPasswordId(aNewPasswordId),
16150 mForce(aForce),
16151 mllMedia(llMedia)
16152 {}
16153
16154 ~ChangeEncryptionTask()
16155 {
16156 if (mstrNewPassword.length())
16157 RTMemWipeThoroughly(mstrNewPassword.mutableRaw(), mstrNewPassword.length(), 10 /* cPasses */);
16158 if (mstrCurrentPassword.length())
16159 RTMemWipeThoroughly(mstrCurrentPassword.mutableRaw(), mstrCurrentPassword.length(), 10 /* cPasses */);
16160 if (m_pCryptoIf)
16161 {
16162 m_pMachine->i_getVirtualBox()->i_releaseCryptoIf(m_pCryptoIf);
16163 m_pCryptoIf = NULL;
16164 }
16165 }
16166
16167 Utf8Str mstrNewPassword;
16168 Utf8Str mstrCurrentPassword;
16169 Utf8Str mstrCipher;
16170 Utf8Str mstrNewPasswordId;
16171 BOOL mForce;
16172 MediaList mllMedia;
16173 PCVBOXCRYPTOIF m_pCryptoIf;
16174private:
16175 void handler()
16176 {
16177 try
16178 {
16179 m_pMachine->i_changeEncryptionHandler(*this);
16180 }
16181 catch (...)
16182 {
16183 LogRel(("Some exception in the function Machine::i_changeEncryptionHandler()\n"));
16184 }
16185 }
16186
16187 friend void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task);
16188};
16189
16190/**
16191 * Scans specified directory and fills list by files found
16192 *
16193 * @returns VBox status code.
16194 * @param lstFiles
16195 * @param strDir
16196 * @param filePattern
16197 */
16198int Machine::i_findFiles(std::list<com::Utf8Str> &lstFiles, const com::Utf8Str &strDir,
16199 const com::Utf8Str &strPattern)
16200{
16201 /* To get all entries including subdirectories. */
16202 char *pszFilePattern = RTPathJoinA(strDir.c_str(), "*");
16203 if (!pszFilePattern)
16204 return VERR_NO_STR_MEMORY;
16205
16206 PRTDIRENTRYEX pDirEntry = NULL;
16207 RTDIR hDir;
16208 size_t cbDirEntry = sizeof(RTDIRENTRYEX);
16209 int rc = RTDirOpenFiltered(&hDir, pszFilePattern, RTDIRFILTER_WINNT, 0 /*fFlags*/);
16210 if (RT_SUCCESS(rc))
16211 {
16212 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
16213 if (pDirEntry)
16214 {
16215 while ( (rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK))
16216 != VERR_NO_MORE_FILES)
16217 {
16218 char *pszFilePath = NULL;
16219
16220 if (rc == VERR_BUFFER_OVERFLOW)
16221 {
16222 /* allocate new buffer. */
16223 RTMemFree(pDirEntry);
16224 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbDirEntry);
16225 if (!pDirEntry)
16226 {
16227 rc = VERR_NO_MEMORY;
16228 break;
16229 }
16230 /* Retry. */
16231 rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
16232 if (RT_FAILURE(rc))
16233 break;
16234 }
16235 else if (RT_FAILURE(rc))
16236 break;
16237
16238 /* Exclude . and .. */
16239 if ( (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '\0')
16240 || (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '.' && pDirEntry->szName[2] == '\0'))
16241 continue;
16242 if (RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode))
16243 {
16244 char *pszSubDirPath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
16245 if (!pszSubDirPath)
16246 {
16247 rc = VERR_NO_STR_MEMORY;
16248 break;
16249 }
16250 rc = i_findFiles(lstFiles, pszSubDirPath, strPattern);
16251 RTMemFree(pszSubDirPath);
16252 if (RT_FAILURE(rc))
16253 break;
16254 continue;
16255 }
16256
16257 /* We got the new entry. */
16258 if (!RTFS_IS_FILE(pDirEntry->Info.Attr.fMode))
16259 continue;
16260
16261 if (!RTStrSimplePatternMatch(strPattern.c_str(), pDirEntry->szName))
16262 continue;
16263
16264 /* Prepend the path to the libraries. */
16265 pszFilePath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
16266 if (!pszFilePath)
16267 {
16268 rc = VERR_NO_STR_MEMORY;
16269 break;
16270 }
16271
16272 lstFiles.push_back(pszFilePath);
16273 RTStrFree(pszFilePath);
16274 }
16275
16276 RTMemFree(pDirEntry);
16277 }
16278 else
16279 rc = VERR_NO_MEMORY;
16280
16281 RTDirClose(hDir);
16282 }
16283 else
16284 {
16285 /* On Windows the above immediately signals that there are no
16286 * files matching, while on other platforms enumerating the
16287 * files below fails. Either way: stop searching. */
16288 }
16289
16290 if ( rc == VERR_NO_MORE_FILES
16291 || rc == VERR_FILE_NOT_FOUND
16292 || rc == VERR_PATH_NOT_FOUND)
16293 rc = VINF_SUCCESS;
16294 RTStrFree(pszFilePattern);
16295 return rc;
16296}
16297
16298/**
16299 * Helper to set up an I/O stream to read or write a possibly encrypted file.
16300 *
16301 * @returns VBox status code.
16302 * @param pszFilename The file to open.
16303 * @param pCryptoIf Pointer to the cryptographic interface if the file should be encrypted or contains encrypted data.
16304 * @param pszKeyStore The keystore if the file should be encrypted or contains encrypted data.
16305 * @param pszPassword The password if the file should be encrypted or contains encrypted data.
16306 * @param fOpen The open flags for the file.
16307 * @param phVfsIos Where to store the handle to the I/O stream on success.
16308 */
16309int Machine::i_createIoStreamForFile(const char *pszFilename, PCVBOXCRYPTOIF pCryptoIf,
16310 const char *pszKeyStore, const char *pszPassword,
16311 uint64_t fOpen, PRTVFSIOSTREAM phVfsIos)
16312{
16313 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
16314 int vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
16315 if (RT_SUCCESS(vrc))
16316 {
16317 if (pCryptoIf)
16318 {
16319 RTVFSFILE hVfsFileCrypto = NIL_RTVFSFILE;
16320 vrc = pCryptoIf->pfnCryptoFileFromVfsFile(hVfsFile, pszKeyStore, pszPassword, &hVfsFileCrypto);
16321 if (RT_SUCCESS(vrc))
16322 {
16323 RTVfsFileRelease(hVfsFile);
16324 hVfsFile = hVfsFileCrypto;
16325 }
16326 }
16327
16328 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
16329 RTVfsFileRelease(hVfsFile);
16330 }
16331
16332 return vrc;
16333}
16334
16335/**
16336 * Helper function processing all actions for one component (saved state files,
16337 * NVRAM files, etc). Used by Machine::i_changeEncryptionHandler only.
16338 *
16339 * @param task
16340 * @param strDirectory
16341 * @param strFilePattern
16342 * @param strMagic
16343 * @param strKeyStore
16344 * @param strKeyId
16345 * @return
16346 */
16347HRESULT Machine::i_changeEncryptionForComponent(ChangeEncryptionTask &task, const com::Utf8Str strDirectory,
16348 const com::Utf8Str strFilePattern, com::Utf8Str &strKeyStore,
16349 com::Utf8Str &strKeyId, int iCipherMode)
16350{
16351 bool fDecrypt = task.mstrCurrentPassword.isNotEmpty()
16352 && task.mstrCipher.isEmpty()
16353 && task.mstrNewPassword.isEmpty()
16354 && task.mstrNewPasswordId.isEmpty();
16355 bool fEncrypt = task.mstrCurrentPassword.isEmpty()
16356 && task.mstrCipher.isNotEmpty()
16357 && task.mstrNewPassword.isNotEmpty()
16358 && task.mstrNewPasswordId.isNotEmpty();
16359
16360 /* check if the cipher is changed which causes the reencryption*/
16361
16362 const char *pszTaskCipher = NULL;
16363 if (task.mstrCipher.isNotEmpty())
16364 pszTaskCipher = getCipherString(task.mstrCipher.c_str(), iCipherMode);
16365
16366 if (!task.mForce && !fDecrypt && !fEncrypt)
16367 {
16368 char *pszCipher = NULL;
16369 int vrc = task.m_pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(strKeyStore.c_str(),
16370 NULL /*pszPassword*/,
16371 NULL /*ppbKey*/,
16372 NULL /*pcbKey*/,
16373 &pszCipher);
16374 if (RT_SUCCESS(vrc))
16375 {
16376 task.mForce = strcmp(pszTaskCipher, pszCipher) != 0;
16377 RTMemFree(pszCipher);
16378 }
16379 else
16380 return setErrorBoth(E_FAIL, vrc, tr("Obtain cipher for '%s' files failed (%Rrc)"),
16381 strFilePattern.c_str(), vrc);
16382 }
16383
16384 /* Only the password needs to be changed */
16385 if (!task.mForce && !fDecrypt && !fEncrypt)
16386 {
16387 Assert(task.m_pCryptoIf);
16388
16389 VBOXCRYPTOCTX hCryptoCtx;
16390 int vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(strKeyStore.c_str(), task.mstrCurrentPassword.c_str(), &hCryptoCtx);
16391 if (RT_FAILURE(vrc))
16392 return setErrorBoth(E_FAIL, vrc, tr("Loading old key store for '%s' files failed, (%Rrc)"),
16393 strFilePattern.c_str(), vrc);
16394 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
16395 if (RT_FAILURE(vrc))
16396 return setErrorBoth(E_FAIL, vrc, tr("Changing the password for '%s' files failed, (%Rrc)"),
16397 strFilePattern.c_str(), vrc);
16398
16399 char *pszKeyStore = NULL;
16400 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16401 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16402 if (RT_FAILURE(vrc))
16403 return setErrorBoth(E_FAIL, vrc, tr("Saving the key store for '%s' files failed, (%Rrc)"),
16404 strFilePattern.c_str(), vrc);
16405 strKeyStore = pszKeyStore;
16406 RTMemFree(pszKeyStore);
16407 strKeyId = task.mstrNewPasswordId;
16408 return S_OK;
16409 }
16410
16411 /* Reencryption required */
16412 HRESULT rc = S_OK;
16413 int vrc = VINF_SUCCESS;
16414
16415 std::list<com::Utf8Str> lstFiles;
16416 if (SUCCEEDED(rc))
16417 {
16418 vrc = i_findFiles(lstFiles, strDirectory, strFilePattern);
16419 if (RT_FAILURE(vrc))
16420 rc = setErrorBoth(E_FAIL, vrc, tr("Getting file list for '%s' files failed, (%Rrc)"),
16421 strFilePattern.c_str(), vrc);
16422 }
16423 com::Utf8Str strNewKeyStore;
16424 if (SUCCEEDED(rc))
16425 {
16426 if (!fDecrypt)
16427 {
16428 VBOXCRYPTOCTX hCryptoCtx;
16429 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(pszTaskCipher, task.mstrNewPassword.c_str(), &hCryptoCtx);
16430 if (RT_FAILURE(vrc))
16431 return setErrorBoth(E_FAIL, vrc, tr("Create new key store for '%s' files failed, (%Rrc)"),
16432 strFilePattern.c_str(), vrc);
16433
16434 char *pszKeyStore = NULL;
16435 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16436 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16437 if (RT_FAILURE(vrc))
16438 return setErrorBoth(E_FAIL, vrc, tr("Saving the new key store for '%s' files failed, (%Rrc)"),
16439 strFilePattern.c_str(), vrc);
16440 strNewKeyStore = pszKeyStore;
16441 RTMemFree(pszKeyStore);
16442 }
16443
16444 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
16445 it != lstFiles.end();
16446 ++it)
16447 {
16448 RTVFSIOSTREAM hVfsIosOld = NIL_RTVFSIOSTREAM;
16449 RTVFSIOSTREAM hVfsIosNew = NIL_RTVFSIOSTREAM;
16450
16451 uint64_t fOpenForRead = RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE;
16452 uint64_t fOpenForWrite = RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE;
16453
16454 vrc = i_createIoStreamForFile((*it).c_str(),
16455 fEncrypt ? NULL : task.m_pCryptoIf,
16456 fEncrypt ? NULL : strKeyStore.c_str(),
16457 fEncrypt ? NULL : task.mstrCurrentPassword.c_str(),
16458 fOpenForRead, &hVfsIosOld);
16459 if (RT_SUCCESS(vrc))
16460 {
16461 vrc = i_createIoStreamForFile((*it + ".tmp").c_str(),
16462 fDecrypt ? NULL : task.m_pCryptoIf,
16463 fDecrypt ? NULL : strNewKeyStore.c_str(),
16464 fDecrypt ? NULL : task.mstrNewPassword.c_str(),
16465 fOpenForWrite, &hVfsIosNew);
16466 if (RT_FAILURE(vrc))
16467 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
16468 (*it + ".tmp").c_str(), vrc);
16469 }
16470 else
16471 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
16472 (*it).c_str(), vrc);
16473
16474 if (RT_SUCCESS(vrc))
16475 {
16476 vrc = RTVfsUtilPumpIoStreams(hVfsIosOld, hVfsIosNew, BUF_DATA_SIZE);
16477 if (RT_FAILURE(vrc))
16478 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Changing encryption of the file '%s' failed with %Rrc"),
16479 (*it).c_str(), vrc);
16480 }
16481
16482 if (hVfsIosOld != NIL_RTVFSIOSTREAM)
16483 RTVfsIoStrmRelease(hVfsIosOld);
16484 if (hVfsIosNew != NIL_RTVFSIOSTREAM)
16485 RTVfsIoStrmRelease(hVfsIosNew);
16486 }
16487 }
16488
16489 if (SUCCEEDED(rc))
16490 {
16491 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
16492 it != lstFiles.end();
16493 ++it)
16494 {
16495 vrc = RTFileRename((*it + ".tmp").c_str(), (*it).c_str(), RTPATHRENAME_FLAGS_REPLACE);
16496 if (RT_FAILURE(vrc))
16497 {
16498 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming the file '%s' failed, (%Rrc)"),
16499 (*it + ".tmp").c_str(), vrc);
16500 break;
16501 }
16502 }
16503 }
16504
16505 if (SUCCEEDED(rc))
16506 {
16507 strKeyStore = strNewKeyStore;
16508 strKeyId = task.mstrNewPasswordId;
16509 }
16510
16511 return rc;
16512}
16513
16514/**
16515 * Task thread implementation for Machine::changeEncryption(), called from
16516 * Machine::taskHandler().
16517 *
16518 * @note Locks this object for writing.
16519 *
16520 * @param task
16521 * @return
16522 */
16523void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task)
16524{
16525 LogFlowThisFuncEnter();
16526
16527 AutoCaller autoCaller(this);
16528 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
16529 if (FAILED(autoCaller.rc()))
16530 {
16531 /* we might have been uninitialized because the session was accidentally
16532 * closed by the client, so don't assert */
16533 HRESULT rc = setError(E_FAIL,
16534 tr("The session has been accidentally closed"));
16535 task.m_pProgress->i_notifyComplete(rc);
16536 LogFlowThisFuncLeave();
16537 return;
16538 }
16539
16540 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16541
16542 HRESULT rc = S_OK;
16543 com::Utf8Str strOldKeyId = mData->mstrKeyId;
16544 com::Utf8Str strOldKeyStore = mData->mstrKeyStore;
16545 try
16546 {
16547 rc = this->i_getVirtualBox()->i_retainCryptoIf(&task.m_pCryptoIf);
16548 if (FAILED(rc))
16549 throw rc;
16550
16551 if (task.mstrCurrentPassword.isEmpty())
16552 {
16553 if (mData->mstrKeyStore.isNotEmpty())
16554 throw setError(VBOX_E_PASSWORD_INCORRECT,
16555 tr("The password given for the encrypted VM is incorrect"));
16556 }
16557 else
16558 {
16559 if (mData->mstrKeyStore.isEmpty())
16560 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16561 tr("The VM is not configured for encryption"));
16562 rc = checkEncryptionPassword(task.mstrCurrentPassword);
16563 if (rc == VBOX_E_PASSWORD_INCORRECT)
16564 throw setError(VBOX_E_PASSWORD_INCORRECT,
16565 tr("The password to decrypt the VM is incorrect"));
16566 }
16567
16568 if (task.mstrCipher.isNotEmpty())
16569 {
16570 if ( task.mstrNewPassword.isEmpty()
16571 && task.mstrNewPasswordId.isEmpty()
16572 && task.mstrCurrentPassword.isNotEmpty())
16573 {
16574 /* An empty password and password ID will default to the current password. */
16575 task.mstrNewPassword = task.mstrCurrentPassword;
16576 }
16577 else if (task.mstrNewPassword.isEmpty())
16578 throw setError(VBOX_E_OBJECT_NOT_FOUND,
16579 tr("A password must be given for the VM encryption"));
16580 else if (task.mstrNewPasswordId.isEmpty())
16581 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16582 tr("A valid identifier for the password must be given"));
16583 }
16584 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
16585 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16586 tr("The password and password identifier must be empty if the output should be unencrypted"));
16587
16588 /*
16589 * Save config.
16590 * Must be first operation to prevent making encrypted copies
16591 * for old version of the config file.
16592 */
16593 int fSave = Machine::SaveS_Force;
16594 if (task.mstrNewPassword.isNotEmpty())
16595 {
16596 VBOXCRYPTOCTX hCryptoCtx;
16597
16598 int vrc = VINF_SUCCESS;
16599 if (task.mForce || task.mstrCurrentPassword.isEmpty() || task.mstrCipher.isNotEmpty())
16600 {
16601 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(getCipherString(task.mstrCipher.c_str(), CipherModeGcm),
16602 task.mstrNewPassword.c_str(), &hCryptoCtx);
16603 if (RT_FAILURE(vrc))
16604 throw setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
16605 }
16606 else
16607 {
16608 vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(mData->mstrKeyStore.c_str(),
16609 task.mstrCurrentPassword.c_str(),
16610 &hCryptoCtx);
16611 if (RT_FAILURE(vrc))
16612 throw setErrorBoth(E_FAIL, vrc, tr("Loading old key store failed, (%Rrc)"), vrc);
16613 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
16614 if (RT_FAILURE(vrc))
16615 throw setErrorBoth(E_FAIL, vrc, tr("Changing the password failed, (%Rrc)"), vrc);
16616 }
16617
16618 char *pszKeyStore;
16619 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16620 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16621 if (RT_FAILURE(vrc))
16622 throw setErrorBoth(E_FAIL, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
16623 mData->mstrKeyStore = pszKeyStore;
16624 RTStrFree(pszKeyStore);
16625 mData->mstrKeyId = task.mstrNewPasswordId;
16626 size_t cbPassword = task.mstrNewPassword.length() + 1;
16627 uint8_t *pbPassword = (uint8_t *)task.mstrNewPassword.c_str();
16628 mData->mpKeyStore->deleteSecretKey(task.mstrNewPasswordId);
16629 mData->mpKeyStore->addSecretKey(task.mstrNewPasswordId, pbPassword, cbPassword);
16630 mNvramStore->i_addPassword(task.mstrNewPasswordId, task.mstrNewPassword);
16631
16632 /*
16633 * Remove backuped config after saving because it can contain
16634 * unencrypted version of the config
16635 */
16636 fSave |= Machine::SaveS_RemoveBackup;
16637 }
16638 else
16639 {
16640 mData->mstrKeyId.setNull();
16641 mData->mstrKeyStore.setNull();
16642 }
16643
16644 Bstr bstrCurrentPassword(task.mstrCurrentPassword);
16645 Bstr bstrCipher(getCipherString(task.mstrCipher.c_str(), CipherModeXts));
16646 Bstr bstrNewPassword(task.mstrNewPassword);
16647 Bstr bstrNewPasswordId(task.mstrNewPasswordId);
16648 /* encrypt mediums */
16649 alock.release();
16650 for (MediaList::iterator it = task.mllMedia.begin();
16651 it != task.mllMedia.end();
16652 ++it)
16653 {
16654 ComPtr<IProgress> pProgress1;
16655 HRESULT hrc = (*it)->ChangeEncryption(bstrCurrentPassword.raw(), bstrCipher.raw(),
16656 bstrNewPassword.raw(), bstrNewPasswordId.raw(),
16657 pProgress1.asOutParam());
16658 if (FAILED(hrc)) throw hrc;
16659 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress1, 0 /* indefinite wait */);
16660 if (FAILED(hrc)) throw hrc;
16661 }
16662 alock.acquire();
16663
16664 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the SAV files")).raw(), 1);
16665
16666 Utf8Str strFullSnapshotFolder;
16667 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
16668
16669 /* .sav files (main and snapshots) */
16670 rc = i_changeEncryptionForComponent(task, strFullSnapshotFolder, "*.sav",
16671 mSSData->strStateKeyStore, mSSData->strStateKeyId, CipherModeGcm);
16672 if (FAILED(rc))
16673 /* the helper function already sets error object */
16674 throw rc;
16675
16676 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the NVRAM files")).raw(), 1);
16677
16678 /* .nvram files */
16679 com::Utf8Str strNVRAMKeyId;
16680 com::Utf8Str strNVRAMKeyStore;
16681 rc = mNvramStore->i_getEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16682 if (FAILED(rc))
16683 throw setError(rc, tr("Getting NVRAM encryption settings failed (%Rhrc)"), rc);
16684
16685 Utf8Str strMachineFolder;
16686 i_calculateFullPath(".", strMachineFolder);
16687
16688 rc = i_changeEncryptionForComponent(task, strMachineFolder, "*.nvram",
16689 strNVRAMKeyStore, strNVRAMKeyId, CipherModeGcm);
16690 if (FAILED(rc))
16691 /* the helper function already sets error object */
16692 throw rc;
16693
16694 rc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16695 if (FAILED(rc))
16696 throw setError(rc, tr("Setting NVRAM encryption settings failed (%Rhrc)"), rc);
16697
16698 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of log files")).raw(), 1);
16699
16700 /* .log files */
16701 com::Utf8Str strLogFolder;
16702 i_getLogFolder(strLogFolder);
16703 rc = i_changeEncryptionForComponent(task, strLogFolder, "VBox.log*",
16704 mData->mstrLogKeyStore, mData->mstrLogKeyId, CipherModeCtr);
16705 if (FAILED(rc))
16706 /* the helper function already sets error object */
16707 throw rc;
16708
16709 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the config file")).raw(), 1);
16710
16711 i_saveSettings(NULL, alock, fSave);
16712 }
16713 catch (HRESULT aRC)
16714 {
16715 rc = aRC;
16716 mData->mstrKeyId = strOldKeyId;
16717 mData->mstrKeyStore = strOldKeyStore;
16718 }
16719
16720 task.m_pProgress->i_notifyComplete(rc);
16721
16722 LogFlowThisFuncLeave();
16723}
16724#endif /*!VBOX_WITH_FULL_VM_ENCRYPTION*/
16725
16726HRESULT Machine::changeEncryption(const com::Utf8Str &aCurrentPassword,
16727 const com::Utf8Str &aCipher,
16728 const com::Utf8Str &aNewPassword,
16729 const com::Utf8Str &aNewPasswordId,
16730 BOOL aForce,
16731 ComPtr<IProgress> &aProgress)
16732{
16733 LogFlowFuncEnter();
16734
16735#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16736 RT_NOREF(aCurrentPassword, aCipher, aNewPassword, aNewPasswordId, aForce, aProgress);
16737 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16738#else
16739 /* make the VM accessible */
16740 if (!mData->mAccessible)
16741 {
16742 if ( aCurrentPassword.isEmpty()
16743 || mData->mstrKeyId.isEmpty())
16744 return setError(E_ACCESSDENIED, tr("Machine is inaccessible"));
16745
16746 HRESULT rc = addEncryptionPassword(mData->mstrKeyId, aCurrentPassword);
16747 if (FAILED(rc))
16748 return rc;
16749 }
16750
16751 AutoLimitedCaller autoCaller(this);
16752 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16753
16754 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16755
16756 /* define mediums to be change encryption */
16757
16758 MediaList llMedia;
16759 for (MediumAttachmentList::iterator
16760 it = mMediumAttachments->begin();
16761 it != mMediumAttachments->end();
16762 ++it)
16763 {
16764 ComObjPtr<MediumAttachment> &pAttach = *it;
16765 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
16766
16767 if (!pMedium.isNull())
16768 {
16769 AutoCaller mac(pMedium);
16770 if (FAILED(mac.rc())) return mac.rc();
16771 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
16772 DeviceType_T devType = pMedium->i_getDeviceType();
16773 if (devType == DeviceType_HardDisk)
16774 {
16775 /*
16776 * We need to move to last child because the Medium::changeEncryption
16777 * encrypts all chain of specified medium with its parents.
16778 * Also we perform cheking of back reference and children for
16779 * all media in the chain to raise error before we start any action.
16780 * So, we first move into root parent and then we will move to last child
16781 * keeping latter in the list for encryption.
16782 */
16783
16784 /* move to root parent */
16785 ComObjPtr<Medium> pTmpMedium = pMedium;
16786 while (pTmpMedium.isNotNull())
16787 {
16788 AutoCaller mediumAC(pTmpMedium);
16789 if (FAILED(mediumAC.rc())) return mac.rc();
16790 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16791
16792 /* Cannot encrypt media which are attached to more than one virtual machine. */
16793 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16794 if (cBackRefs > 1)
16795 return setError(VBOX_E_INVALID_OBJECT_STATE,
16796 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16797 pTmpMedium->i_getName().c_str(), cBackRefs);
16798
16799 size_t cChildren = pTmpMedium->i_getChildren().size();
16800 if (cChildren > 1)
16801 return setError(VBOX_E_INVALID_OBJECT_STATE,
16802 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16803 pTmpMedium->i_getName().c_str(), cChildren);
16804
16805 pTmpMedium = pTmpMedium->i_getParent();
16806 }
16807 /* move to last child */
16808 pTmpMedium = pMedium;
16809 while (pTmpMedium.isNotNull() && pTmpMedium->i_getChildren().size() != 0)
16810 {
16811 AutoCaller mediumAC(pTmpMedium);
16812 if (FAILED(mediumAC.rc())) return mac.rc();
16813 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16814
16815 /* Cannot encrypt media which are attached to more than one virtual machine. */
16816 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16817 if (cBackRefs > 1)
16818 return setError(VBOX_E_INVALID_OBJECT_STATE,
16819 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16820 pTmpMedium->i_getName().c_str(), cBackRefs);
16821
16822 size_t cChildren = pTmpMedium->i_getChildren().size();
16823 if (cChildren > 1)
16824 return setError(VBOX_E_INVALID_OBJECT_STATE,
16825 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16826 pTmpMedium->i_getName().c_str(), cChildren);
16827
16828 pTmpMedium = pTmpMedium->i_getChildren().front();
16829 }
16830 llMedia.push_back(pTmpMedium);
16831 }
16832 }
16833 }
16834
16835 ComObjPtr<Progress> pProgress;
16836 pProgress.createObject();
16837 HRESULT rc = pProgress->init(i_getVirtualBox(),
16838 static_cast<IMachine*>(this) /* aInitiator */,
16839 tr("Change encryption"),
16840 TRUE /* fCancellable */,
16841 (ULONG)(4 + + llMedia.size()), // cOperations
16842 tr("Change encryption of the mediuma"));
16843 if (FAILED(rc))
16844 return rc;
16845
16846 /* create and start the task on a separate thread (note that it will not
16847 * start working until we release alock) */
16848 ChangeEncryptionTask *pTask = new ChangeEncryptionTask(this, pProgress, "VM encryption",
16849 aCurrentPassword, aCipher, aNewPassword,
16850 aNewPasswordId, aForce, llMedia);
16851 rc = pTask->createThread();
16852 pTask = NULL;
16853 if (FAILED(rc))
16854 return rc;
16855
16856 pProgress.queryInterfaceTo(aProgress.asOutParam());
16857
16858 LogFlowFuncLeave();
16859
16860 return S_OK;
16861#endif
16862}
16863
16864HRESULT Machine::getEncryptionSettings(com::Utf8Str &aCipher,
16865 com::Utf8Str &aPasswordId)
16866{
16867#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16868 RT_NOREF(aCipher, aPasswordId);
16869 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16870#else
16871 AutoLimitedCaller autoCaller(this);
16872 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16873
16874 PCVBOXCRYPTOIF pCryptoIf = NULL;
16875 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16876 if (FAILED(hrc)) return hrc; /* Error is set */
16877
16878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16879
16880 if (mData->mstrKeyStore.isNotEmpty())
16881 {
16882 char *pszCipher = NULL;
16883 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), NULL /*pszPassword*/,
16884 NULL /*ppbKey*/, NULL /*pcbKey*/, &pszCipher);
16885 if (RT_SUCCESS(vrc))
16886 {
16887 aCipher = getCipherStringWithoutMode(pszCipher);
16888 RTStrFree(pszCipher);
16889 aPasswordId = mData->mstrKeyId;
16890 }
16891 else
16892 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
16893 tr("Failed to query the encryption settings with %Rrc"),
16894 vrc);
16895 }
16896 else
16897 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16898
16899 mParent->i_releaseCryptoIf(pCryptoIf);
16900
16901 return hrc;
16902#endif
16903}
16904
16905HRESULT Machine::checkEncryptionPassword(const com::Utf8Str &aPassword)
16906{
16907#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16908 RT_NOREF(aPassword);
16909 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16910#else
16911 AutoLimitedCaller autoCaller(this);
16912 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16913
16914 PCVBOXCRYPTOIF pCryptoIf = NULL;
16915 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16916 if (FAILED(hrc)) return hrc; /* Error is set */
16917
16918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16919
16920 if (mData->mstrKeyStore.isNotEmpty())
16921 {
16922 char *pszCipher = NULL;
16923 uint8_t *pbDek = NULL;
16924 size_t cbDek = 0;
16925 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), aPassword.c_str(),
16926 &pbDek, &cbDek, &pszCipher);
16927 if (RT_SUCCESS(vrc))
16928 {
16929 RTStrFree(pszCipher);
16930 RTMemSaferFree(pbDek, cbDek);
16931 }
16932 else
16933 hrc = setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc,
16934 tr("The password supplied for the encrypted machine is incorrect"));
16935 }
16936 else
16937 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16938
16939 mParent->i_releaseCryptoIf(pCryptoIf);
16940
16941 return hrc;
16942#endif
16943}
16944
16945HRESULT Machine::addEncryptionPassword(const com::Utf8Str &aId,
16946 const com::Utf8Str &aPassword)
16947{
16948#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16949 RT_NOREF(aId, aPassword);
16950 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16951#else
16952 AutoLimitedCaller autoCaller(this);
16953 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16954
16955 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16956
16957 size_t cbPassword = aPassword.length() + 1;
16958 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
16959
16960 mData->mpKeyStore->addSecretKey(aId, pbPassword, cbPassword);
16961
16962 if ( mData->mAccessible
16963 && mData->mSession.mState == SessionState_Locked
16964 && mData->mSession.mLockType == LockType_VM
16965 && mData->mSession.mDirectControl != NULL)
16966 {
16967 /* get the console from the direct session */
16968 ComPtr<IConsole> console;
16969 HRESULT rc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16970 ComAssertComRC(rc);
16971 /* send passsword to console */
16972 console->AddEncryptionPassword(Bstr(aId).raw(),
16973 Bstr(aPassword).raw(),
16974 TRUE);
16975 }
16976
16977 if (mData->mstrKeyId == aId)
16978 {
16979 HRESULT hrc = checkEncryptionPassword(aPassword);
16980 if (FAILED(hrc))
16981 return hrc;
16982
16983 if (SUCCEEDED(hrc))
16984 {
16985 /*
16986 * Encryption is used and password is correct,
16987 * Reinit the machine if required.
16988 */
16989 BOOL fAccessible;
16990 alock.release();
16991 getAccessible(&fAccessible);
16992 alock.acquire();
16993 }
16994 }
16995
16996 /*
16997 * Add the password into the NvramStore only after
16998 * the machine becomes accessible and the NvramStore
16999 * contains key id and key store.
17000 */
17001 if (mNvramStore.isNotNull())
17002 mNvramStore->i_addPassword(aId, aPassword);
17003
17004 return S_OK;
17005#endif
17006}
17007
17008HRESULT Machine::addEncryptionPasswords(const std::vector<com::Utf8Str> &aIds,
17009 const std::vector<com::Utf8Str> &aPasswords)
17010{
17011#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
17012 RT_NOREF(aIds, aPasswords);
17013 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
17014#else
17015 if (aIds.size() != aPasswords.size())
17016 return setError(E_INVALIDARG, tr("Id and passwords arrays must have the same size"));
17017
17018 HRESULT hrc = S_OK;
17019 for (size_t i = 0; i < aIds.size() && SUCCEEDED(hrc); ++i)
17020 hrc = addEncryptionPassword(aIds[i], aPasswords[i]);
17021
17022 return hrc;
17023#endif
17024}
17025
17026HRESULT Machine::removeEncryptionPassword(AutoCaller &autoCaller, const com::Utf8Str &aId)
17027{
17028#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
17029 RT_NOREF(autoCaller, aId);
17030 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
17031#else
17032 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
17033
17034 if ( mData->mAccessible
17035 && mData->mSession.mState == SessionState_Locked
17036 && mData->mSession.mLockType == LockType_VM
17037 && mData->mSession.mDirectControl != NULL)
17038 {
17039 /* get the console from the direct session */
17040 ComPtr<IConsole> console;
17041 HRESULT rc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
17042 ComAssertComRC(rc);
17043 /* send passsword to console */
17044 console->RemoveEncryptionPassword(Bstr(aId).raw());
17045 }
17046
17047 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty() && mData->mstrKeyId == aId)
17048 {
17049 if (Global::IsOnlineOrTransient(mData->mMachineState))
17050 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
17051 alock.release();
17052 autoCaller.release();
17053 /* return because all passwords are purged when machine becomes inaccessible; */
17054 return i_setInaccessible();
17055 }
17056
17057 if (mNvramStore.isNotNull())
17058 mNvramStore->i_removePassword(aId);
17059 mData->mpKeyStore->deleteSecretKey(aId);
17060 return S_OK;
17061#endif
17062}
17063
17064HRESULT Machine::clearAllEncryptionPasswords(AutoCaller &autoCaller)
17065{
17066#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
17067 RT_NOREF(autoCaller);
17068 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
17069#else
17070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
17071
17072 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty())
17073 {
17074 if (Global::IsOnlineOrTransient(mData->mMachineState))
17075 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
17076 alock.release();
17077 autoCaller.release();
17078 /* return because all passwords are purged when machine becomes inaccessible; */
17079 return i_setInaccessible();
17080 }
17081
17082 mNvramStore->i_removeAllPasswords();
17083 mData->mpKeyStore->deleteAllSecretKeys(false, true);
17084 return S_OK;
17085#endif
17086}
17087
17088#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
17089HRESULT Machine::i_setInaccessible()
17090{
17091 if (!mData->mAccessible)
17092 return S_OK;
17093
17094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
17095 VirtualBox *pParent = mParent;
17096 com::Utf8Str strConfigFile = mData->m_strConfigFile;
17097 Guid id(i_getId());
17098
17099 alock.release();
17100
17101 uninit();
17102 HRESULT rc = initFromSettings(pParent, strConfigFile, &id, com::Utf8Str());
17103
17104 alock.acquire();
17105 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
17106 return rc;
17107}
17108#endif
17109
17110/* This isn't handled entirely by the wrapper generator yet. */
17111#ifdef VBOX_WITH_XPCOM
17112NS_DECL_CLASSINFO(SessionMachine)
17113NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
17114
17115NS_DECL_CLASSINFO(SnapshotMachine)
17116NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
17117#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