VirtualBox

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

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

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 599.9 KB
Line 
1/* $Id: MachineImpl.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
29
30/* Make sure all the stdint.h macros are included - must come first! */
31#ifndef __STDC_LIMIT_MACROS
32# define __STDC_LIMIT_MACROS
33#endif
34#ifndef __STDC_CONSTANT_MACROS
35# define __STDC_CONSTANT_MACROS
36#endif
37
38#include "LoggingNew.h"
39#include "VirtualBoxImpl.h"
40#include "MachineImpl.h"
41#include "SnapshotImpl.h"
42#include "ClientToken.h"
43#include "ProgressImpl.h"
44#include "ProgressProxyImpl.h"
45#include "MediumAttachmentImpl.h"
46#include "MediumImpl.h"
47#include "MediumLock.h"
48#include "USBControllerImpl.h"
49#include "USBDeviceFiltersImpl.h"
50#include "HostImpl.h"
51#include "SharedFolderImpl.h"
52#include "GuestOSTypeImpl.h"
53#include "VirtualBoxErrorInfoImpl.h"
54#include "StorageControllerImpl.h"
55#include "DisplayImpl.h"
56#include "DisplayUtils.h"
57#include "MachineImplCloneVM.h"
58#include "AutostartDb.h"
59#include "SystemPropertiesImpl.h"
60#include "MachineImplMoveVM.h"
61#include "ExtPackManagerImpl.h"
62#include "MachineLaunchVMCommonWorker.h"
63#include "CryptoUtils.h"
64
65// generated header
66#include "VBoxEvents.h"
67
68#ifdef VBOX_WITH_USB
69# include "USBProxyService.h"
70#endif
71
72#include "AutoCaller.h"
73#include "HashedPw.h"
74#include "Performance.h"
75#include "StringifyEnums.h"
76
77#include <iprt/asm.h>
78#include <iprt/path.h>
79#include <iprt/dir.h>
80#include <iprt/env.h>
81#include <iprt/lockvalidator.h>
82#include <iprt/memsafer.h>
83#include <iprt/process.h>
84#include <iprt/cpp/utils.h>
85#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
86#include <iprt/sha.h>
87#include <iprt/string.h>
88#include <iprt/ctype.h>
89
90#include <VBox/com/array.h>
91#include <VBox/com/list.h>
92#include <VBox/VBoxCryptoIf.h>
93
94#include <VBox/err.h>
95#include <VBox/param.h>
96#include <VBox/settings.h>
97#include <VBox/VMMDev.h>
98#include <VBox/vmm/ssm.h>
99
100#ifdef VBOX_WITH_GUEST_PROPS
101# include <VBox/HostServices/GuestPropertySvc.h>
102# include <VBox/com/array.h>
103#endif
104
105#ifdef VBOX_WITH_SHARED_CLIPBOARD
106# include <VBox/HostServices/VBoxClipboardSvc.h>
107#endif
108
109#include "VBox/com/MultiResult.h"
110
111#include <algorithm>
112
113#ifdef VBOX_WITH_DTRACE_R3_MAIN
114# include "dtrace/VBoxAPI.h"
115#endif
116
117#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
118# define HOSTSUFF_EXE ".exe"
119#else /* !RT_OS_WINDOWS */
120# define HOSTSUFF_EXE ""
121#endif /* !RT_OS_WINDOWS */
122
123// defines / prototypes
124/////////////////////////////////////////////////////////////////////////////
125
126#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
127# define BUF_DATA_SIZE _64K
128
129enum CipherMode
130{
131 CipherModeGcm = 0,
132 CipherModeCtr,
133 CipherModeXts,
134 CipherModeMax
135};
136
137enum AesSize
138{
139 Aes128 = 0,
140 Aes256,
141 AesMax
142};
143
144const char *g_apszCipher[AesMax][CipherModeMax] =
145{
146 {"AES-GCM128", "AES-CTR128", "AES-XTS128-PLAIN64"},
147 {"AES-GCM256", "AES-CTR256", "AES-XTS256-PLAIN64"}
148};
149const char *g_apszCipherAlgo[AesMax] = {"AES-128", "AES-256"};
150
151static const char *getCipherString(const char *pszAlgo, const int iMode)
152{
153 if (iMode >= CipherModeMax)
154 return pszAlgo;
155
156 for (int i = 0; i < AesMax; i++)
157 {
158 if (strcmp(pszAlgo, g_apszCipherAlgo[i]) == 0)
159 return g_apszCipher[i][iMode];
160 }
161 return pszAlgo;
162}
163
164static const char *getCipherStringWithoutMode(const char *pszAlgo)
165{
166 for (int i = 0; i < AesMax; i++)
167 {
168 for (int j = 0; j < CipherModeMax; j++)
169 {
170 if (strcmp(pszAlgo, g_apszCipher[i][j]) == 0)
171 return g_apszCipherAlgo[i];
172 }
173 }
174 return pszAlgo;
175}
176#endif
177
178/////////////////////////////////////////////////////////////////////////////
179// Machine::Data structure
180/////////////////////////////////////////////////////////////////////////////
181
182Machine::Data::Data()
183{
184 mRegistered = FALSE;
185 pMachineConfigFile = NULL;
186 /* Contains hints on what has changed when the user is using the VM (config
187 * changes, running the VM, ...). This is used to decide if a config needs
188 * to be written to disk. */
189 flModifications = 0;
190 /* VM modification usually also trigger setting the current state to
191 * "Modified". Although this is not always the case. An e.g. is the VM
192 * initialization phase or when snapshot related data is changed. The
193 * actually behavior is controlled by the following flag. */
194 m_fAllowStateModification = false;
195 mAccessible = FALSE;
196 /* mUuid is initialized in Machine::init() */
197
198 mMachineState = MachineState_PoweredOff;
199 RTTimeNow(&mLastStateChange);
200
201 mMachineStateDeps = 0;
202 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
203 mMachineStateChangePending = 0;
204
205 mCurrentStateModified = TRUE;
206 mGuestPropertiesModified = FALSE;
207
208 mSession.mPID = NIL_RTPROCESS;
209 mSession.mLockType = LockType_Null;
210 mSession.mState = SessionState_Unlocked;
211
212#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
213 mpKeyStore = NULL;
214#endif
215}
216
217Machine::Data::~Data()
218{
219 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
220 {
221 RTSemEventMultiDestroy(mMachineStateDepsSem);
222 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
223 }
224 if (pMachineConfigFile)
225 {
226 delete pMachineConfigFile;
227 pMachineConfigFile = NULL;
228 }
229}
230
231/////////////////////////////////////////////////////////////////////////////
232// Machine::HWData structure
233/////////////////////////////////////////////////////////////////////////////
234
235Machine::HWData::HWData()
236{
237 /* default values for a newly created machine */
238 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
239 mMemorySize = 128;
240 mCPUCount = 1;
241 mCPUHotPlugEnabled = false;
242 mMemoryBalloonSize = 0;
243 mPageFusionEnabled = false;
244 mHWVirtExEnabled = true;
245 mHWVirtExNestedPagingEnabled = true;
246 mHWVirtExLargePagesEnabled = HC_ARCH_BITS == 64; /* Not supported on 32 bits hosts. */
247 mHWVirtExVPIDEnabled = true;
248 mHWVirtExUXEnabled = true;
249 mHWVirtExForceEnabled = false;
250 mHWVirtExUseNativeApi = false;
251 mHWVirtExVirtVmsaveVmload = true;
252#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
253 mPAEEnabled = true;
254#else
255 mPAEEnabled = false;
256#endif
257 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
258 mTripleFaultReset = false;
259 mAPIC = true;
260 mX2APIC = false;
261 mIBPBOnVMExit = false;
262 mIBPBOnVMEntry = false;
263 mSpecCtrl = false;
264 mSpecCtrlByHost = false;
265 mL1DFlushOnSched = true;
266 mL1DFlushOnVMEntry = false;
267 mMDSClearOnSched = true;
268 mMDSClearOnVMEntry = false;
269 mNestedHWVirt = false;
270 mHPETEnabled = false;
271 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
272 mCpuIdPortabilityLevel = 0;
273 mCpuProfile = "host";
274
275 /* default boot order: floppy - DVD - HDD */
276 mBootOrder[0] = DeviceType_Floppy;
277 mBootOrder[1] = DeviceType_DVD;
278 mBootOrder[2] = DeviceType_HardDisk;
279 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
280 mBootOrder[i] = DeviceType_Null;
281
282 mClipboardMode = ClipboardMode_Disabled;
283 mClipboardFileTransfersEnabled = FALSE;
284
285 mDnDMode = DnDMode_Disabled;
286
287 mFirmwareType = FirmwareType_BIOS;
288 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
289 mPointingHIDType = PointingHIDType_PS2Mouse;
290 mChipsetType = ChipsetType_PIIX3;
291 mIommuType = IommuType_None;
292 mParavirtProvider = ParavirtProvider_Default;
293 mEmulatedUSBCardReaderEnabled = FALSE;
294
295 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
296 mCPUAttached[i] = false;
297
298 mIOCacheEnabled = true;
299 mIOCacheSize = 5; /* 5MB */
300}
301
302Machine::HWData::~HWData()
303{
304}
305
306/////////////////////////////////////////////////////////////////////////////
307// Machine class
308/////////////////////////////////////////////////////////////////////////////
309
310// constructor / destructor
311/////////////////////////////////////////////////////////////////////////////
312
313Machine::Machine() :
314#ifdef VBOX_WITH_RESOURCE_USAGE_API
315 mCollectorGuest(NULL),
316#endif
317 mPeer(NULL),
318 mParent(NULL),
319 mSerialPorts(),
320 mParallelPorts(),
321 uRegistryNeedsSaving(0)
322{}
323
324Machine::~Machine()
325{}
326
327HRESULT Machine::FinalConstruct()
328{
329 LogFlowThisFunc(("\n"));
330 return BaseFinalConstruct();
331}
332
333void Machine::FinalRelease()
334{
335 LogFlowThisFunc(("\n"));
336 uninit();
337 BaseFinalRelease();
338}
339
340/**
341 * Initializes a new machine instance; this init() variant creates a new, empty machine.
342 * This gets called from VirtualBox::CreateMachine().
343 *
344 * @param aParent Associated parent object
345 * @param strConfigFile Local file system path to the VM settings file (can
346 * be relative to the VirtualBox config directory).
347 * @param strName name for the machine
348 * @param llGroups list of groups for the machine
349 * @param strOsType OS Type string (stored as is if aOsType is NULL).
350 * @param aOsType OS Type of this machine or NULL.
351 * @param aId UUID for the new machine.
352 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
353 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
354 * scheme (includes the UUID).
355 * @param aCipher The cipher to encrypt the VM with.
356 * @param aPasswordId The password ID, empty if the VM should not be encrypted.
357 * @param aPassword The password to encrypt the VM with.
358 *
359 * @return Success indicator. if not S_OK, the machine object is invalid
360 */
361HRESULT Machine::init(VirtualBox *aParent,
362 const Utf8Str &strConfigFile,
363 const Utf8Str &strName,
364 const StringsList &llGroups,
365 const Utf8Str &strOsType,
366 GuestOSType *aOsType,
367 const Guid &aId,
368 bool fForceOverwrite,
369 bool fDirectoryIncludesUUID,
370 const com::Utf8Str &aCipher,
371 const com::Utf8Str &aPasswordId,
372 const com::Utf8Str &aPassword)
373{
374 LogFlowThisFuncEnter();
375 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
376
377#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
378 RT_NOREF(aCipher);
379 if (aPassword.isNotEmpty() || aPasswordId.isNotEmpty())
380 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
381#endif
382
383 /* Enclose the state transition NotReady->InInit->Ready */
384 AutoInitSpan autoInitSpan(this);
385 AssertReturn(autoInitSpan.isOk(), E_FAIL);
386
387 HRESULT rc = initImpl(aParent, strConfigFile);
388 if (FAILED(rc)) return rc;
389
390#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
391 com::Utf8Str strSsmKeyId;
392 com::Utf8Str strSsmKeyStore;
393 com::Utf8Str strNVRAMKeyId;
394 com::Utf8Str strNVRAMKeyStore;
395
396 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
397 {
398 /* Resolve the cryptographic interface. */
399 PCVBOXCRYPTOIF pCryptoIf = NULL;
400 HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
401 if (SUCCEEDED(hrc))
402 {
403 CipherMode aenmMode[] = {CipherModeGcm, CipherModeGcm, CipherModeGcm, CipherModeCtr};
404 com::Utf8Str *astrKeyId[] = {&mData->mstrKeyId, &strSsmKeyId, &strNVRAMKeyId, &mData->mstrLogKeyId};
405 com::Utf8Str *astrKeyStore[] = {&mData->mstrKeyStore, &strSsmKeyStore, &strNVRAMKeyStore, &mData->mstrLogKeyStore};
406
407 for (uint32_t i = 0; i < RT_ELEMENTS(astrKeyId); i++)
408 {
409 const char *pszCipher = getCipherString(aCipher.c_str(), aenmMode[i]);
410 if (!pszCipher)
411 {
412 hrc = setError(VBOX_E_NOT_SUPPORTED,
413 tr("The cipher '%s' is not supported"), aCipher.c_str());
414 break;
415 }
416
417 VBOXCRYPTOCTX hCryptoCtx;
418 int vrc = pCryptoIf->pfnCryptoCtxCreate(pszCipher, aPassword.c_str(), &hCryptoCtx);
419 if (RT_FAILURE(vrc))
420 {
421 hrc = setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
422 break;
423 }
424
425 char *pszKeyStore;
426 vrc = pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
427 int vrc2 = pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
428 AssertRC(vrc2);
429
430 if (RT_FAILURE(vrc))
431 {
432 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
433 break;
434 }
435
436 *(astrKeyStore[i]) = pszKeyStore;
437 RTMemFree(pszKeyStore);
438 *(astrKeyId[i]) = aPasswordId;
439 }
440
441 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
442 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
443
444 if (FAILED(hrc))
445 return hrc; /* Error is set. */
446 }
447 else
448 return hrc; /* Error is set. */
449 }
450#endif
451
452 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
453 if (FAILED(rc)) return rc;
454
455 if (SUCCEEDED(rc))
456 {
457 // create an empty machine config
458 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
459
460 rc = initDataAndChildObjects();
461 }
462
463 if (SUCCEEDED(rc))
464 {
465#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
466 mSSData->strStateKeyId = strSsmKeyId;
467 mSSData->strStateKeyStore = strSsmKeyStore;
468#endif
469
470 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
471 mData->mAccessible = TRUE;
472
473 unconst(mData->mUuid) = aId;
474
475 mUserData->s.strName = strName;
476
477 if (llGroups.size())
478 mUserData->s.llGroups = llGroups;
479
480 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
481 // the "name sync" flag determines whether the machine directory gets renamed along
482 // with the machine file; say so if the settings file name is the same as the
483 // settings file parent directory (machine directory)
484 mUserData->s.fNameSync = i_isInOwnDir();
485
486 // initialize the default snapshots folder
487 rc = COMSETTER(SnapshotFolder)(NULL);
488 AssertComRC(rc);
489
490 if (aOsType)
491 {
492 /* Store OS type */
493 mUserData->s.strOsType = aOsType->i_id();
494
495 /* Let the OS type select 64-bit ness. */
496 mHWData->mLongMode = aOsType->i_is64Bit()
497 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
498
499 /* Let the OS type enable the X2APIC */
500 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
501
502 rc = aOsType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
503 AssertComRC(rc);
504 }
505 else if (!strOsType.isEmpty())
506 {
507 /* Store OS type */
508 mUserData->s.strOsType = strOsType;
509
510 /* No guest OS type object. Pick some plausible defaults which the
511 * host can handle. There's no way to know or validate anything. */
512 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
513 mHWData->mX2APIC = false;
514 }
515
516 /* Apply BIOS defaults. */
517 mBIOSSettings->i_applyDefaults(aOsType);
518
519 /* Apply TPM defaults. */
520 mTrustedPlatformModule->i_applyDefaults(aOsType);
521
522 /* Apply recording defaults. */
523 mRecordingSettings->i_applyDefaults();
524
525 /* Apply network adapters defaults */
526 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
527 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
528
529 /* Apply serial port defaults */
530 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
531 mSerialPorts[slot]->i_applyDefaults(aOsType);
532
533 /* Apply parallel port defaults */
534 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
535 mParallelPorts[slot]->i_applyDefaults();
536
537 /* Enable the VMMDev testing feature for bootsector VMs: */
538 if (aOsType && aOsType->i_id() == "VBoxBS_64")
539 mData->pMachineConfigFile->mapExtraDataItems["VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled"] = "1";
540
541#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
542 rc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
543#endif
544 if (SUCCEEDED(rc))
545 {
546 /* At this point the changing of the current state modification
547 * flag is allowed. */
548 i_allowStateModification();
549
550 /* commit all changes made during the initialization */
551 i_commit();
552 }
553 }
554
555 /* Confirm a successful initialization when it's the case */
556 if (SUCCEEDED(rc))
557 {
558#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
559 if (aPassword.isNotEmpty() && aPasswordId.isNotEmpty())
560 {
561 size_t cbPassword = aPassword.length() + 1;
562 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
563 mData->mpKeyStore->addSecretKey(aPasswordId, pbPassword, cbPassword);
564 }
565#endif
566
567 if (mData->mAccessible)
568 autoInitSpan.setSucceeded();
569 else
570 autoInitSpan.setLimited();
571 }
572
573 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
574 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
575 mData->mRegistered,
576 mData->mAccessible,
577 rc));
578
579 LogFlowThisFuncLeave();
580
581 return rc;
582}
583
584/**
585 * Initializes a new instance with data from machine XML (formerly Init_Registered).
586 * Gets called in two modes:
587 *
588 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
589 * UUID is specified and we mark the machine as "registered";
590 *
591 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
592 * and the machine remains unregistered until RegisterMachine() is called.
593 *
594 * @param aParent Associated parent object
595 * @param strConfigFile Local file system path to the VM settings file (can
596 * be relative to the VirtualBox config directory).
597 * @param aId UUID of the machine or NULL (see above).
598 * @param strPassword Password for decrypting the config
599 *
600 * @return Success indicator. if not S_OK, the machine object is invalid
601 */
602HRESULT Machine::initFromSettings(VirtualBox *aParent,
603 const Utf8Str &strConfigFile,
604 const Guid *aId,
605 const com::Utf8Str &strPassword)
606{
607 LogFlowThisFuncEnter();
608 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
609
610 PCVBOXCRYPTOIF pCryptoIf = NULL;
611#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
612 if (strPassword.isNotEmpty())
613 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
614#else
615 if (strPassword.isNotEmpty())
616 {
617 /* Get at the crpytographic interface. */
618 HRESULT hrc = aParent->i_retainCryptoIf(&pCryptoIf);
619 if (FAILED(hrc))
620 return hrc; /* Error is set. */
621 }
622#endif
623
624 /* Enclose the state transition NotReady->InInit->Ready */
625 AutoInitSpan autoInitSpan(this);
626 AssertReturn(autoInitSpan.isOk(), E_FAIL);
627
628 HRESULT rc = initImpl(aParent, strConfigFile);
629 if (FAILED(rc)) return rc;
630
631 if (aId)
632 {
633 // loading a registered VM:
634 unconst(mData->mUuid) = *aId;
635 mData->mRegistered = TRUE;
636 // now load the settings from XML:
637 rc = i_registeredInit();
638 // this calls initDataAndChildObjects() and loadSettings()
639 }
640 else
641 {
642 // opening an unregistered VM (VirtualBox::OpenMachine()):
643 rc = initDataAndChildObjects();
644
645 if (SUCCEEDED(rc))
646 {
647 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
648 mData->mAccessible = TRUE;
649
650 try
651 {
652 // load and parse machine XML; this will throw on XML or logic errors
653 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
654 pCryptoIf,
655 strPassword.c_str());
656
657 // reject VM UUID duplicates, they can happen if someone
658 // tries to register an already known VM config again
659 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
660 true /* fPermitInaccessible */,
661 false /* aDoSetError */,
662 NULL) != VBOX_E_OBJECT_NOT_FOUND)
663 {
664 throw setError(E_FAIL,
665 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
666 mData->m_strConfigFile.c_str());
667 }
668
669 // use UUID from machine config
670 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
671
672#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
673 // No exception is thrown if config is encrypted, allowing us to get the uuid and the encryption fields.
674 // We fill in the encryptions fields, and the rest will be filled in if all data parsed.
675 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
676 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
677#endif
678
679 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
680 {
681 // We just set the inaccessible state and fill the error info allowing the caller
682 // to register the machine with encrypted config even if the password is incorrect
683 mData->mAccessible = FALSE;
684
685 /* fetch the current error info */
686 mData->mAccessError = com::ErrorInfo();
687
688 setError(VBOX_E_PASSWORD_INCORRECT,
689 tr("Decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
690 mData->pMachineConfigFile->uuid.raw());
691 }
692 else
693 {
694#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
695 if (strPassword.isNotEmpty())
696 {
697 size_t cbKey = strPassword.length() + 1; /* Include terminator */
698 const uint8_t *pbKey = (const uint8_t *)strPassword.c_str();
699 mData->mpKeyStore->addSecretKey(mData->mstrKeyId, pbKey, cbKey);
700 }
701#endif
702
703 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
704 NULL /* puuidRegistry */);
705 if (FAILED(rc)) throw rc;
706
707 /* At this point the changing of the current state modification
708 * flag is allowed. */
709 i_allowStateModification();
710
711 i_commit();
712 }
713 }
714 catch (HRESULT err)
715 {
716 /* we assume that error info is set by the thrower */
717 rc = err;
718 }
719 catch (...)
720 {
721 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
722 }
723 }
724 }
725
726 /* Confirm a successful initialization when it's the case */
727 if (SUCCEEDED(rc))
728 {
729 if (mData->mAccessible)
730 autoInitSpan.setSucceeded();
731 else
732 {
733 autoInitSpan.setLimited();
734
735 // uninit media from this machine's media registry, or else
736 // reloading the settings will fail
737 mParent->i_unregisterMachineMedia(i_getId());
738 }
739 }
740
741#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
742 if (pCryptoIf)
743 {
744 HRESULT hrc2 = aParent->i_releaseCryptoIf(pCryptoIf);
745 Assert(hrc2 == S_OK); RT_NOREF(hrc2);
746 }
747#endif
748
749 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
750 "rc=%08X\n",
751 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
752 mData->mRegistered, mData->mAccessible, rc));
753
754 LogFlowThisFuncLeave();
755
756 return rc;
757}
758
759/**
760 * Initializes a new instance from a machine config that is already in memory
761 * (import OVF case). Since we are importing, the UUID in the machine
762 * config is ignored and we always generate a fresh one.
763 *
764 * @param aParent Associated parent object.
765 * @param strName Name for the new machine; this overrides what is specified in config.
766 * @param strSettingsFilename File name of .vbox file.
767 * @param config Machine configuration loaded and parsed from XML.
768 *
769 * @return Success indicator. if not S_OK, the machine object is invalid
770 */
771HRESULT Machine::init(VirtualBox *aParent,
772 const Utf8Str &strName,
773 const Utf8Str &strSettingsFilename,
774 const settings::MachineConfigFile &config)
775{
776 LogFlowThisFuncEnter();
777
778 /* Enclose the state transition NotReady->InInit->Ready */
779 AutoInitSpan autoInitSpan(this);
780 AssertReturn(autoInitSpan.isOk(), E_FAIL);
781
782 HRESULT rc = initImpl(aParent, strSettingsFilename);
783 if (FAILED(rc)) return rc;
784
785 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
786 if (FAILED(rc)) return rc;
787
788 rc = initDataAndChildObjects();
789
790 if (SUCCEEDED(rc))
791 {
792 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
793 mData->mAccessible = TRUE;
794
795 // create empty machine config for instance data
796 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
797
798 // generate fresh UUID, ignore machine config
799 unconst(mData->mUuid).create();
800
801 rc = i_loadMachineDataFromSettings(config,
802 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
803
804 // override VM name as well, it may be different
805 mUserData->s.strName = strName;
806
807 if (SUCCEEDED(rc))
808 {
809 /* At this point the changing of the current state modification
810 * flag is allowed. */
811 i_allowStateModification();
812
813 /* commit all changes made during the initialization */
814 i_commit();
815 }
816 }
817
818 /* Confirm a successful initialization when it's the case */
819 if (SUCCEEDED(rc))
820 {
821 if (mData->mAccessible)
822 autoInitSpan.setSucceeded();
823 else
824 {
825 /* Ignore all errors from unregistering, they would destroy
826- * the more interesting error information we already have,
827- * pinpointing the issue with the VM config. */
828 ErrorInfoKeeper eik;
829
830 autoInitSpan.setLimited();
831
832 // uninit media from this machine's media registry, or else
833 // reloading the settings will fail
834 mParent->i_unregisterMachineMedia(i_getId());
835 }
836 }
837
838 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
839 "rc=%08X\n",
840 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
841 mData->mRegistered, mData->mAccessible, rc));
842
843 LogFlowThisFuncLeave();
844
845 return rc;
846}
847
848/**
849 * Shared code between the various init() implementations.
850 * @param aParent The VirtualBox object.
851 * @param strConfigFile Settings file.
852 * @return
853 */
854HRESULT Machine::initImpl(VirtualBox *aParent,
855 const Utf8Str &strConfigFile)
856{
857 LogFlowThisFuncEnter();
858
859 AssertReturn(aParent, E_INVALIDARG);
860 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
861
862 HRESULT rc = S_OK;
863
864 /* share the parent weakly */
865 unconst(mParent) = aParent;
866
867 /* allocate the essential machine data structure (the rest will be
868 * allocated later by initDataAndChildObjects() */
869 mData.allocate();
870
871 /* memorize the config file name (as provided) */
872 mData->m_strConfigFile = strConfigFile;
873
874 /* get the full file name */
875 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
876 if (RT_FAILURE(vrc1))
877 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
878 tr("Invalid machine settings file name '%s' (%Rrc)"),
879 strConfigFile.c_str(),
880 vrc1);
881
882#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
883 /** @todo Only create when the machine is going to be encrypted. */
884 /* Non-pageable memory is not accessible for non-VM process */
885 mData->mpKeyStore = new SecretKeyStore(false /* fKeyBufNonPageable */);
886 AssertReturn(mData->mpKeyStore, E_OUTOFMEMORY);
887#endif
888
889 LogFlowThisFuncLeave();
890
891 return rc;
892}
893
894/**
895 * Tries to create a machine settings file in the path stored in the machine
896 * instance data. Used when a new machine is created to fail gracefully if
897 * the settings file could not be written (e.g. because machine dir is read-only).
898 * @return
899 */
900HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
901{
902 HRESULT rc = S_OK;
903
904 // when we create a new machine, we must be able to create the settings file
905 RTFILE f = NIL_RTFILE;
906 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
907 if ( RT_SUCCESS(vrc)
908 || vrc == VERR_SHARING_VIOLATION
909 )
910 {
911 if (RT_SUCCESS(vrc))
912 RTFileClose(f);
913 if (!fForceOverwrite)
914 rc = setError(VBOX_E_FILE_ERROR,
915 tr("Machine settings file '%s' already exists"),
916 mData->m_strConfigFileFull.c_str());
917 else
918 {
919 /* try to delete the config file, as otherwise the creation
920 * of a new settings file will fail. */
921 i_deleteFile(mData->m_strConfigFileFull.c_str(), false /* fIgnoreFailures */, tr("existing settings file"));
922 }
923 }
924 else if ( vrc != VERR_FILE_NOT_FOUND
925 && vrc != VERR_PATH_NOT_FOUND
926 )
927 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
928 tr("Invalid machine settings file name '%s' (%Rrc)"),
929 mData->m_strConfigFileFull.c_str(),
930 vrc);
931 return rc;
932}
933
934/**
935 * Initializes the registered machine by loading the settings file.
936 * This method is separated from #init() in order to make it possible to
937 * retry the operation after VirtualBox startup instead of refusing to
938 * startup the whole VirtualBox server in case if the settings file of some
939 * registered VM is invalid or inaccessible.
940 *
941 * @note Must be always called from this object's write lock
942 * (unless called from #init() that doesn't need any locking).
943 * @note Locks the mUSBController method for writing.
944 * @note Subclasses must not call this method.
945 */
946HRESULT Machine::i_registeredInit()
947{
948 AssertReturn(!i_isSessionMachine(), E_FAIL);
949 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
950 AssertReturn(mData->mUuid.isValid(), E_FAIL);
951 AssertReturn(!mData->mAccessible, E_FAIL);
952
953 HRESULT rc = initDataAndChildObjects();
954
955 if (SUCCEEDED(rc))
956 {
957 /* Temporarily reset the registered flag in order to let setters
958 * potentially called from loadSettings() succeed (isMutable() used in
959 * all setters will return FALSE for a Machine instance if mRegistered
960 * is TRUE). */
961 mData->mRegistered = FALSE;
962
963 PCVBOXCRYPTOIF pCryptoIf = NULL;
964 SecretKey *pKey = NULL;
965 const char *pszPassword = NULL;
966#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
967 /* Resolve password and cryptographic support interface if machine is encrypted. */
968 if (mData->mstrKeyId.isNotEmpty())
969 {
970 /* Get at the crpytographic interface. */
971 rc = mParent->i_retainCryptoIf(&pCryptoIf);
972 if (SUCCEEDED(rc))
973 {
974 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
975 if (RT_SUCCESS(vrc))
976 pszPassword = (const char *)pKey->getKeyBuffer();
977 else
978 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Failed to retain key for key ID '%s' with %Rrc"),
979 mData->mstrKeyId.c_str(), vrc);
980 }
981 }
982#else
983 RT_NOREF(pKey);
984#endif
985
986 if (SUCCEEDED(rc))
987 {
988 try
989 {
990 // load and parse machine XML; this will throw on XML or logic errors
991 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull,
992 pCryptoIf, pszPassword);
993
994 if (mData->mUuid != mData->pMachineConfigFile->uuid)
995 throw setError(E_FAIL,
996 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
997 mData->pMachineConfigFile->uuid.raw(),
998 mData->m_strConfigFileFull.c_str(),
999 mData->mUuid.toString().c_str(),
1000 mParent->i_settingsFilePath().c_str());
1001
1002#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1003 // If config is encrypted, no exception is thrown allowing us to get the uuid and the encryption fields.
1004 // We fill in the encryptions fields, and the rest will be filled in if all data parsed
1005 mData->mstrKeyId = mData->pMachineConfigFile->strKeyId;
1006 mData->mstrKeyStore = mData->pMachineConfigFile->strKeyStore;
1007
1008 if (mData->pMachineConfigFile->enmParseState == settings::MachineConfigFile::ParseState_PasswordError)
1009 rc = setError(VBOX_E_PASSWORD_INCORRECT,
1010 tr("Config decryption of the machine {%RTuuid} failed. Incorrect or unknown password"),
1011 mData->pMachineConfigFile->uuid.raw());
1012 else
1013#endif
1014 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
1015 NULL /* const Guid *puuidRegistry */);
1016 if (FAILED(rc)) throw rc;
1017 }
1018 catch (HRESULT err)
1019 {
1020 /* we assume that error info is set by the thrower */
1021 rc = err;
1022 }
1023 catch (...)
1024 {
1025 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
1026 }
1027
1028 /* Restore the registered flag (even on failure) */
1029 mData->mRegistered = TRUE;
1030 }
1031
1032#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1033 if (pCryptoIf)
1034 mParent->i_releaseCryptoIf(pCryptoIf);
1035 if (pKey)
1036 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
1037#endif
1038 }
1039
1040 if (SUCCEEDED(rc))
1041 {
1042 /* Set mAccessible to TRUE only if we successfully locked and loaded
1043 * the settings file */
1044 mData->mAccessible = TRUE;
1045
1046 /* commit all changes made during loading the settings file */
1047 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
1048 /// @todo r=klaus for some reason the settings loading logic backs up
1049 // the settings, and therefore a commit is needed. Should probably be changed.
1050 }
1051 else
1052 {
1053 /* If the machine is registered, then, instead of returning a
1054 * failure, we mark it as inaccessible and set the result to
1055 * success to give it a try later */
1056
1057 /* fetch the current error info */
1058 mData->mAccessError = com::ErrorInfo();
1059 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
1060
1061 /* rollback all changes */
1062 i_rollback(false /* aNotify */);
1063
1064 // uninit media from this machine's media registry, or else
1065 // reloading the settings will fail
1066 mParent->i_unregisterMachineMedia(i_getId());
1067
1068 /* uninitialize the common part to make sure all data is reset to
1069 * default (null) values */
1070 uninitDataAndChildObjects();
1071
1072 rc = S_OK;
1073 }
1074
1075 return rc;
1076}
1077
1078/**
1079 * Uninitializes the instance.
1080 * Called either from FinalRelease() or by the parent when it gets destroyed.
1081 *
1082 * @note The caller of this method must make sure that this object
1083 * a) doesn't have active callers on the current thread and b) is not locked
1084 * by the current thread; otherwise uninit() will hang either a) due to
1085 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
1086 * a dead-lock caused by this thread waiting for all callers on the other
1087 * threads are done but preventing them from doing so by holding a lock.
1088 */
1089void Machine::uninit()
1090{
1091 LogFlowThisFuncEnter();
1092
1093 Assert(!isWriteLockOnCurrentThread());
1094
1095 Assert(!uRegistryNeedsSaving);
1096 if (uRegistryNeedsSaving)
1097 {
1098 AutoCaller autoCaller(this);
1099 if (SUCCEEDED(autoCaller.rc()))
1100 {
1101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1102 i_saveSettings(NULL, alock, Machine::SaveS_Force);
1103 }
1104 }
1105
1106 /* Enclose the state transition Ready->InUninit->NotReady */
1107 AutoUninitSpan autoUninitSpan(this);
1108 if (autoUninitSpan.uninitDone())
1109 return;
1110
1111 Assert(!i_isSnapshotMachine());
1112 Assert(!i_isSessionMachine());
1113 Assert(!!mData);
1114
1115 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
1116 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
1117
1118 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1119
1120 if (!mData->mSession.mMachine.isNull())
1121 {
1122 /* Theoretically, this can only happen if the VirtualBox server has been
1123 * terminated while there were clients running that owned open direct
1124 * sessions. Since in this case we are definitely called by
1125 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
1126 * won't happen on the client watcher thread (because it has a
1127 * VirtualBox caller for the duration of the
1128 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
1129 * cannot happen until the VirtualBox caller is released). This is
1130 * important, because SessionMachine::uninit() cannot correctly operate
1131 * after we return from this method (it expects the Machine instance is
1132 * still valid). We'll call it ourselves below.
1133 */
1134 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
1135 (SessionMachine*)mData->mSession.mMachine));
1136
1137 if (Global::IsOnlineOrTransient(mData->mMachineState))
1138 {
1139 Log1WarningThisFunc(("Setting state to Aborted!\n"));
1140 /* set machine state using SessionMachine reimplementation */
1141 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
1142 }
1143
1144 /*
1145 * Uninitialize SessionMachine using public uninit() to indicate
1146 * an unexpected uninitialization.
1147 */
1148 mData->mSession.mMachine->uninit();
1149 /* SessionMachine::uninit() must set mSession.mMachine to null */
1150 Assert(mData->mSession.mMachine.isNull());
1151 }
1152
1153 // uninit media from this machine's media registry, if they're still there
1154 Guid uuidMachine(i_getId());
1155
1156 /* the lock is no more necessary (SessionMachine is uninitialized) */
1157 alock.release();
1158
1159 /* XXX This will fail with
1160 * "cannot be closed because it is still attached to 1 virtual machines"
1161 * because at this point we did not call uninitDataAndChildObjects() yet
1162 * and therefore also removeBackReference() for all these mediums was not called! */
1163
1164 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
1165 mParent->i_unregisterMachineMedia(uuidMachine);
1166
1167 // has machine been modified?
1168 if (mData->flModifications)
1169 {
1170 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
1171 i_rollback(false /* aNotify */);
1172 }
1173
1174 if (mData->mAccessible)
1175 uninitDataAndChildObjects();
1176
1177#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
1178 if (mData->mpKeyStore != NULL)
1179 delete mData->mpKeyStore;
1180#endif
1181
1182 /* free the essential data structure last */
1183 mData.free();
1184
1185 LogFlowThisFuncLeave();
1186}
1187
1188// Wrapped IMachine properties
1189/////////////////////////////////////////////////////////////////////////////
1190HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
1191{
1192 /* mParent is constant during life time, no need to lock */
1193 ComObjPtr<VirtualBox> pVirtualBox(mParent);
1194 aParent = pVirtualBox;
1195
1196 return S_OK;
1197}
1198
1199
1200HRESULT Machine::getAccessible(BOOL *aAccessible)
1201{
1202 /* In some cases (medium registry related), it is necessary to be able to
1203 * go through the list of all machines. Happens when an inaccessible VM
1204 * has a sensible medium registry. */
1205 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
1206 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1207
1208 HRESULT rc = S_OK;
1209
1210 if (!mData->mAccessible)
1211 {
1212 /* try to initialize the VM once more if not accessible */
1213
1214 AutoReinitSpan autoReinitSpan(this);
1215 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
1216
1217#ifdef DEBUG
1218 LogFlowThisFunc(("Dumping media backreferences\n"));
1219 mParent->i_dumpAllBackRefs();
1220#endif
1221
1222 if (mData->pMachineConfigFile)
1223 {
1224 // reset the XML file to force loadSettings() (called from i_registeredInit())
1225 // to parse it again; the file might have changed
1226 delete mData->pMachineConfigFile;
1227 mData->pMachineConfigFile = NULL;
1228 }
1229
1230 rc = i_registeredInit();
1231
1232 if (SUCCEEDED(rc) && mData->mAccessible)
1233 {
1234 autoReinitSpan.setSucceeded();
1235
1236 /* make sure interesting parties will notice the accessibility
1237 * state change */
1238 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
1239 mParent->i_onMachineDataChanged(mData->mUuid);
1240 }
1241 }
1242
1243 if (SUCCEEDED(rc))
1244 *aAccessible = mData->mAccessible;
1245
1246 LogFlowThisFuncLeave();
1247
1248 return rc;
1249}
1250
1251HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
1252{
1253 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1254
1255 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
1256 {
1257 /* return shortly */
1258 aAccessError = NULL;
1259 return S_OK;
1260 }
1261
1262 HRESULT rc = S_OK;
1263
1264 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1265 rc = errorInfo.createObject();
1266 if (SUCCEEDED(rc))
1267 {
1268 errorInfo->init(mData->mAccessError.getResultCode(),
1269 mData->mAccessError.getInterfaceID().ref(),
1270 Utf8Str(mData->mAccessError.getComponent()).c_str(),
1271 Utf8Str(mData->mAccessError.getText()));
1272 aAccessError = errorInfo;
1273 }
1274
1275 return rc;
1276}
1277
1278HRESULT Machine::getName(com::Utf8Str &aName)
1279{
1280 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1281
1282 aName = mUserData->s.strName;
1283
1284 return S_OK;
1285}
1286
1287HRESULT Machine::setName(const com::Utf8Str &aName)
1288{
1289 // prohibit setting a UUID only as the machine name, or else it can
1290 // never be found by findMachine()
1291 Guid test(aName);
1292
1293 if (test.isValid())
1294 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1295
1296 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1297
1298 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1299 if (FAILED(rc)) return rc;
1300
1301 i_setModified(IsModified_MachineData);
1302 mUserData.backup();
1303 mUserData->s.strName = aName;
1304
1305 return S_OK;
1306}
1307
1308HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1309{
1310 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1311
1312 aDescription = mUserData->s.strDescription;
1313
1314 return S_OK;
1315}
1316
1317HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1318{
1319 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1320
1321 // this can be done in principle in any state as it doesn't affect the VM
1322 // significantly, but play safe by not messing around while complex
1323 // activities are going on
1324 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1325 if (FAILED(rc)) return rc;
1326
1327 i_setModified(IsModified_MachineData);
1328 mUserData.backup();
1329 mUserData->s.strDescription = aDescription;
1330
1331 return S_OK;
1332}
1333
1334HRESULT Machine::getId(com::Guid &aId)
1335{
1336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1337
1338 aId = mData->mUuid;
1339
1340 return S_OK;
1341}
1342
1343HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1344{
1345 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1346 aGroups.resize(mUserData->s.llGroups.size());
1347 size_t i = 0;
1348 for (StringsList::const_iterator
1349 it = mUserData->s.llGroups.begin();
1350 it != mUserData->s.llGroups.end();
1351 ++it, ++i)
1352 aGroups[i] = (*it);
1353
1354 return S_OK;
1355}
1356
1357HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1358{
1359 StringsList llGroups;
1360 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1361 if (FAILED(rc))
1362 return rc;
1363
1364 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1365
1366 rc = i_checkStateDependency(MutableOrSavedStateDep);
1367 if (FAILED(rc)) return rc;
1368
1369 i_setModified(IsModified_MachineData);
1370 mUserData.backup();
1371 mUserData->s.llGroups = llGroups;
1372
1373 return S_OK;
1374}
1375
1376HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1377{
1378 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1379
1380 aOSTypeId = mUserData->s.strOsType;
1381
1382 return S_OK;
1383}
1384
1385HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1386{
1387 /* look up the object by Id to check it is valid */
1388 ComObjPtr<GuestOSType> pGuestOSType;
1389 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1390
1391 /* when setting, always use the "etalon" value for consistency -- lookup
1392 * by ID is case-insensitive and the input value may have different case */
1393 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1394
1395 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1396
1397 HRESULT rc = i_checkStateDependency(MutableStateDep);
1398 if (FAILED(rc)) return rc;
1399
1400 i_setModified(IsModified_MachineData);
1401 mUserData.backup();
1402 mUserData->s.strOsType = osTypeId;
1403
1404 return S_OK;
1405}
1406
1407HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1408{
1409 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1410
1411 *aFirmwareType = mHWData->mFirmwareType;
1412
1413 return S_OK;
1414}
1415
1416HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1417{
1418 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1419
1420 HRESULT rc = i_checkStateDependency(MutableStateDep);
1421 if (FAILED(rc)) return rc;
1422
1423 i_setModified(IsModified_MachineData);
1424 mHWData.backup();
1425 mHWData->mFirmwareType = aFirmwareType;
1426 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1427 alock.release();
1428
1429 mNvramStore->i_updateNonVolatileStorageFile(strNVRAM);
1430
1431 return S_OK;
1432}
1433
1434HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1435{
1436 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1437
1438 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1439
1440 return S_OK;
1441}
1442
1443HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1444{
1445 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1446
1447 HRESULT rc = i_checkStateDependency(MutableStateDep);
1448 if (FAILED(rc)) return rc;
1449
1450 i_setModified(IsModified_MachineData);
1451 mHWData.backup();
1452 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1453
1454 return S_OK;
1455}
1456
1457HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1458{
1459 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1460
1461 *aPointingHIDType = mHWData->mPointingHIDType;
1462
1463 return S_OK;
1464}
1465
1466HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1467{
1468 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1469
1470 HRESULT rc = i_checkStateDependency(MutableStateDep);
1471 if (FAILED(rc)) return rc;
1472
1473 i_setModified(IsModified_MachineData);
1474 mHWData.backup();
1475 mHWData->mPointingHIDType = aPointingHIDType;
1476
1477 return S_OK;
1478}
1479
1480HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1481{
1482 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1483
1484 *aChipsetType = mHWData->mChipsetType;
1485
1486 return S_OK;
1487}
1488
1489HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1490{
1491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1492
1493 HRESULT rc = i_checkStateDependency(MutableStateDep);
1494 if (FAILED(rc)) return rc;
1495
1496 if (aChipsetType != mHWData->mChipsetType)
1497 {
1498 i_setModified(IsModified_MachineData);
1499 mHWData.backup();
1500 mHWData->mChipsetType = aChipsetType;
1501
1502 // Resize network adapter array, to be finalized on commit/rollback.
1503 // We must not throw away entries yet, otherwise settings are lost
1504 // without a way to roll back.
1505 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1506 size_t oldCount = mNetworkAdapters.size();
1507 if (newCount > oldCount)
1508 {
1509 mNetworkAdapters.resize(newCount);
1510 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1511 {
1512 unconst(mNetworkAdapters[slot]).createObject();
1513 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1514 }
1515 }
1516 }
1517
1518 return S_OK;
1519}
1520
1521HRESULT Machine::getIommuType(IommuType_T *aIommuType)
1522{
1523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1524
1525 *aIommuType = mHWData->mIommuType;
1526
1527 return S_OK;
1528}
1529
1530HRESULT Machine::setIommuType(IommuType_T aIommuType)
1531{
1532 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1533
1534 HRESULT rc = i_checkStateDependency(MutableStateDep);
1535 if (FAILED(rc)) return rc;
1536
1537 if (aIommuType != mHWData->mIommuType)
1538 {
1539 if (aIommuType == IommuType_Intel)
1540 {
1541#ifndef VBOX_WITH_IOMMU_INTEL
1542 LogRelFunc(("Setting Intel IOMMU when Intel IOMMU support not available!\n"));
1543 return E_UNEXPECTED;
1544#endif
1545 }
1546
1547 i_setModified(IsModified_MachineData);
1548 mHWData.backup();
1549 mHWData->mIommuType = aIommuType;
1550 }
1551
1552 return S_OK;
1553}
1554
1555HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1556{
1557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1558
1559 aParavirtDebug = mHWData->mParavirtDebug;
1560 return S_OK;
1561}
1562
1563HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1564{
1565 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1566
1567 HRESULT rc = i_checkStateDependency(MutableStateDep);
1568 if (FAILED(rc)) return rc;
1569
1570 /** @todo Parse/validate options? */
1571 if (aParavirtDebug != mHWData->mParavirtDebug)
1572 {
1573 i_setModified(IsModified_MachineData);
1574 mHWData.backup();
1575 mHWData->mParavirtDebug = aParavirtDebug;
1576 }
1577
1578 return S_OK;
1579}
1580
1581HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1582{
1583 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1584
1585 *aParavirtProvider = mHWData->mParavirtProvider;
1586
1587 return S_OK;
1588}
1589
1590HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1591{
1592 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1593
1594 HRESULT rc = i_checkStateDependency(MutableStateDep);
1595 if (FAILED(rc)) return rc;
1596
1597 if (aParavirtProvider != mHWData->mParavirtProvider)
1598 {
1599 i_setModified(IsModified_MachineData);
1600 mHWData.backup();
1601 mHWData->mParavirtProvider = aParavirtProvider;
1602 }
1603
1604 return S_OK;
1605}
1606
1607HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1608{
1609 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1610
1611 *aParavirtProvider = mHWData->mParavirtProvider;
1612 switch (mHWData->mParavirtProvider)
1613 {
1614 case ParavirtProvider_None:
1615 case ParavirtProvider_HyperV:
1616 case ParavirtProvider_KVM:
1617 case ParavirtProvider_Minimal:
1618 break;
1619
1620 /* Resolve dynamic provider types to the effective types. */
1621 default:
1622 {
1623 ComObjPtr<GuestOSType> pGuestOSType;
1624 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1625 pGuestOSType);
1626 if (FAILED(hrc2) || pGuestOSType.isNull())
1627 {
1628 *aParavirtProvider = ParavirtProvider_None;
1629 break;
1630 }
1631
1632 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1633 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1634
1635 switch (mHWData->mParavirtProvider)
1636 {
1637 case ParavirtProvider_Legacy:
1638 {
1639 if (fOsXGuest)
1640 *aParavirtProvider = ParavirtProvider_Minimal;
1641 else
1642 *aParavirtProvider = ParavirtProvider_None;
1643 break;
1644 }
1645
1646 case ParavirtProvider_Default:
1647 {
1648 if (fOsXGuest)
1649 *aParavirtProvider = ParavirtProvider_Minimal;
1650 else if ( mUserData->s.strOsType == "Windows11_64"
1651 || mUserData->s.strOsType == "Windows10"
1652 || mUserData->s.strOsType == "Windows10_64"
1653 || mUserData->s.strOsType == "Windows81"
1654 || mUserData->s.strOsType == "Windows81_64"
1655 || mUserData->s.strOsType == "Windows8"
1656 || mUserData->s.strOsType == "Windows8_64"
1657 || mUserData->s.strOsType == "Windows7"
1658 || mUserData->s.strOsType == "Windows7_64"
1659 || mUserData->s.strOsType == "WindowsVista"
1660 || mUserData->s.strOsType == "WindowsVista_64"
1661 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1662 || mUserData->s.strOsType.startsWith("Windows201"))
1663 && mUserData->s.strOsType.endsWith("_64"))
1664 || mUserData->s.strOsType == "Windows2012"
1665 || mUserData->s.strOsType == "Windows2012_64"
1666 || mUserData->s.strOsType == "Windows2008"
1667 || mUserData->s.strOsType == "Windows2008_64")
1668 {
1669 *aParavirtProvider = ParavirtProvider_HyperV;
1670 }
1671 else if (guestTypeFamilyId == "Linux" &&
1672 mUserData->s.strOsType != "Linux22" && // Linux22 and Linux24{_64} excluded as they're too old
1673 mUserData->s.strOsType != "Linux24" && // to have any KVM paravirtualization support.
1674 mUserData->s.strOsType != "Linux24_64")
1675 {
1676 *aParavirtProvider = ParavirtProvider_KVM;
1677 }
1678 else
1679 *aParavirtProvider = ParavirtProvider_None;
1680 break;
1681 }
1682
1683 default: AssertFailedBreak(); /* Shut up MSC. */
1684 }
1685 break;
1686 }
1687 }
1688
1689 Assert( *aParavirtProvider == ParavirtProvider_None
1690 || *aParavirtProvider == ParavirtProvider_Minimal
1691 || *aParavirtProvider == ParavirtProvider_HyperV
1692 || *aParavirtProvider == ParavirtProvider_KVM);
1693 return S_OK;
1694}
1695
1696HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1697{
1698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1699
1700 aHardwareVersion = mHWData->mHWVersion;
1701
1702 return S_OK;
1703}
1704
1705HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1706{
1707 /* check known version */
1708 Utf8Str hwVersion = aHardwareVersion;
1709 if ( hwVersion.compare("1") != 0
1710 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1711 return setError(E_INVALIDARG,
1712 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1713
1714 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1715
1716 HRESULT rc = i_checkStateDependency(MutableStateDep);
1717 if (FAILED(rc)) return rc;
1718
1719 i_setModified(IsModified_MachineData);
1720 mHWData.backup();
1721 mHWData->mHWVersion = aHardwareVersion;
1722
1723 return S_OK;
1724}
1725
1726HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1727{
1728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1729
1730 if (!mHWData->mHardwareUUID.isZero())
1731 aHardwareUUID = mHWData->mHardwareUUID;
1732 else
1733 aHardwareUUID = mData->mUuid;
1734
1735 return S_OK;
1736}
1737
1738HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1739{
1740 if (!aHardwareUUID.isValid())
1741 return E_INVALIDARG;
1742
1743 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1744
1745 HRESULT rc = i_checkStateDependency(MutableStateDep);
1746 if (FAILED(rc)) return rc;
1747
1748 i_setModified(IsModified_MachineData);
1749 mHWData.backup();
1750 if (aHardwareUUID == mData->mUuid)
1751 mHWData->mHardwareUUID.clear();
1752 else
1753 mHWData->mHardwareUUID = aHardwareUUID;
1754
1755 return S_OK;
1756}
1757
1758HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1759{
1760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1761
1762 *aMemorySize = mHWData->mMemorySize;
1763
1764 return S_OK;
1765}
1766
1767HRESULT Machine::setMemorySize(ULONG aMemorySize)
1768{
1769 /* check RAM limits */
1770 if ( aMemorySize < MM_RAM_MIN_IN_MB
1771 || aMemorySize > MM_RAM_MAX_IN_MB
1772 )
1773 return setError(E_INVALIDARG,
1774 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1775 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1776
1777 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1778
1779 HRESULT rc = i_checkStateDependency(MutableStateDep);
1780 if (FAILED(rc)) return rc;
1781
1782 i_setModified(IsModified_MachineData);
1783 mHWData.backup();
1784 mHWData->mMemorySize = aMemorySize;
1785
1786 return S_OK;
1787}
1788
1789HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1790{
1791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1792
1793 *aCPUCount = mHWData->mCPUCount;
1794
1795 return S_OK;
1796}
1797
1798HRESULT Machine::setCPUCount(ULONG aCPUCount)
1799{
1800 /* check CPU limits */
1801 if ( aCPUCount < SchemaDefs::MinCPUCount
1802 || aCPUCount > SchemaDefs::MaxCPUCount
1803 )
1804 return setError(E_INVALIDARG,
1805 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1806 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1807
1808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1809
1810 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1811 if (mHWData->mCPUHotPlugEnabled)
1812 {
1813 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1814 {
1815 if (mHWData->mCPUAttached[idx])
1816 return setError(E_INVALIDARG,
1817 tr("There is still a CPU attached to socket %lu."
1818 "Detach the CPU before removing the socket"),
1819 aCPUCount, idx+1);
1820 }
1821 }
1822
1823 HRESULT rc = i_checkStateDependency(MutableStateDep);
1824 if (FAILED(rc)) return rc;
1825
1826 i_setModified(IsModified_MachineData);
1827 mHWData.backup();
1828 mHWData->mCPUCount = aCPUCount;
1829
1830 return S_OK;
1831}
1832
1833HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1834{
1835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1836
1837 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1838
1839 return S_OK;
1840}
1841
1842HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1843{
1844 HRESULT rc = S_OK;
1845
1846 /* check throttle limits */
1847 if ( aCPUExecutionCap < 1
1848 || aCPUExecutionCap > 100
1849 )
1850 return setError(E_INVALIDARG,
1851 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1852 aCPUExecutionCap, 1, 100);
1853
1854 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1855
1856 rc = i_checkStateDependency(MutableOrRunningStateDep);
1857 if (FAILED(rc)) return rc;
1858
1859 alock.release();
1860 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1861 alock.acquire();
1862 if (FAILED(rc)) return rc;
1863
1864 i_setModified(IsModified_MachineData);
1865 mHWData.backup();
1866 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1867
1868 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1869 if (Global::IsOnline(mData->mMachineState))
1870 i_saveSettings(NULL, alock);
1871
1872 return S_OK;
1873}
1874
1875HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1876{
1877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1878
1879 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1880
1881 return S_OK;
1882}
1883
1884HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1885{
1886 HRESULT rc = S_OK;
1887
1888 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1889
1890 rc = i_checkStateDependency(MutableStateDep);
1891 if (FAILED(rc)) return rc;
1892
1893 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1894 {
1895 if (aCPUHotPlugEnabled)
1896 {
1897 i_setModified(IsModified_MachineData);
1898 mHWData.backup();
1899
1900 /* Add the amount of CPUs currently attached */
1901 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1902 mHWData->mCPUAttached[i] = true;
1903 }
1904 else
1905 {
1906 /*
1907 * We can disable hotplug only if the amount of maximum CPUs is equal
1908 * to the amount of attached CPUs
1909 */
1910 unsigned cCpusAttached = 0;
1911 unsigned iHighestId = 0;
1912
1913 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1914 {
1915 if (mHWData->mCPUAttached[i])
1916 {
1917 cCpusAttached++;
1918 iHighestId = i;
1919 }
1920 }
1921
1922 if ( (cCpusAttached != mHWData->mCPUCount)
1923 || (iHighestId >= mHWData->mCPUCount))
1924 return setError(E_INVALIDARG,
1925 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1926
1927 i_setModified(IsModified_MachineData);
1928 mHWData.backup();
1929 }
1930 }
1931
1932 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1933
1934 return rc;
1935}
1936
1937HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1938{
1939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1940
1941 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1942
1943 return S_OK;
1944}
1945
1946HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1947{
1948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1949
1950 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1951 if (SUCCEEDED(hrc))
1952 {
1953 i_setModified(IsModified_MachineData);
1954 mHWData.backup();
1955 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1956 }
1957 return hrc;
1958}
1959
1960HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1961{
1962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1963 aCPUProfile = mHWData->mCpuProfile;
1964 return S_OK;
1965}
1966
1967HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1968{
1969 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1970 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1971 if (SUCCEEDED(hrc))
1972 {
1973 i_setModified(IsModified_MachineData);
1974 mHWData.backup();
1975 /* Empty equals 'host'. */
1976 if (aCPUProfile.isNotEmpty())
1977 mHWData->mCpuProfile = aCPUProfile;
1978 else
1979 mHWData->mCpuProfile = "host";
1980 }
1981 return hrc;
1982}
1983
1984HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1985{
1986#ifdef VBOX_WITH_USB_CARDREADER
1987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1988
1989 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1990
1991 return S_OK;
1992#else
1993 NOREF(aEmulatedUSBCardReaderEnabled);
1994 return E_NOTIMPL;
1995#endif
1996}
1997
1998HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1999{
2000#ifdef VBOX_WITH_USB_CARDREADER
2001 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2002
2003 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2004 if (FAILED(rc)) return rc;
2005
2006 i_setModified(IsModified_MachineData);
2007 mHWData.backup();
2008 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
2009
2010 return S_OK;
2011#else
2012 NOREF(aEmulatedUSBCardReaderEnabled);
2013 return E_NOTIMPL;
2014#endif
2015}
2016
2017HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
2018{
2019 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2020
2021 *aHPETEnabled = mHWData->mHPETEnabled;
2022
2023 return S_OK;
2024}
2025
2026HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
2027{
2028 HRESULT rc = S_OK;
2029
2030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2031
2032 rc = i_checkStateDependency(MutableStateDep);
2033 if (FAILED(rc)) return rc;
2034
2035 i_setModified(IsModified_MachineData);
2036 mHWData.backup();
2037
2038 mHWData->mHPETEnabled = aHPETEnabled;
2039
2040 return rc;
2041}
2042
2043/** @todo this method should not be public */
2044HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2045{
2046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2047
2048 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2049
2050 return S_OK;
2051}
2052
2053/**
2054 * Set the memory balloon size.
2055 *
2056 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2057 * we have to make sure that we never call IGuest from here.
2058 */
2059HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2060{
2061 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2062#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2063 /* check limits */
2064 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2065 return setError(E_INVALIDARG,
2066 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2067 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2068
2069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2070
2071 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
2072 if (FAILED(rc)) return rc;
2073
2074 i_setModified(IsModified_MachineData);
2075 mHWData.backup();
2076 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2077
2078 return S_OK;
2079#else
2080 NOREF(aMemoryBalloonSize);
2081 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2082#endif
2083}
2084
2085HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2086{
2087 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2088
2089 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2090 return S_OK;
2091}
2092
2093HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2094{
2095#ifdef VBOX_WITH_PAGE_SHARING
2096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2097
2098 HRESULT rc = i_checkStateDependency(MutableStateDep);
2099 if (FAILED(rc)) return rc;
2100
2101 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2102 i_setModified(IsModified_MachineData);
2103 mHWData.backup();
2104 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2105 return S_OK;
2106#else
2107 NOREF(aPageFusionEnabled);
2108 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2109#endif
2110}
2111
2112HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2113{
2114 /* mBIOSSettings is constant during life time, no need to lock */
2115 aBIOSSettings = mBIOSSettings;
2116
2117 return S_OK;
2118}
2119
2120HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
2121{
2122 /* mTrustedPlatformModule is constant during life time, no need to lock */
2123 aTrustedPlatformModule = mTrustedPlatformModule;
2124
2125 return S_OK;
2126}
2127
2128HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
2129{
2130 /* mNvramStore is constant during life time, no need to lock */
2131 aNvramStore = mNvramStore;
2132
2133 return S_OK;
2134}
2135
2136HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
2137{
2138 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2139
2140 aRecordingSettings = mRecordingSettings;
2141
2142 return S_OK;
2143}
2144
2145HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
2146{
2147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2148
2149 aGraphicsAdapter = mGraphicsAdapter;
2150
2151 return S_OK;
2152}
2153
2154HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2155{
2156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2157
2158 switch (aProperty)
2159 {
2160 case CPUPropertyType_PAE:
2161 *aValue = mHWData->mPAEEnabled;
2162 break;
2163
2164 case CPUPropertyType_LongMode:
2165 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2166 *aValue = TRUE;
2167 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2168 *aValue = FALSE;
2169#if HC_ARCH_BITS == 64
2170 else
2171 *aValue = TRUE;
2172#else
2173 else
2174 {
2175 *aValue = FALSE;
2176
2177 ComObjPtr<GuestOSType> pGuestOSType;
2178 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2179 pGuestOSType);
2180 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
2181 {
2182 if (pGuestOSType->i_is64Bit())
2183 {
2184 ComObjPtr<Host> pHost = mParent->i_host();
2185 alock.release();
2186
2187 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2188 if (FAILED(hrc2))
2189 *aValue = FALSE;
2190 }
2191 }
2192 }
2193#endif
2194 break;
2195
2196 case CPUPropertyType_TripleFaultReset:
2197 *aValue = mHWData->mTripleFaultReset;
2198 break;
2199
2200 case CPUPropertyType_APIC:
2201 *aValue = mHWData->mAPIC;
2202 break;
2203
2204 case CPUPropertyType_X2APIC:
2205 *aValue = mHWData->mX2APIC;
2206 break;
2207
2208 case CPUPropertyType_IBPBOnVMExit:
2209 *aValue = mHWData->mIBPBOnVMExit;
2210 break;
2211
2212 case CPUPropertyType_IBPBOnVMEntry:
2213 *aValue = mHWData->mIBPBOnVMEntry;
2214 break;
2215
2216 case CPUPropertyType_SpecCtrl:
2217 *aValue = mHWData->mSpecCtrl;
2218 break;
2219
2220 case CPUPropertyType_SpecCtrlByHost:
2221 *aValue = mHWData->mSpecCtrlByHost;
2222 break;
2223
2224 case CPUPropertyType_HWVirt:
2225 *aValue = mHWData->mNestedHWVirt;
2226 break;
2227
2228 case CPUPropertyType_L1DFlushOnEMTScheduling:
2229 *aValue = mHWData->mL1DFlushOnSched;
2230 break;
2231
2232 case CPUPropertyType_L1DFlushOnVMEntry:
2233 *aValue = mHWData->mL1DFlushOnVMEntry;
2234 break;
2235
2236 case CPUPropertyType_MDSClearOnEMTScheduling:
2237 *aValue = mHWData->mMDSClearOnSched;
2238 break;
2239
2240 case CPUPropertyType_MDSClearOnVMEntry:
2241 *aValue = mHWData->mMDSClearOnVMEntry;
2242 break;
2243
2244 default:
2245 return E_INVALIDARG;
2246 }
2247 return S_OK;
2248}
2249
2250HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2251{
2252 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2253
2254 HRESULT rc = i_checkStateDependency(MutableStateDep);
2255 if (FAILED(rc)) return rc;
2256
2257 switch (aProperty)
2258 {
2259 case CPUPropertyType_PAE:
2260 i_setModified(IsModified_MachineData);
2261 mHWData.backup();
2262 mHWData->mPAEEnabled = !!aValue;
2263 break;
2264
2265 case CPUPropertyType_LongMode:
2266 i_setModified(IsModified_MachineData);
2267 mHWData.backup();
2268 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2269 break;
2270
2271 case CPUPropertyType_TripleFaultReset:
2272 i_setModified(IsModified_MachineData);
2273 mHWData.backup();
2274 mHWData->mTripleFaultReset = !!aValue;
2275 break;
2276
2277 case CPUPropertyType_APIC:
2278 if (mHWData->mX2APIC)
2279 aValue = TRUE;
2280 i_setModified(IsModified_MachineData);
2281 mHWData.backup();
2282 mHWData->mAPIC = !!aValue;
2283 break;
2284
2285 case CPUPropertyType_X2APIC:
2286 i_setModified(IsModified_MachineData);
2287 mHWData.backup();
2288 mHWData->mX2APIC = !!aValue;
2289 if (aValue)
2290 mHWData->mAPIC = !!aValue;
2291 break;
2292
2293 case CPUPropertyType_IBPBOnVMExit:
2294 i_setModified(IsModified_MachineData);
2295 mHWData.backup();
2296 mHWData->mIBPBOnVMExit = !!aValue;
2297 break;
2298
2299 case CPUPropertyType_IBPBOnVMEntry:
2300 i_setModified(IsModified_MachineData);
2301 mHWData.backup();
2302 mHWData->mIBPBOnVMEntry = !!aValue;
2303 break;
2304
2305 case CPUPropertyType_SpecCtrl:
2306 i_setModified(IsModified_MachineData);
2307 mHWData.backup();
2308 mHWData->mSpecCtrl = !!aValue;
2309 break;
2310
2311 case CPUPropertyType_SpecCtrlByHost:
2312 i_setModified(IsModified_MachineData);
2313 mHWData.backup();
2314 mHWData->mSpecCtrlByHost = !!aValue;
2315 break;
2316
2317 case CPUPropertyType_HWVirt:
2318 i_setModified(IsModified_MachineData);
2319 mHWData.backup();
2320 mHWData->mNestedHWVirt = !!aValue;
2321 break;
2322
2323 case CPUPropertyType_L1DFlushOnEMTScheduling:
2324 i_setModified(IsModified_MachineData);
2325 mHWData.backup();
2326 mHWData->mL1DFlushOnSched = !!aValue;
2327 break;
2328
2329 case CPUPropertyType_L1DFlushOnVMEntry:
2330 i_setModified(IsModified_MachineData);
2331 mHWData.backup();
2332 mHWData->mL1DFlushOnVMEntry = !!aValue;
2333 break;
2334
2335 case CPUPropertyType_MDSClearOnEMTScheduling:
2336 i_setModified(IsModified_MachineData);
2337 mHWData.backup();
2338 mHWData->mMDSClearOnSched = !!aValue;
2339 break;
2340
2341 case CPUPropertyType_MDSClearOnVMEntry:
2342 i_setModified(IsModified_MachineData);
2343 mHWData.backup();
2344 mHWData->mMDSClearOnVMEntry = !!aValue;
2345 break;
2346
2347 default:
2348 return E_INVALIDARG;
2349 }
2350 return S_OK;
2351}
2352
2353HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2354 ULONG *aValEcx, ULONG *aValEdx)
2355{
2356 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2357 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2358 {
2359 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2360 it != mHWData->mCpuIdLeafList.end();
2361 ++it)
2362 {
2363 if (aOrdinal == 0)
2364 {
2365 const settings::CpuIdLeaf &rLeaf= *it;
2366 *aIdx = rLeaf.idx;
2367 *aSubIdx = rLeaf.idxSub;
2368 *aValEax = rLeaf.uEax;
2369 *aValEbx = rLeaf.uEbx;
2370 *aValEcx = rLeaf.uEcx;
2371 *aValEdx = rLeaf.uEdx;
2372 return S_OK;
2373 }
2374 aOrdinal--;
2375 }
2376 }
2377 return E_INVALIDARG;
2378}
2379
2380HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2381{
2382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2383
2384 /*
2385 * Search the list.
2386 */
2387 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2388 {
2389 const settings::CpuIdLeaf &rLeaf= *it;
2390 if ( rLeaf.idx == aIdx
2391 && ( aSubIdx == UINT32_MAX
2392 || rLeaf.idxSub == aSubIdx) )
2393 {
2394 *aValEax = rLeaf.uEax;
2395 *aValEbx = rLeaf.uEbx;
2396 *aValEcx = rLeaf.uEcx;
2397 *aValEdx = rLeaf.uEdx;
2398 return S_OK;
2399 }
2400 }
2401
2402 return E_INVALIDARG;
2403}
2404
2405
2406HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2407{
2408 /*
2409 * Validate input before taking locks and checking state.
2410 */
2411 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2412 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2413 if ( aIdx >= UINT32_C(0x20)
2414 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2415 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2416 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2417
2418 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2419 HRESULT rc = i_checkStateDependency(MutableStateDep);
2420 if (FAILED(rc)) return rc;
2421
2422 /*
2423 * Impose a maximum number of leaves.
2424 */
2425 if (mHWData->mCpuIdLeafList.size() > 256)
2426 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2427
2428 /*
2429 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2430 */
2431 i_setModified(IsModified_MachineData);
2432 mHWData.backup();
2433
2434 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2435 {
2436 settings::CpuIdLeaf &rLeaf= *it;
2437 if ( rLeaf.idx == aIdx
2438 && ( aSubIdx == UINT32_MAX
2439 || rLeaf.idxSub == aSubIdx) )
2440 it = mHWData->mCpuIdLeafList.erase(it);
2441 else
2442 ++it;
2443 }
2444
2445 settings::CpuIdLeaf NewLeaf;
2446 NewLeaf.idx = aIdx;
2447 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2448 NewLeaf.uEax = aValEax;
2449 NewLeaf.uEbx = aValEbx;
2450 NewLeaf.uEcx = aValEcx;
2451 NewLeaf.uEdx = aValEdx;
2452 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2453 return S_OK;
2454}
2455
2456HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2457{
2458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2459
2460 HRESULT rc = i_checkStateDependency(MutableStateDep);
2461 if (FAILED(rc)) return rc;
2462
2463 /*
2464 * Do the removal.
2465 */
2466 bool fModified = mHWData.isBackedUp();
2467 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2468 {
2469 settings::CpuIdLeaf &rLeaf= *it;
2470 if ( rLeaf.idx == aIdx
2471 && ( aSubIdx == UINT32_MAX
2472 || rLeaf.idxSub == aSubIdx) )
2473 {
2474 if (!fModified)
2475 {
2476 fModified = true;
2477 i_setModified(IsModified_MachineData);
2478 mHWData.backup();
2479 // Start from the beginning, since mHWData.backup() creates
2480 // a new list, causing iterator mixup. This makes sure that
2481 // the settings are not unnecessarily marked as modified,
2482 // at the price of extra list walking.
2483 it = mHWData->mCpuIdLeafList.begin();
2484 }
2485 else
2486 it = mHWData->mCpuIdLeafList.erase(it);
2487 }
2488 else
2489 ++it;
2490 }
2491
2492 return S_OK;
2493}
2494
2495HRESULT Machine::removeAllCPUIDLeaves()
2496{
2497 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2498
2499 HRESULT rc = i_checkStateDependency(MutableStateDep);
2500 if (FAILED(rc)) return rc;
2501
2502 if (mHWData->mCpuIdLeafList.size() > 0)
2503 {
2504 i_setModified(IsModified_MachineData);
2505 mHWData.backup();
2506
2507 mHWData->mCpuIdLeafList.clear();
2508 }
2509
2510 return S_OK;
2511}
2512HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2513{
2514 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2515
2516 switch(aProperty)
2517 {
2518 case HWVirtExPropertyType_Enabled:
2519 *aValue = mHWData->mHWVirtExEnabled;
2520 break;
2521
2522 case HWVirtExPropertyType_VPID:
2523 *aValue = mHWData->mHWVirtExVPIDEnabled;
2524 break;
2525
2526 case HWVirtExPropertyType_NestedPaging:
2527 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2528 break;
2529
2530 case HWVirtExPropertyType_UnrestrictedExecution:
2531 *aValue = mHWData->mHWVirtExUXEnabled;
2532 break;
2533
2534 case HWVirtExPropertyType_LargePages:
2535 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2536 break;
2537
2538 case HWVirtExPropertyType_Force:
2539 *aValue = mHWData->mHWVirtExForceEnabled;
2540 break;
2541
2542 case HWVirtExPropertyType_UseNativeApi:
2543 *aValue = mHWData->mHWVirtExUseNativeApi;
2544 break;
2545
2546 case HWVirtExPropertyType_VirtVmsaveVmload:
2547 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2548 break;
2549
2550 default:
2551 return E_INVALIDARG;
2552 }
2553 return S_OK;
2554}
2555
2556HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2557{
2558 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2559
2560 HRESULT rc = i_checkStateDependency(MutableStateDep);
2561 if (FAILED(rc)) return rc;
2562
2563 switch (aProperty)
2564 {
2565 case HWVirtExPropertyType_Enabled:
2566 i_setModified(IsModified_MachineData);
2567 mHWData.backup();
2568 mHWData->mHWVirtExEnabled = !!aValue;
2569 break;
2570
2571 case HWVirtExPropertyType_VPID:
2572 i_setModified(IsModified_MachineData);
2573 mHWData.backup();
2574 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2575 break;
2576
2577 case HWVirtExPropertyType_NestedPaging:
2578 i_setModified(IsModified_MachineData);
2579 mHWData.backup();
2580 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2581 break;
2582
2583 case HWVirtExPropertyType_UnrestrictedExecution:
2584 i_setModified(IsModified_MachineData);
2585 mHWData.backup();
2586 mHWData->mHWVirtExUXEnabled = !!aValue;
2587 break;
2588
2589 case HWVirtExPropertyType_LargePages:
2590 i_setModified(IsModified_MachineData);
2591 mHWData.backup();
2592 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2593 break;
2594
2595 case HWVirtExPropertyType_Force:
2596 i_setModified(IsModified_MachineData);
2597 mHWData.backup();
2598 mHWData->mHWVirtExForceEnabled = !!aValue;
2599 break;
2600
2601 case HWVirtExPropertyType_UseNativeApi:
2602 i_setModified(IsModified_MachineData);
2603 mHWData.backup();
2604 mHWData->mHWVirtExUseNativeApi = !!aValue;
2605 break;
2606
2607 case HWVirtExPropertyType_VirtVmsaveVmload:
2608 i_setModified(IsModified_MachineData);
2609 mHWData.backup();
2610 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2611 break;
2612
2613 default:
2614 return E_INVALIDARG;
2615 }
2616
2617 return S_OK;
2618}
2619
2620HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2621{
2622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2623
2624 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2625
2626 return S_OK;
2627}
2628
2629HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2630{
2631 /** @todo (r=dmik):
2632 * 1. Allow to change the name of the snapshot folder containing snapshots
2633 * 2. Rename the folder on disk instead of just changing the property
2634 * value (to be smart and not to leave garbage). Note that it cannot be
2635 * done here because the change may be rolled back. Thus, the right
2636 * place is #saveSettings().
2637 */
2638
2639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2640
2641 HRESULT rc = i_checkStateDependency(MutableStateDep);
2642 if (FAILED(rc)) return rc;
2643
2644 if (!mData->mCurrentSnapshot.isNull())
2645 return setError(E_FAIL,
2646 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2647
2648 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2649
2650 if (strSnapshotFolder.isEmpty())
2651 strSnapshotFolder = "Snapshots";
2652 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2653 if (RT_FAILURE(vrc))
2654 return setErrorBoth(E_FAIL, vrc,
2655 tr("Invalid snapshot folder '%s' (%Rrc)"),
2656 strSnapshotFolder.c_str(), vrc);
2657
2658 i_setModified(IsModified_MachineData);
2659 mUserData.backup();
2660
2661 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2662
2663 return S_OK;
2664}
2665
2666HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2667{
2668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2669
2670 aMediumAttachments.resize(mMediumAttachments->size());
2671 size_t i = 0;
2672 for (MediumAttachmentList::const_iterator
2673 it = mMediumAttachments->begin();
2674 it != mMediumAttachments->end();
2675 ++it, ++i)
2676 aMediumAttachments[i] = *it;
2677
2678 return S_OK;
2679}
2680
2681HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2682{
2683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2684
2685 Assert(!!mVRDEServer);
2686
2687 aVRDEServer = mVRDEServer;
2688
2689 return S_OK;
2690}
2691
2692HRESULT Machine::getAudioSettings(ComPtr<IAudioSettings> &aAudioSettings)
2693{
2694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2695
2696 aAudioSettings = mAudioSettings;
2697
2698 return S_OK;
2699}
2700
2701HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2702{
2703#ifdef VBOX_WITH_VUSB
2704 clearError();
2705 MultiResult rc(S_OK);
2706
2707# ifdef VBOX_WITH_USB
2708 rc = mParent->i_host()->i_checkUSBProxyService();
2709 if (FAILED(rc)) return rc;
2710# endif
2711
2712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2713
2714 aUSBControllers.resize(mUSBControllers->size());
2715 size_t i = 0;
2716 for (USBControllerList::const_iterator
2717 it = mUSBControllers->begin();
2718 it != mUSBControllers->end();
2719 ++it, ++i)
2720 aUSBControllers[i] = *it;
2721
2722 return S_OK;
2723#else
2724 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2725 * extended error info to indicate that USB is simply not available
2726 * (w/o treating it as a failure), for example, as in OSE */
2727 NOREF(aUSBControllers);
2728 ReturnComNotImplemented();
2729#endif /* VBOX_WITH_VUSB */
2730}
2731
2732HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2733{
2734#ifdef VBOX_WITH_VUSB
2735 clearError();
2736 MultiResult rc(S_OK);
2737
2738# ifdef VBOX_WITH_USB
2739 rc = mParent->i_host()->i_checkUSBProxyService();
2740 if (FAILED(rc)) return rc;
2741# endif
2742
2743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2744
2745 aUSBDeviceFilters = mUSBDeviceFilters;
2746 return rc;
2747#else
2748 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2749 * extended error info to indicate that USB is simply not available
2750 * (w/o treating it as a failure), for example, as in OSE */
2751 NOREF(aUSBDeviceFilters);
2752 ReturnComNotImplemented();
2753#endif /* VBOX_WITH_VUSB */
2754}
2755
2756HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2757{
2758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2759
2760 aSettingsFilePath = mData->m_strConfigFileFull;
2761
2762 return S_OK;
2763}
2764
2765HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2766{
2767 RT_NOREF(aSettingsFilePath);
2768 ReturnComNotImplemented();
2769}
2770
2771HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2772{
2773 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2774
2775 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2776 if (FAILED(rc)) return rc;
2777
2778 if (!mData->pMachineConfigFile->fileExists())
2779 // this is a new machine, and no config file exists yet:
2780 *aSettingsModified = TRUE;
2781 else
2782 *aSettingsModified = (mData->flModifications != 0);
2783
2784 return S_OK;
2785}
2786
2787HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2788{
2789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2790
2791 *aSessionState = mData->mSession.mState;
2792
2793 return S_OK;
2794}
2795
2796HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2797{
2798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2799
2800 aSessionName = mData->mSession.mName;
2801
2802 return S_OK;
2803}
2804
2805HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2806{
2807 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2808
2809 *aSessionPID = mData->mSession.mPID;
2810
2811 return S_OK;
2812}
2813
2814HRESULT Machine::getState(MachineState_T *aState)
2815{
2816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2817
2818 *aState = mData->mMachineState;
2819 Assert(mData->mMachineState != MachineState_Null);
2820
2821 return S_OK;
2822}
2823
2824HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2825{
2826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2827
2828 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2829
2830 return S_OK;
2831}
2832
2833HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2834{
2835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2836
2837 aStateFilePath = mSSData->strStateFilePath;
2838
2839 return S_OK;
2840}
2841
2842HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2843{
2844 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2845
2846 i_getLogFolder(aLogFolder);
2847
2848 return S_OK;
2849}
2850
2851HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2852{
2853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2854
2855 aCurrentSnapshot = mData->mCurrentSnapshot;
2856
2857 return S_OK;
2858}
2859
2860HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2861{
2862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2863
2864 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2865 ? 0
2866 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2867
2868 return S_OK;
2869}
2870
2871HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2872{
2873 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2874
2875 /* Note: for machines with no snapshots, we always return FALSE
2876 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2877 * reasons :) */
2878
2879 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2880 ? FALSE
2881 : mData->mCurrentStateModified;
2882
2883 return S_OK;
2884}
2885
2886HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2887{
2888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2889
2890 aSharedFolders.resize(mHWData->mSharedFolders.size());
2891 size_t i = 0;
2892 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2893 it = mHWData->mSharedFolders.begin();
2894 it != mHWData->mSharedFolders.end();
2895 ++it, ++i)
2896 aSharedFolders[i] = *it;
2897
2898 return S_OK;
2899}
2900
2901HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2902{
2903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2904
2905 *aClipboardMode = mHWData->mClipboardMode;
2906
2907 return S_OK;
2908}
2909
2910HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2911{
2912 HRESULT rc = S_OK;
2913
2914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2915
2916 rc = i_checkStateDependency(MutableOrRunningStateDep);
2917 if (FAILED(rc)) return rc;
2918
2919 alock.release();
2920 rc = i_onClipboardModeChange(aClipboardMode);
2921 alock.acquire();
2922 if (FAILED(rc)) return rc;
2923
2924 i_setModified(IsModified_MachineData);
2925 mHWData.backup();
2926 mHWData->mClipboardMode = aClipboardMode;
2927
2928 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2929 if (Global::IsOnline(mData->mMachineState))
2930 i_saveSettings(NULL, alock);
2931
2932 return S_OK;
2933}
2934
2935HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2936{
2937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2938
2939 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2940
2941 return S_OK;
2942}
2943
2944HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2945{
2946 HRESULT rc = S_OK;
2947
2948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2949
2950 rc = i_checkStateDependency(MutableOrRunningStateDep);
2951 if (FAILED(rc)) return rc;
2952
2953 alock.release();
2954 rc = i_onClipboardFileTransferModeChange(aEnabled);
2955 alock.acquire();
2956 if (FAILED(rc)) return rc;
2957
2958 i_setModified(IsModified_MachineData);
2959 mHWData.backup();
2960 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2961
2962 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2963 if (Global::IsOnline(mData->mMachineState))
2964 i_saveSettings(NULL, alock);
2965
2966 return S_OK;
2967}
2968
2969HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2970{
2971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2972
2973 *aDnDMode = mHWData->mDnDMode;
2974
2975 return S_OK;
2976}
2977
2978HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2979{
2980 HRESULT rc = S_OK;
2981
2982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2983
2984 rc = i_checkStateDependency(MutableOrRunningStateDep);
2985 if (FAILED(rc)) return rc;
2986
2987 alock.release();
2988 rc = i_onDnDModeChange(aDnDMode);
2989
2990 alock.acquire();
2991 if (FAILED(rc)) return rc;
2992
2993 i_setModified(IsModified_MachineData);
2994 mHWData.backup();
2995 mHWData->mDnDMode = aDnDMode;
2996
2997 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2998 if (Global::IsOnline(mData->mMachineState))
2999 i_saveSettings(NULL, alock);
3000
3001 return S_OK;
3002}
3003
3004HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
3005{
3006 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3007
3008 aStorageControllers.resize(mStorageControllers->size());
3009 size_t i = 0;
3010 for (StorageControllerList::const_iterator
3011 it = mStorageControllers->begin();
3012 it != mStorageControllers->end();
3013 ++it, ++i)
3014 aStorageControllers[i] = *it;
3015
3016 return S_OK;
3017}
3018
3019HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
3020{
3021 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3022
3023 *aEnabled = mUserData->s.fTeleporterEnabled;
3024
3025 return S_OK;
3026}
3027
3028HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
3029{
3030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3031
3032 /* Only allow it to be set to true when PoweredOff or Aborted.
3033 (Clearing it is always permitted.) */
3034 if ( aTeleporterEnabled
3035 && mData->mRegistered
3036 && ( !i_isSessionMachine()
3037 || ( mData->mMachineState != MachineState_PoweredOff
3038 && mData->mMachineState != MachineState_Teleported
3039 && mData->mMachineState != MachineState_Aborted
3040 )
3041 )
3042 )
3043 return setError(VBOX_E_INVALID_VM_STATE,
3044 tr("The machine is not powered off (state is %s)"),
3045 Global::stringifyMachineState(mData->mMachineState));
3046
3047 i_setModified(IsModified_MachineData);
3048 mUserData.backup();
3049 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
3050
3051 return S_OK;
3052}
3053
3054HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
3055{
3056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3057
3058 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
3059
3060 return S_OK;
3061}
3062
3063HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
3064{
3065 if (aTeleporterPort >= _64K)
3066 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
3067
3068 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3069
3070 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3071 if (FAILED(rc)) return rc;
3072
3073 i_setModified(IsModified_MachineData);
3074 mUserData.backup();
3075 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3076
3077 return S_OK;
3078}
3079
3080HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3081{
3082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3083
3084 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3085
3086 return S_OK;
3087}
3088
3089HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3090{
3091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3092
3093 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3094 if (FAILED(rc)) return rc;
3095
3096 i_setModified(IsModified_MachineData);
3097 mUserData.backup();
3098 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3099
3100 return S_OK;
3101}
3102
3103HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3104{
3105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3106 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3107
3108 return S_OK;
3109}
3110
3111HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3112{
3113 /*
3114 * Hash the password first.
3115 */
3116 com::Utf8Str aT = aTeleporterPassword;
3117
3118 if (!aT.isEmpty())
3119 {
3120 if (VBoxIsPasswordHashed(&aT))
3121 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3122 VBoxHashPassword(&aT);
3123 }
3124
3125 /*
3126 * Do the update.
3127 */
3128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3129 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3130 if (SUCCEEDED(hrc))
3131 {
3132 i_setModified(IsModified_MachineData);
3133 mUserData.backup();
3134 mUserData->s.strTeleporterPassword = aT;
3135 }
3136
3137 return hrc;
3138}
3139
3140HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3141{
3142 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3143
3144 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3145
3146 return S_OK;
3147}
3148
3149HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3150{
3151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3152
3153 /* Only allow it to be set to true when PoweredOff or Aborted.
3154 (Clearing it is always permitted.) */
3155 if ( aRTCUseUTC
3156 && mData->mRegistered
3157 && ( !i_isSessionMachine()
3158 || ( mData->mMachineState != MachineState_PoweredOff
3159 && mData->mMachineState != MachineState_Teleported
3160 && mData->mMachineState != MachineState_Aborted
3161 )
3162 )
3163 )
3164 return setError(VBOX_E_INVALID_VM_STATE,
3165 tr("The machine is not powered off (state is %s)"),
3166 Global::stringifyMachineState(mData->mMachineState));
3167
3168 i_setModified(IsModified_MachineData);
3169 mUserData.backup();
3170 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3171
3172 return S_OK;
3173}
3174
3175HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3176{
3177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3178
3179 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3180
3181 return S_OK;
3182}
3183
3184HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3185{
3186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3187
3188 HRESULT rc = i_checkStateDependency(MutableStateDep);
3189 if (FAILED(rc)) return rc;
3190
3191 i_setModified(IsModified_MachineData);
3192 mHWData.backup();
3193 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3194
3195 return S_OK;
3196}
3197
3198HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3199{
3200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3201
3202 *aIOCacheSize = mHWData->mIOCacheSize;
3203
3204 return S_OK;
3205}
3206
3207HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3208{
3209 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3210
3211 HRESULT rc = i_checkStateDependency(MutableStateDep);
3212 if (FAILED(rc)) return rc;
3213
3214 i_setModified(IsModified_MachineData);
3215 mHWData.backup();
3216 mHWData->mIOCacheSize = aIOCacheSize;
3217
3218 return S_OK;
3219}
3220
3221HRESULT Machine::getStateKeyId(com::Utf8Str &aKeyId)
3222{
3223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3224
3225#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3226 aKeyId = mSSData->strStateKeyId;
3227#else
3228 aKeyId = com::Utf8Str::Empty;
3229#endif
3230
3231 return S_OK;
3232}
3233
3234HRESULT Machine::getStateKeyStore(com::Utf8Str &aKeyStore)
3235{
3236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3237
3238#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3239 aKeyStore = mSSData->strStateKeyStore;
3240#else
3241 aKeyStore = com::Utf8Str::Empty;
3242#endif
3243
3244 return S_OK;
3245}
3246
3247HRESULT Machine::getLogKeyId(com::Utf8Str &aKeyId)
3248{
3249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3250
3251#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3252 aKeyId = mData->mstrLogKeyId;
3253#else
3254 aKeyId = com::Utf8Str::Empty;
3255#endif
3256
3257 return S_OK;
3258}
3259
3260HRESULT Machine::getLogKeyStore(com::Utf8Str &aKeyStore)
3261{
3262 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3263
3264#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3265 aKeyStore = mData->mstrLogKeyStore;
3266#else
3267 aKeyStore = com::Utf8Str::Empty;
3268#endif
3269
3270 return S_OK;
3271}
3272
3273
3274/**
3275 * @note Locks objects!
3276 */
3277HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3278 LockType_T aLockType)
3279{
3280 /* check the session state */
3281 SessionState_T state;
3282 HRESULT rc = aSession->COMGETTER(State)(&state);
3283 if (FAILED(rc)) return rc;
3284
3285 if (state != SessionState_Unlocked)
3286 return setError(VBOX_E_INVALID_OBJECT_STATE,
3287 tr("The given session is busy"));
3288
3289 // get the client's IInternalSessionControl interface
3290 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3291 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
3292 E_INVALIDARG);
3293
3294 // session name (only used in some code paths)
3295 Utf8Str strSessionName;
3296
3297 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3298
3299 if (!mData->mRegistered)
3300 return setError(E_UNEXPECTED,
3301 tr("The machine '%s' is not registered"),
3302 mUserData->s.strName.c_str());
3303
3304 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3305
3306 SessionState_T oldState = mData->mSession.mState;
3307 /* Hack: in case the session is closing and there is a progress object
3308 * which allows waiting for the session to be closed, take the opportunity
3309 * and do a limited wait (max. 1 second). This helps a lot when the system
3310 * is busy and thus session closing can take a little while. */
3311 if ( mData->mSession.mState == SessionState_Unlocking
3312 && mData->mSession.mProgress)
3313 {
3314 alock.release();
3315 mData->mSession.mProgress->WaitForCompletion(1000);
3316 alock.acquire();
3317 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
3318 }
3319
3320 // try again now
3321 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3322 // (i.e. session machine exists)
3323 && (aLockType == LockType_Shared) // caller wants a shared link to the
3324 // existing session that holds the write lock:
3325 )
3326 {
3327 // OK, share the session... we are now dealing with three processes:
3328 // 1) VBoxSVC (where this code runs);
3329 // 2) process C: the caller's client process (who wants a shared session);
3330 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3331
3332 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3333 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3334 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3335 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3336 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3337
3338 /*
3339 * Release the lock before calling the client process. It's safe here
3340 * since the only thing to do after we get the lock again is to add
3341 * the remote control to the list (which doesn't directly influence
3342 * anything).
3343 */
3344 alock.release();
3345
3346 // get the console of the session holding the write lock (this is a remote call)
3347 ComPtr<IConsole> pConsoleW;
3348 if (mData->mSession.mLockType == LockType_VM)
3349 {
3350 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3351 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3352 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3353 if (FAILED(rc))
3354 // the failure may occur w/o any error info (from RPC), so provide one
3355 return setError(VBOX_E_VM_ERROR,
3356 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3357 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3358 }
3359
3360 // share the session machine and W's console with the caller's session
3361 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3362 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3363 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3364
3365 if (FAILED(rc))
3366 // the failure may occur w/o any error info (from RPC), so provide one
3367 return setError(VBOX_E_VM_ERROR,
3368 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3369 alock.acquire();
3370
3371 // need to revalidate the state after acquiring the lock again
3372 if (mData->mSession.mState != SessionState_Locked)
3373 {
3374 pSessionControl->Uninitialize();
3375 return setError(VBOX_E_INVALID_SESSION_STATE,
3376 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3377 mUserData->s.strName.c_str());
3378 }
3379
3380 // add the caller's session to the list
3381 mData->mSession.mRemoteControls.push_back(pSessionControl);
3382 }
3383 else if ( mData->mSession.mState == SessionState_Locked
3384 || mData->mSession.mState == SessionState_Unlocking
3385 )
3386 {
3387 // sharing not permitted, or machine still unlocking:
3388 return setError(VBOX_E_INVALID_OBJECT_STATE,
3389 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3390 mUserData->s.strName.c_str());
3391 }
3392 else
3393 {
3394 // machine is not locked: then write-lock the machine (create the session machine)
3395
3396 // must not be busy
3397 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3398
3399 // get the caller's session PID
3400 RTPROCESS pid = NIL_RTPROCESS;
3401 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3402 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3403 Assert(pid != NIL_RTPROCESS);
3404
3405 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3406
3407 if (fLaunchingVMProcess)
3408 {
3409 if (mData->mSession.mPID == NIL_RTPROCESS)
3410 {
3411 // two or more clients racing for a lock, the one which set the
3412 // session state to Spawning will win, the others will get an
3413 // error as we can't decide here if waiting a little would help
3414 // (only for shared locks this would avoid an error)
3415 return setError(VBOX_E_INVALID_OBJECT_STATE,
3416 tr("The machine '%s' already has a lock request pending"),
3417 mUserData->s.strName.c_str());
3418 }
3419
3420 // this machine is awaiting for a spawning session to be opened:
3421 // then the calling process must be the one that got started by
3422 // LaunchVMProcess()
3423
3424 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3425 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3426
3427#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3428 /* Hardened windows builds spawns three processes when a VM is
3429 launched, the 3rd one is the one that will end up here. */
3430 RTPROCESS pidParent;
3431 int vrc = RTProcQueryParent(pid, &pidParent);
3432 if (RT_SUCCESS(vrc))
3433 vrc = RTProcQueryParent(pidParent, &pidParent);
3434 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3435 || vrc == VERR_ACCESS_DENIED)
3436 {
3437 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3438 mData->mSession.mPID = pid;
3439 }
3440#endif
3441
3442 if (mData->mSession.mPID != pid)
3443 return setError(E_ACCESSDENIED,
3444 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3445 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3446 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3447 }
3448
3449 // create the mutable SessionMachine from the current machine
3450 ComObjPtr<SessionMachine> sessionMachine;
3451 sessionMachine.createObject();
3452 rc = sessionMachine->init(this);
3453 AssertComRC(rc);
3454
3455 /* NOTE: doing return from this function after this point but
3456 * before the end is forbidden since it may call SessionMachine::uninit()
3457 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3458 * lock while still holding the Machine lock in alock so that a deadlock
3459 * is possible due to the wrong lock order. */
3460
3461 if (SUCCEEDED(rc))
3462 {
3463 /*
3464 * Set the session state to Spawning to protect against subsequent
3465 * attempts to open a session and to unregister the machine after
3466 * we release the lock.
3467 */
3468 SessionState_T origState = mData->mSession.mState;
3469 mData->mSession.mState = SessionState_Spawning;
3470
3471#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3472 /* Get the client token ID to be passed to the client process */
3473 Utf8Str strTokenId;
3474 sessionMachine->i_getTokenId(strTokenId);
3475 Assert(!strTokenId.isEmpty());
3476#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3477 /* Get the client token to be passed to the client process */
3478 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3479 /* The token is now "owned" by pToken, fix refcount */
3480 if (!pToken.isNull())
3481 pToken->Release();
3482#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3483
3484 /*
3485 * Release the lock before calling the client process -- it will call
3486 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3487 * because the state is Spawning, so that LaunchVMProcess() and
3488 * LockMachine() calls will fail. This method, called before we
3489 * acquire the lock again, will fail because of the wrong PID.
3490 *
3491 * Note that mData->mSession.mRemoteControls accessed outside
3492 * the lock may not be modified when state is Spawning, so it's safe.
3493 */
3494 alock.release();
3495
3496 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3497#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3498 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3499#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3500 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3501 /* Now the token is owned by the client process. */
3502 pToken.setNull();
3503#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3504 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3505
3506 /* The failure may occur w/o any error info (from RPC), so provide one */
3507 if (FAILED(rc))
3508 setError(VBOX_E_VM_ERROR,
3509 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3510
3511 // get session name, either to remember or to compare against
3512 // the already known session name.
3513 {
3514 Bstr bstrSessionName;
3515 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3516 if (SUCCEEDED(rc2))
3517 strSessionName = bstrSessionName;
3518 }
3519
3520 if ( SUCCEEDED(rc)
3521 && fLaunchingVMProcess
3522 )
3523 {
3524 /* complete the remote session initialization */
3525
3526 /* get the console from the direct session */
3527 ComPtr<IConsole> console;
3528 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3529 ComAssertComRC(rc);
3530
3531 if (SUCCEEDED(rc) && !console)
3532 {
3533 ComAssert(!!console);
3534 rc = E_FAIL;
3535 }
3536
3537 /* assign machine & console to the remote session */
3538 if (SUCCEEDED(rc))
3539 {
3540 /*
3541 * after LaunchVMProcess(), the first and the only
3542 * entry in remoteControls is that remote session
3543 */
3544 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3545 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3546 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3547
3548 /* The failure may occur w/o any error info (from RPC), so provide one */
3549 if (FAILED(rc))
3550 setError(VBOX_E_VM_ERROR,
3551 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3552 }
3553
3554 if (FAILED(rc))
3555 pSessionControl->Uninitialize();
3556 }
3557
3558 /* acquire the lock again */
3559 alock.acquire();
3560
3561 /* Restore the session state */
3562 mData->mSession.mState = origState;
3563 }
3564
3565 // finalize spawning anyway (this is why we don't return on errors above)
3566 if (fLaunchingVMProcess)
3567 {
3568 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3569 /* Note that the progress object is finalized later */
3570 /** @todo Consider checking mData->mSession.mProgress for cancellation
3571 * around here. */
3572
3573 /* We don't reset mSession.mPID here because it is necessary for
3574 * SessionMachine::uninit() to reap the child process later. */
3575
3576 if (FAILED(rc))
3577 {
3578 /* Close the remote session, remove the remote control from the list
3579 * and reset session state to Closed (@note keep the code in sync
3580 * with the relevant part in checkForSpawnFailure()). */
3581
3582 Assert(mData->mSession.mRemoteControls.size() == 1);
3583 if (mData->mSession.mRemoteControls.size() == 1)
3584 {
3585 ErrorInfoKeeper eik;
3586 mData->mSession.mRemoteControls.front()->Uninitialize();
3587 }
3588
3589 mData->mSession.mRemoteControls.clear();
3590 mData->mSession.mState = SessionState_Unlocked;
3591 }
3592 }
3593 else
3594 {
3595 /* memorize PID of the directly opened session */
3596 if (SUCCEEDED(rc))
3597 mData->mSession.mPID = pid;
3598 }
3599
3600 if (SUCCEEDED(rc))
3601 {
3602 mData->mSession.mLockType = aLockType;
3603 /* memorize the direct session control and cache IUnknown for it */
3604 mData->mSession.mDirectControl = pSessionControl;
3605 mData->mSession.mState = SessionState_Locked;
3606 if (!fLaunchingVMProcess)
3607 mData->mSession.mName = strSessionName;
3608 /* associate the SessionMachine with this Machine */
3609 mData->mSession.mMachine = sessionMachine;
3610
3611 /* request an IUnknown pointer early from the remote party for later
3612 * identity checks (it will be internally cached within mDirectControl
3613 * at least on XPCOM) */
3614 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3615 NOREF(unk);
3616
3617#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
3618 if (aLockType == LockType_VM)
3619 {
3620 /* get the console from the direct session */
3621 ComPtr<IConsole> console;
3622 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3623 ComAssertComRC(rc);
3624 /* send passswords to console */
3625 for (SecretKeyStore::SecretKeyMap::iterator it = mData->mpKeyStore->begin();
3626 it != mData->mpKeyStore->end();
3627 ++it)
3628 {
3629 SecretKey *pKey = it->second;
3630 pKey->retain();
3631 console->AddEncryptionPassword(Bstr(it->first).raw(),
3632 Bstr((const char*)pKey->getKeyBuffer()).raw(),
3633 TRUE);
3634 pKey->release();
3635 }
3636
3637 }
3638#endif
3639 }
3640
3641 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3642 * would break the lock order */
3643 alock.release();
3644
3645 /* uninitialize the created session machine on failure */
3646 if (FAILED(rc))
3647 sessionMachine->uninit();
3648 }
3649
3650 if (SUCCEEDED(rc))
3651 {
3652 /*
3653 * tell the client watcher thread to update the set of
3654 * machines that have open sessions
3655 */
3656 mParent->i_updateClientWatcher();
3657
3658 if (oldState != SessionState_Locked)
3659 /* fire an event */
3660 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3661 }
3662
3663 return rc;
3664}
3665
3666/**
3667 * @note Locks objects!
3668 */
3669HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3670 const com::Utf8Str &aName,
3671 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3672 ComPtr<IProgress> &aProgress)
3673{
3674 Utf8Str strFrontend(aName);
3675 /* "emergencystop" doesn't need the session, so skip the checks/interface
3676 * retrieval. This code doesn't quite fit in here, but introducing a
3677 * special API method would be even more effort, and would require explicit
3678 * support by every API client. It's better to hide the feature a bit. */
3679 if (strFrontend != "emergencystop")
3680 CheckComArgNotNull(aSession);
3681
3682 HRESULT rc = S_OK;
3683 if (strFrontend.isEmpty())
3684 {
3685 Bstr bstrFrontend;
3686 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3687 if (FAILED(rc))
3688 return rc;
3689 strFrontend = bstrFrontend;
3690 if (strFrontend.isEmpty())
3691 {
3692 ComPtr<ISystemProperties> systemProperties;
3693 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3694 if (FAILED(rc))
3695 return rc;
3696 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3697 if (FAILED(rc))
3698 return rc;
3699 strFrontend = bstrFrontend;
3700 }
3701 /* paranoia - emergencystop is not a valid default */
3702 if (strFrontend == "emergencystop")
3703 strFrontend = Utf8Str::Empty;
3704 }
3705 /* default frontend: Qt GUI */
3706 if (strFrontend.isEmpty())
3707 strFrontend = "GUI/Qt";
3708
3709 if (strFrontend != "emergencystop")
3710 {
3711 /* check the session state */
3712 SessionState_T state;
3713 rc = aSession->COMGETTER(State)(&state);
3714 if (FAILED(rc))
3715 return rc;
3716
3717 if (state != SessionState_Unlocked)
3718 return setError(VBOX_E_INVALID_OBJECT_STATE,
3719 tr("The given session is busy"));
3720
3721 /* get the IInternalSessionControl interface */
3722 ComPtr<IInternalSessionControl> control(aSession);
3723 ComAssertMsgRet(!control.isNull(),
3724 ("No IInternalSessionControl interface"),
3725 E_INVALIDARG);
3726
3727 /* get the teleporter enable state for the progress object init. */
3728 BOOL fTeleporterEnabled;
3729 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3730 if (FAILED(rc))
3731 return rc;
3732
3733 /* create a progress object */
3734 ComObjPtr<ProgressProxy> progress;
3735 progress.createObject();
3736 rc = progress->init(mParent,
3737 static_cast<IMachine*>(this),
3738 Bstr(tr("Starting VM")).raw(),
3739 TRUE /* aCancelable */,
3740 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3741 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3742 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3743 2 /* uFirstOperationWeight */,
3744 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3745
3746 if (SUCCEEDED(rc))
3747 {
3748 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3749 if (SUCCEEDED(rc))
3750 {
3751 aProgress = progress;
3752
3753 /* signal the client watcher thread */
3754 mParent->i_updateClientWatcher();
3755
3756 /* fire an event */
3757 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3758 }
3759 }
3760 }
3761 else
3762 {
3763 /* no progress object - either instant success or failure */
3764 aProgress = NULL;
3765
3766 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3767
3768 if (mData->mSession.mState != SessionState_Locked)
3769 return setError(VBOX_E_INVALID_OBJECT_STATE,
3770 tr("The machine '%s' is not locked by a session"),
3771 mUserData->s.strName.c_str());
3772
3773 /* must have a VM process associated - do not kill normal API clients
3774 * with an open session */
3775 if (!Global::IsOnline(mData->mMachineState))
3776 return setError(VBOX_E_INVALID_OBJECT_STATE,
3777 tr("The machine '%s' does not have a VM process"),
3778 mUserData->s.strName.c_str());
3779
3780 /* forcibly terminate the VM process */
3781 if (mData->mSession.mPID != NIL_RTPROCESS)
3782 RTProcTerminate(mData->mSession.mPID);
3783
3784 /* signal the client watcher thread, as most likely the client has
3785 * been terminated */
3786 mParent->i_updateClientWatcher();
3787 }
3788
3789 return rc;
3790}
3791
3792HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3793{
3794 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3795 return setError(E_INVALIDARG,
3796 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3797 aPosition, SchemaDefs::MaxBootPosition);
3798
3799 if (aDevice == DeviceType_USB)
3800 return setError(E_NOTIMPL,
3801 tr("Booting from USB device is currently not supported"));
3802
3803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3804
3805 HRESULT rc = i_checkStateDependency(MutableStateDep);
3806 if (FAILED(rc)) return rc;
3807
3808 i_setModified(IsModified_MachineData);
3809 mHWData.backup();
3810 mHWData->mBootOrder[aPosition - 1] = aDevice;
3811
3812 return S_OK;
3813}
3814
3815HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3816{
3817 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3818 return setError(E_INVALIDARG,
3819 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3820 aPosition, SchemaDefs::MaxBootPosition);
3821
3822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3823
3824 *aDevice = mHWData->mBootOrder[aPosition - 1];
3825
3826 return S_OK;
3827}
3828
3829HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3830 LONG aControllerPort,
3831 LONG aDevice,
3832 DeviceType_T aType,
3833 const ComPtr<IMedium> &aMedium)
3834{
3835 IMedium *aM = aMedium;
3836 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3837 aName.c_str(), aControllerPort, aDevice, aType, aM));
3838
3839 // request the host lock first, since might be calling Host methods for getting host drives;
3840 // next, protect the media tree all the while we're in here, as well as our member variables
3841 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3842 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3843
3844 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3845 if (FAILED(rc)) return rc;
3846
3847 /// @todo NEWMEDIA implicit machine registration
3848 if (!mData->mRegistered)
3849 return setError(VBOX_E_INVALID_OBJECT_STATE,
3850 tr("Cannot attach storage devices to an unregistered machine"));
3851
3852 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3853
3854 /* Check for an existing controller. */
3855 ComObjPtr<StorageController> ctl;
3856 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3857 if (FAILED(rc)) return rc;
3858
3859 StorageControllerType_T ctrlType;
3860 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3861 if (FAILED(rc))
3862 return setError(E_FAIL,
3863 tr("Could not get type of controller '%s'"),
3864 aName.c_str());
3865
3866 bool fSilent = false;
3867 Utf8Str strReconfig;
3868
3869 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3870 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3871 if ( mData->mMachineState == MachineState_Paused
3872 && strReconfig == "1")
3873 fSilent = true;
3874
3875 /* Check that the controller can do hot-plugging if we attach the device while the VM is running. */
3876 bool fHotplug = false;
3877 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3878 fHotplug = true;
3879
3880 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3881 return setError(VBOX_E_INVALID_VM_STATE,
3882 tr("Controller '%s' does not support hot-plugging"),
3883 aName.c_str());
3884
3885 /* Attaching a USB device when a VM is powered off should default to being marked as hot-pluggable */
3886 if (!fHotplug && !Global::IsOnlineOrTransient(mData->mMachineState) && ctrlType == StorageControllerType_USB)
3887 fHotplug = true;
3888
3889 // check that the port and device are not out of range
3890 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3891 if (FAILED(rc)) return rc;
3892
3893 /* check if the device slot is already busy */
3894 MediumAttachment *pAttachTemp;
3895 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3896 aName,
3897 aControllerPort,
3898 aDevice)))
3899 {
3900 Medium *pMedium = pAttachTemp->i_getMedium();
3901 if (pMedium)
3902 {
3903 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3904 return setError(VBOX_E_OBJECT_IN_USE,
3905 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3906 pMedium->i_getLocationFull().c_str(),
3907 aControllerPort,
3908 aDevice,
3909 aName.c_str());
3910 }
3911 else
3912 return setError(VBOX_E_OBJECT_IN_USE,
3913 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3914 aControllerPort, aDevice, aName.c_str());
3915 }
3916
3917 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3918 if (aMedium && medium.isNull())
3919 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3920
3921 AutoCaller mediumCaller(medium);
3922 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3923
3924 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3925
3926 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3927 && !medium.isNull()
3928 && ( medium->i_getType() != MediumType_Readonly
3929 || medium->i_getDeviceType() != DeviceType_DVD)
3930 )
3931 return setError(VBOX_E_OBJECT_IN_USE,
3932 tr("Medium '%s' is already attached to this virtual machine"),
3933 medium->i_getLocationFull().c_str());
3934
3935 if (!medium.isNull())
3936 {
3937 MediumType_T mtype = medium->i_getType();
3938 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3939 // For DVDs it's not written to the config file, so needs no global config
3940 // version bump. For floppies it's a new attribute "type", which is ignored
3941 // by older VirtualBox version, so needs no global config version bump either.
3942 // For hard disks this type is not accepted.
3943 if (mtype == MediumType_MultiAttach)
3944 {
3945 // This type is new with VirtualBox 4.0 and therefore requires settings
3946 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3947 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3948 // two reasons: The medium type is a property of the media registry tree, which
3949 // can reside in the global config file (for pre-4.0 media); we would therefore
3950 // possibly need to bump the global config version. We don't want to do that though
3951 // because that might make downgrading to pre-4.0 impossible.
3952 // As a result, we can only use these two new types if the medium is NOT in the
3953 // global registry:
3954 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3955 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3956 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3957 )
3958 return setError(VBOX_E_INVALID_OBJECT_STATE,
3959 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3960 "to machines that were created with VirtualBox 4.0 or later"),
3961 medium->i_getLocationFull().c_str());
3962 }
3963 }
3964
3965 bool fIndirect = false;
3966 if (!medium.isNull())
3967 fIndirect = medium->i_isReadOnly();
3968 bool associate = true;
3969
3970 do
3971 {
3972 if ( aType == DeviceType_HardDisk
3973 && mMediumAttachments.isBackedUp())
3974 {
3975 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3976
3977 /* check if the medium was attached to the VM before we started
3978 * changing attachments in which case the attachment just needs to
3979 * be restored */
3980 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3981 {
3982 AssertReturn(!fIndirect, E_FAIL);
3983
3984 /* see if it's the same bus/channel/device */
3985 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3986 {
3987 /* the simplest case: restore the whole attachment
3988 * and return, nothing else to do */
3989 mMediumAttachments->push_back(pAttachTemp);
3990
3991 /* Reattach the medium to the VM. */
3992 if (fHotplug || fSilent)
3993 {
3994 mediumLock.release();
3995 treeLock.release();
3996 alock.release();
3997
3998 MediumLockList *pMediumLockList(new MediumLockList());
3999
4000 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4001 medium /* pToLockWrite */,
4002 false /* fMediumLockWriteAll */,
4003 NULL,
4004 *pMediumLockList);
4005 alock.acquire();
4006 if (FAILED(rc))
4007 delete pMediumLockList;
4008 else
4009 {
4010 mData->mSession.mLockedMedia.Unlock();
4011 alock.release();
4012 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4013 mData->mSession.mLockedMedia.Lock();
4014 alock.acquire();
4015 }
4016 alock.release();
4017
4018 if (SUCCEEDED(rc))
4019 {
4020 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4021 /* Remove lock list in case of error. */
4022 if (FAILED(rc))
4023 {
4024 mData->mSession.mLockedMedia.Unlock();
4025 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4026 mData->mSession.mLockedMedia.Lock();
4027 }
4028 }
4029 }
4030
4031 return S_OK;
4032 }
4033
4034 /* bus/channel/device differ; we need a new attachment object,
4035 * but don't try to associate it again */
4036 associate = false;
4037 break;
4038 }
4039 }
4040
4041 /* go further only if the attachment is to be indirect */
4042 if (!fIndirect)
4043 break;
4044
4045 /* perform the so called smart attachment logic for indirect
4046 * attachments. Note that smart attachment is only applicable to base
4047 * hard disks. */
4048
4049 if (medium->i_getParent().isNull())
4050 {
4051 /* first, investigate the backup copy of the current hard disk
4052 * attachments to make it possible to re-attach existing diffs to
4053 * another device slot w/o losing their contents */
4054 if (mMediumAttachments.isBackedUp())
4055 {
4056 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4057
4058 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4059 uint32_t foundLevel = 0;
4060
4061 for (MediumAttachmentList::const_iterator
4062 it = oldAtts.begin();
4063 it != oldAtts.end();
4064 ++it)
4065 {
4066 uint32_t level = 0;
4067 MediumAttachment *pAttach = *it;
4068 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4069 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4070 if (pMedium.isNull())
4071 continue;
4072
4073 if (pMedium->i_getBase(&level) == medium)
4074 {
4075 /* skip the hard disk if its currently attached (we
4076 * cannot attach the same hard disk twice) */
4077 if (i_findAttachment(*mMediumAttachments.data(),
4078 pMedium))
4079 continue;
4080
4081 /* matched device, channel and bus (i.e. attached to the
4082 * same place) will win and immediately stop the search;
4083 * otherwise the attachment that has the youngest
4084 * descendant of medium will be used
4085 */
4086 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4087 {
4088 /* the simplest case: restore the whole attachment
4089 * and return, nothing else to do */
4090 mMediumAttachments->push_back(*it);
4091
4092 /* Reattach the medium to the VM. */
4093 if (fHotplug || fSilent)
4094 {
4095 mediumLock.release();
4096 treeLock.release();
4097 alock.release();
4098
4099 MediumLockList *pMediumLockList(new MediumLockList());
4100
4101 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4102 medium /* pToLockWrite */,
4103 false /* fMediumLockWriteAll */,
4104 NULL,
4105 *pMediumLockList);
4106 alock.acquire();
4107 if (FAILED(rc))
4108 delete pMediumLockList;
4109 else
4110 {
4111 mData->mSession.mLockedMedia.Unlock();
4112 alock.release();
4113 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4114 mData->mSession.mLockedMedia.Lock();
4115 alock.acquire();
4116 }
4117 alock.release();
4118
4119 if (SUCCEEDED(rc))
4120 {
4121 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4122 /* Remove lock list in case of error. */
4123 if (FAILED(rc))
4124 {
4125 mData->mSession.mLockedMedia.Unlock();
4126 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4127 mData->mSession.mLockedMedia.Lock();
4128 }
4129 }
4130 }
4131
4132 return S_OK;
4133 }
4134 else if ( foundIt == oldAtts.end()
4135 || level > foundLevel /* prefer younger */
4136 )
4137 {
4138 foundIt = it;
4139 foundLevel = level;
4140 }
4141 }
4142 }
4143
4144 if (foundIt != oldAtts.end())
4145 {
4146 /* use the previously attached hard disk */
4147 medium = (*foundIt)->i_getMedium();
4148 mediumCaller.attach(medium);
4149 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4150 mediumLock.attach(medium);
4151 /* not implicit, doesn't require association with this VM */
4152 fIndirect = false;
4153 associate = false;
4154 /* go right to the MediumAttachment creation */
4155 break;
4156 }
4157 }
4158
4159 /* must give up the medium lock and medium tree lock as below we
4160 * go over snapshots, which needs a lock with higher lock order. */
4161 mediumLock.release();
4162 treeLock.release();
4163
4164 /* then, search through snapshots for the best diff in the given
4165 * hard disk's chain to base the new diff on */
4166
4167 ComObjPtr<Medium> base;
4168 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4169 while (snap)
4170 {
4171 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4172
4173 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4174
4175 MediumAttachment *pAttachFound = NULL;
4176 uint32_t foundLevel = 0;
4177
4178 for (MediumAttachmentList::const_iterator
4179 it = snapAtts.begin();
4180 it != snapAtts.end();
4181 ++it)
4182 {
4183 MediumAttachment *pAttach = *it;
4184 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4185 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4186 if (pMedium.isNull())
4187 continue;
4188
4189 uint32_t level = 0;
4190 if (pMedium->i_getBase(&level) == medium)
4191 {
4192 /* matched device, channel and bus (i.e. attached to the
4193 * same place) will win and immediately stop the search;
4194 * otherwise the attachment that has the youngest
4195 * descendant of medium will be used
4196 */
4197 if ( pAttach->i_getDevice() == aDevice
4198 && pAttach->i_getPort() == aControllerPort
4199 && pAttach->i_getControllerName() == aName
4200 )
4201 {
4202 pAttachFound = pAttach;
4203 break;
4204 }
4205 else if ( !pAttachFound
4206 || level > foundLevel /* prefer younger */
4207 )
4208 {
4209 pAttachFound = pAttach;
4210 foundLevel = level;
4211 }
4212 }
4213 }
4214
4215 if (pAttachFound)
4216 {
4217 base = pAttachFound->i_getMedium();
4218 break;
4219 }
4220
4221 snap = snap->i_getParent();
4222 }
4223
4224 /* re-lock medium tree and the medium, as we need it below */
4225 treeLock.acquire();
4226 mediumLock.acquire();
4227
4228 /* found a suitable diff, use it as a base */
4229 if (!base.isNull())
4230 {
4231 medium = base;
4232 mediumCaller.attach(medium);
4233 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4234 mediumLock.attach(medium);
4235 }
4236 }
4237
4238 Utf8Str strFullSnapshotFolder;
4239 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4240
4241 ComObjPtr<Medium> diff;
4242 diff.createObject();
4243 // store this diff in the same registry as the parent
4244 Guid uuidRegistryParent;
4245 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4246 {
4247 // parent image has no registry: this can happen if we're attaching a new immutable
4248 // image that has not yet been attached (medium then points to the base and we're
4249 // creating the diff image for the immutable, and the parent is not yet registered);
4250 // put the parent in the machine registry then
4251 mediumLock.release();
4252 treeLock.release();
4253 alock.release();
4254 i_addMediumToRegistry(medium);
4255 alock.acquire();
4256 treeLock.acquire();
4257 mediumLock.acquire();
4258 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4259 }
4260 rc = diff->init(mParent,
4261 medium->i_getPreferredDiffFormat(),
4262 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4263 uuidRegistryParent,
4264 DeviceType_HardDisk);
4265 if (FAILED(rc)) return rc;
4266
4267 /* Apply the normal locking logic to the entire chain. */
4268 MediumLockList *pMediumLockList(new MediumLockList());
4269 mediumLock.release();
4270 treeLock.release();
4271 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4272 diff /* pToLockWrite */,
4273 false /* fMediumLockWriteAll */,
4274 medium,
4275 *pMediumLockList);
4276 treeLock.acquire();
4277 mediumLock.acquire();
4278 if (SUCCEEDED(rc))
4279 {
4280 mediumLock.release();
4281 treeLock.release();
4282 rc = pMediumLockList->Lock();
4283 treeLock.acquire();
4284 mediumLock.acquire();
4285 if (FAILED(rc))
4286 setError(rc,
4287 tr("Could not lock medium when creating diff '%s'"),
4288 diff->i_getLocationFull().c_str());
4289 else
4290 {
4291 /* will release the lock before the potentially lengthy
4292 * operation, so protect with the special state */
4293 MachineState_T oldState = mData->mMachineState;
4294 i_setMachineState(MachineState_SettingUp);
4295
4296 mediumLock.release();
4297 treeLock.release();
4298 alock.release();
4299
4300 rc = medium->i_createDiffStorage(diff,
4301 medium->i_getPreferredDiffVariant(),
4302 pMediumLockList,
4303 NULL /* aProgress */,
4304 true /* aWait */,
4305 false /* aNotify */);
4306
4307 alock.acquire();
4308 treeLock.acquire();
4309 mediumLock.acquire();
4310
4311 i_setMachineState(oldState);
4312 }
4313 }
4314
4315 /* Unlock the media and free the associated memory. */
4316 delete pMediumLockList;
4317
4318 if (FAILED(rc)) return rc;
4319
4320 /* use the created diff for the actual attachment */
4321 medium = diff;
4322 mediumCaller.attach(medium);
4323 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4324 mediumLock.attach(medium);
4325 }
4326 while (0);
4327
4328 ComObjPtr<MediumAttachment> attachment;
4329 attachment.createObject();
4330 rc = attachment->init(this,
4331 medium,
4332 aName,
4333 aControllerPort,
4334 aDevice,
4335 aType,
4336 fIndirect,
4337 false /* fPassthrough */,
4338 false /* fTempEject */,
4339 false /* fNonRotational */,
4340 false /* fDiscard */,
4341 fHotplug /* fHotPluggable */,
4342 Utf8Str::Empty);
4343 if (FAILED(rc)) return rc;
4344
4345 if (associate && !medium.isNull())
4346 {
4347 // as the last step, associate the medium to the VM
4348 rc = medium->i_addBackReference(mData->mUuid);
4349 // here we can fail because of Deleting, or being in process of creating a Diff
4350 if (FAILED(rc)) return rc;
4351
4352 mediumLock.release();
4353 treeLock.release();
4354 alock.release();
4355 i_addMediumToRegistry(medium);
4356 alock.acquire();
4357 treeLock.acquire();
4358 mediumLock.acquire();
4359 }
4360
4361 /* success: finally remember the attachment */
4362 i_setModified(IsModified_Storage);
4363 mMediumAttachments.backup();
4364 mMediumAttachments->push_back(attachment);
4365
4366 mediumLock.release();
4367 treeLock.release();
4368 alock.release();
4369
4370 if (fHotplug || fSilent)
4371 {
4372 if (!medium.isNull())
4373 {
4374 MediumLockList *pMediumLockList(new MediumLockList());
4375
4376 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4377 medium /* pToLockWrite */,
4378 false /* fMediumLockWriteAll */,
4379 NULL,
4380 *pMediumLockList);
4381 alock.acquire();
4382 if (FAILED(rc))
4383 delete pMediumLockList;
4384 else
4385 {
4386 mData->mSession.mLockedMedia.Unlock();
4387 alock.release();
4388 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4389 mData->mSession.mLockedMedia.Lock();
4390 alock.acquire();
4391 }
4392 alock.release();
4393 }
4394
4395 if (SUCCEEDED(rc))
4396 {
4397 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4398 /* Remove lock list in case of error. */
4399 if (FAILED(rc))
4400 {
4401 mData->mSession.mLockedMedia.Unlock();
4402 mData->mSession.mLockedMedia.Remove(attachment);
4403 mData->mSession.mLockedMedia.Lock();
4404 }
4405 }
4406 }
4407
4408 /* Save modified registries, but skip this machine as it's the caller's
4409 * job to save its settings like all other settings changes. */
4410 mParent->i_unmarkRegistryModified(i_getId());
4411 mParent->i_saveModifiedRegistries();
4412
4413 if (SUCCEEDED(rc))
4414 {
4415 if (fIndirect && medium != aM)
4416 mParent->i_onMediumConfigChanged(medium);
4417 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4418 }
4419
4420 return rc;
4421}
4422
4423HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4424 LONG aDevice)
4425{
4426 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4427 aName.c_str(), aControllerPort, aDevice));
4428
4429 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4430
4431 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4432 if (FAILED(rc)) return rc;
4433
4434 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4435
4436 /* Check for an existing controller. */
4437 ComObjPtr<StorageController> ctl;
4438 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4439 if (FAILED(rc)) return rc;
4440
4441 StorageControllerType_T ctrlType;
4442 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4443 if (FAILED(rc))
4444 return setError(E_FAIL,
4445 tr("Could not get type of controller '%s'"),
4446 aName.c_str());
4447
4448 bool fSilent = false;
4449 Utf8Str strReconfig;
4450
4451 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4452 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4453 if ( mData->mMachineState == MachineState_Paused
4454 && strReconfig == "1")
4455 fSilent = true;
4456
4457 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4458 bool fHotplug = false;
4459 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4460 fHotplug = true;
4461
4462 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4463 return setError(VBOX_E_INVALID_VM_STATE,
4464 tr("Controller '%s' does not support hot-plugging"),
4465 aName.c_str());
4466
4467 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4468 aName,
4469 aControllerPort,
4470 aDevice);
4471 if (!pAttach)
4472 return setError(VBOX_E_OBJECT_NOT_FOUND,
4473 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4474 aDevice, aControllerPort, aName.c_str());
4475
4476 if (fHotplug && !pAttach->i_getHotPluggable())
4477 return setError(VBOX_E_NOT_SUPPORTED,
4478 tr("The device slot %d on port %d of controller '%s' does not support hot-plugging"),
4479 aDevice, aControllerPort, aName.c_str());
4480
4481 /*
4482 * The VM has to detach the device before we delete any implicit diffs.
4483 * If this fails we can roll back without loosing data.
4484 */
4485 if (fHotplug || fSilent)
4486 {
4487 alock.release();
4488 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4489 alock.acquire();
4490 }
4491 if (FAILED(rc)) return rc;
4492
4493 /* If we are here everything went well and we can delete the implicit now. */
4494 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4495
4496 alock.release();
4497
4498 /* Save modified registries, but skip this machine as it's the caller's
4499 * job to save its settings like all other settings changes. */
4500 mParent->i_unmarkRegistryModified(i_getId());
4501 mParent->i_saveModifiedRegistries();
4502
4503 if (SUCCEEDED(rc))
4504 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4505
4506 return rc;
4507}
4508
4509HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4510 LONG aDevice, BOOL aPassthrough)
4511{
4512 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4513 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4514
4515 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4516
4517 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4518 if (FAILED(rc)) return rc;
4519
4520 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4521
4522 /* Check for an existing controller. */
4523 ComObjPtr<StorageController> ctl;
4524 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4525 if (FAILED(rc)) return rc;
4526
4527 StorageControllerType_T ctrlType;
4528 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4529 if (FAILED(rc))
4530 return setError(E_FAIL,
4531 tr("Could not get type of controller '%s'"),
4532 aName.c_str());
4533
4534 bool fSilent = false;
4535 Utf8Str strReconfig;
4536
4537 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4538 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4539 if ( mData->mMachineState == MachineState_Paused
4540 && strReconfig == "1")
4541 fSilent = true;
4542
4543 /* Check that the controller can do hot-plugging if we detach the device while the VM is running. */
4544 bool fHotplug = false;
4545 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4546 fHotplug = true;
4547
4548 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4549 return setError(VBOX_E_INVALID_VM_STATE,
4550 tr("Controller '%s' does not support hot-plugging which is required to change the passthrough setting while the VM is running"),
4551 aName.c_str());
4552
4553 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4554 aName,
4555 aControllerPort,
4556 aDevice);
4557 if (!pAttach)
4558 return setError(VBOX_E_OBJECT_NOT_FOUND,
4559 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4560 aDevice, aControllerPort, aName.c_str());
4561
4562
4563 i_setModified(IsModified_Storage);
4564 mMediumAttachments.backup();
4565
4566 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4567
4568 if (pAttach->i_getType() != DeviceType_DVD)
4569 return setError(E_INVALIDARG,
4570 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4571 aDevice, aControllerPort, aName.c_str());
4572
4573 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4574
4575 pAttach->i_updatePassthrough(!!aPassthrough);
4576
4577 attLock.release();
4578 alock.release();
4579 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4580 if (SUCCEEDED(rc) && fValueChanged)
4581 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4582
4583 return rc;
4584}
4585
4586HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4587 LONG aDevice, BOOL aTemporaryEject)
4588{
4589
4590 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4591 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4592
4593 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4594
4595 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4596 if (FAILED(rc)) return rc;
4597
4598 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4599 aName,
4600 aControllerPort,
4601 aDevice);
4602 if (!pAttach)
4603 return setError(VBOX_E_OBJECT_NOT_FOUND,
4604 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4605 aDevice, aControllerPort, aName.c_str());
4606
4607
4608 i_setModified(IsModified_Storage);
4609 mMediumAttachments.backup();
4610
4611 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4612
4613 if (pAttach->i_getType() != DeviceType_DVD)
4614 return setError(E_INVALIDARG,
4615 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4616 aDevice, aControllerPort, aName.c_str());
4617 pAttach->i_updateTempEject(!!aTemporaryEject);
4618
4619 return S_OK;
4620}
4621
4622HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4623 LONG aDevice, BOOL aNonRotational)
4624{
4625
4626 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4627 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4628
4629 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4630
4631 HRESULT rc = i_checkStateDependency(MutableStateDep);
4632 if (FAILED(rc)) return rc;
4633
4634 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4635
4636 if (Global::IsOnlineOrTransient(mData->mMachineState))
4637 return setError(VBOX_E_INVALID_VM_STATE,
4638 tr("Invalid machine state: %s"),
4639 Global::stringifyMachineState(mData->mMachineState));
4640
4641 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4642 aName,
4643 aControllerPort,
4644 aDevice);
4645 if (!pAttach)
4646 return setError(VBOX_E_OBJECT_NOT_FOUND,
4647 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4648 aDevice, aControllerPort, aName.c_str());
4649
4650
4651 i_setModified(IsModified_Storage);
4652 mMediumAttachments.backup();
4653
4654 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4655
4656 if (pAttach->i_getType() != DeviceType_HardDisk)
4657 return setError(E_INVALIDARG,
4658 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"),
4659 aDevice, aControllerPort, aName.c_str());
4660 pAttach->i_updateNonRotational(!!aNonRotational);
4661
4662 return S_OK;
4663}
4664
4665HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4666 LONG aDevice, BOOL aDiscard)
4667{
4668
4669 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4670 aName.c_str(), aControllerPort, aDevice, aDiscard));
4671
4672 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4673
4674 HRESULT rc = i_checkStateDependency(MutableStateDep);
4675 if (FAILED(rc)) return rc;
4676
4677 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4678
4679 if (Global::IsOnlineOrTransient(mData->mMachineState))
4680 return setError(VBOX_E_INVALID_VM_STATE,
4681 tr("Invalid machine state: %s"),
4682 Global::stringifyMachineState(mData->mMachineState));
4683
4684 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4685 aName,
4686 aControllerPort,
4687 aDevice);
4688 if (!pAttach)
4689 return setError(VBOX_E_OBJECT_NOT_FOUND,
4690 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4691 aDevice, aControllerPort, aName.c_str());
4692
4693
4694 i_setModified(IsModified_Storage);
4695 mMediumAttachments.backup();
4696
4697 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4698
4699 if (pAttach->i_getType() != DeviceType_HardDisk)
4700 return setError(E_INVALIDARG,
4701 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"),
4702 aDevice, aControllerPort, aName.c_str());
4703 pAttach->i_updateDiscard(!!aDiscard);
4704
4705 return S_OK;
4706}
4707
4708HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4709 LONG aDevice, BOOL aHotPluggable)
4710{
4711 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4712 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4713
4714 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4715
4716 HRESULT rc = i_checkStateDependency(MutableStateDep);
4717 if (FAILED(rc)) return rc;
4718
4719 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4720
4721 if (Global::IsOnlineOrTransient(mData->mMachineState))
4722 return setError(VBOX_E_INVALID_VM_STATE,
4723 tr("Invalid machine state: %s"),
4724 Global::stringifyMachineState(mData->mMachineState));
4725
4726 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4727 aName,
4728 aControllerPort,
4729 aDevice);
4730 if (!pAttach)
4731 return setError(VBOX_E_OBJECT_NOT_FOUND,
4732 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4733 aDevice, aControllerPort, aName.c_str());
4734
4735 /* Check for an existing controller. */
4736 ComObjPtr<StorageController> ctl;
4737 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4738 if (FAILED(rc)) return rc;
4739
4740 StorageControllerType_T ctrlType;
4741 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4742 if (FAILED(rc))
4743 return setError(E_FAIL,
4744 tr("Could not get type of controller '%s'"),
4745 aName.c_str());
4746
4747 if (!i_isControllerHotplugCapable(ctrlType))
4748 return setError(VBOX_E_NOT_SUPPORTED,
4749 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4750 aName.c_str());
4751
4752 /* silently ignore attempts to modify the hot-plug status of USB devices */
4753 if (ctrlType == StorageControllerType_USB)
4754 return S_OK;
4755
4756 i_setModified(IsModified_Storage);
4757 mMediumAttachments.backup();
4758
4759 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4760
4761 if (pAttach->i_getType() == DeviceType_Floppy)
4762 return setError(E_INVALIDARG,
4763 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"),
4764 aDevice, aControllerPort, aName.c_str());
4765 pAttach->i_updateHotPluggable(!!aHotPluggable);
4766
4767 return S_OK;
4768}
4769
4770HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4771 LONG aDevice)
4772{
4773 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4774 aName.c_str(), aControllerPort, aDevice));
4775
4776 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4777}
4778
4779HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4780 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4781{
4782 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4783 aName.c_str(), aControllerPort, aDevice));
4784
4785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4786
4787 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4788 if (FAILED(rc)) return rc;
4789
4790 if (Global::IsOnlineOrTransient(mData->mMachineState))
4791 return setError(VBOX_E_INVALID_VM_STATE,
4792 tr("Invalid machine state: %s"),
4793 Global::stringifyMachineState(mData->mMachineState));
4794
4795 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4796 aName,
4797 aControllerPort,
4798 aDevice);
4799 if (!pAttach)
4800 return setError(VBOX_E_OBJECT_NOT_FOUND,
4801 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4802 aDevice, aControllerPort, aName.c_str());
4803
4804
4805 i_setModified(IsModified_Storage);
4806 mMediumAttachments.backup();
4807
4808 IBandwidthGroup *iB = aBandwidthGroup;
4809 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4810 if (aBandwidthGroup && group.isNull())
4811 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4812
4813 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4814
4815 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4816 if (strBandwidthGroupOld.isNotEmpty())
4817 {
4818 /* Get the bandwidth group object and release it - this must not fail. */
4819 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4820 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4821 Assert(SUCCEEDED(rc));
4822
4823 pBandwidthGroupOld->i_release();
4824 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4825 }
4826
4827 if (!group.isNull())
4828 {
4829 group->i_reference();
4830 pAttach->i_updateBandwidthGroup(group->i_getName());
4831 }
4832
4833 return S_OK;
4834}
4835
4836HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4837 LONG aControllerPort,
4838 LONG aDevice,
4839 DeviceType_T aType)
4840{
4841 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4842 aName.c_str(), aControllerPort, aDevice, aType));
4843
4844 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4845}
4846
4847
4848HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4849 LONG aControllerPort,
4850 LONG aDevice,
4851 BOOL aForce)
4852{
4853 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4854 aName.c_str(), aControllerPort, aForce));
4855
4856 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4857}
4858
4859HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4860 LONG aControllerPort,
4861 LONG aDevice,
4862 const ComPtr<IMedium> &aMedium,
4863 BOOL aForce)
4864{
4865 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4866 aName.c_str(), aControllerPort, aDevice, aForce));
4867
4868 // request the host lock first, since might be calling Host methods for getting host drives;
4869 // next, protect the media tree all the while we're in here, as well as our member variables
4870 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4871 this->lockHandle(),
4872 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4873
4874 HRESULT hrc = i_checkStateDependency(MutableOrRunningStateDep);
4875 if (FAILED(hrc)) return hrc;
4876
4877 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4878 aName,
4879 aControllerPort,
4880 aDevice);
4881 if (pAttach.isNull())
4882 return setError(VBOX_E_OBJECT_NOT_FOUND,
4883 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4884 aDevice, aControllerPort, aName.c_str());
4885
4886 /* Remember previously mounted medium. The medium before taking the
4887 * backup is not necessarily the same thing. */
4888 ComObjPtr<Medium> oldmedium;
4889 oldmedium = pAttach->i_getMedium();
4890
4891 IMedium *iM = aMedium;
4892 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4893 if (aMedium && pMedium.isNull())
4894 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4895
4896 /* Check if potential medium is already mounted */
4897 if (pMedium == oldmedium)
4898 return S_OK;
4899
4900 AutoCaller mediumCaller(pMedium);
4901 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4902
4903 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4904 if (pMedium)
4905 {
4906 DeviceType_T mediumType = pAttach->i_getType();
4907 switch (mediumType)
4908 {
4909 case DeviceType_DVD:
4910 case DeviceType_Floppy:
4911 break;
4912
4913 default:
4914 return setError(VBOX_E_INVALID_OBJECT_STATE,
4915 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4916 aControllerPort,
4917 aDevice,
4918 aName.c_str());
4919 }
4920 }
4921
4922 i_setModified(IsModified_Storage);
4923 mMediumAttachments.backup();
4924
4925 {
4926 // The backup operation makes the pAttach reference point to the
4927 // old settings. Re-get the correct reference.
4928 pAttach = i_findAttachment(*mMediumAttachments.data(),
4929 aName,
4930 aControllerPort,
4931 aDevice);
4932 if (!oldmedium.isNull())
4933 oldmedium->i_removeBackReference(mData->mUuid);
4934 if (!pMedium.isNull())
4935 {
4936 pMedium->i_addBackReference(mData->mUuid);
4937
4938 mediumLock.release();
4939 multiLock.release();
4940 i_addMediumToRegistry(pMedium);
4941 multiLock.acquire();
4942 mediumLock.acquire();
4943 }
4944
4945 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4946 pAttach->i_updateMedium(pMedium);
4947 }
4948
4949 i_setModified(IsModified_Storage);
4950
4951 mediumLock.release();
4952 multiLock.release();
4953 HRESULT rc = i_onMediumChange(pAttach, aForce);
4954 multiLock.acquire();
4955 mediumLock.acquire();
4956
4957 /* On error roll back this change only. */
4958 if (FAILED(rc))
4959 {
4960 if (!pMedium.isNull())
4961 pMedium->i_removeBackReference(mData->mUuid);
4962 pAttach = i_findAttachment(*mMediumAttachments.data(),
4963 aName,
4964 aControllerPort,
4965 aDevice);
4966 /* If the attachment is gone in the meantime, bail out. */
4967 if (pAttach.isNull())
4968 return rc;
4969 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4970 if (!oldmedium.isNull())
4971 oldmedium->i_addBackReference(mData->mUuid);
4972 pAttach->i_updateMedium(oldmedium);
4973 }
4974
4975 mediumLock.release();
4976 multiLock.release();
4977
4978 /* Save modified registries, but skip this machine as it's the caller's
4979 * job to save its settings like all other settings changes. */
4980 mParent->i_unmarkRegistryModified(i_getId());
4981 mParent->i_saveModifiedRegistries();
4982
4983 return rc;
4984}
4985HRESULT Machine::getMedium(const com::Utf8Str &aName,
4986 LONG aControllerPort,
4987 LONG aDevice,
4988 ComPtr<IMedium> &aMedium)
4989{
4990 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4991 aName.c_str(), aControllerPort, aDevice));
4992
4993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4994
4995 aMedium = NULL;
4996
4997 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4998 aName,
4999 aControllerPort,
5000 aDevice);
5001 if (pAttach.isNull())
5002 return setError(VBOX_E_OBJECT_NOT_FOUND,
5003 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5004 aDevice, aControllerPort, aName.c_str());
5005
5006 aMedium = pAttach->i_getMedium();
5007
5008 return S_OK;
5009}
5010
5011HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
5012{
5013 if (aSlot < RT_ELEMENTS(mSerialPorts))
5014 {
5015 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5016 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5017 return S_OK;
5018 }
5019 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
5020}
5021
5022HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
5023{
5024 if (aSlot < RT_ELEMENTS(mParallelPorts))
5025 {
5026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5027 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5028 return S_OK;
5029 }
5030 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
5031}
5032
5033
5034HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
5035{
5036 /* Do not assert if slot is out of range, just return the advertised
5037 status. testdriver/vbox.py triggers this in logVmInfo. */
5038 if (aSlot >= mNetworkAdapters.size())
5039 return setError(E_INVALIDARG,
5040 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
5041 aSlot, mNetworkAdapters.size());
5042
5043 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5044
5045 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
5046
5047 return S_OK;
5048}
5049
5050HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
5051{
5052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5053
5054 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
5055 size_t i = 0;
5056 for (settings::StringsMap::const_iterator
5057 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5058 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5059 ++it, ++i)
5060 aKeys[i] = it->first;
5061
5062 return S_OK;
5063}
5064
5065 /**
5066 * @note Locks this object for reading.
5067 */
5068HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
5069 com::Utf8Str &aValue)
5070{
5071 /* start with nothing found */
5072 aValue = "";
5073
5074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5075
5076 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5077 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5078 // found:
5079 aValue = it->second; // source is a Utf8Str
5080
5081 /* return the result to caller (may be empty) */
5082 return S_OK;
5083}
5084
5085 /**
5086 * @note Locks mParent for writing + this object for writing.
5087 */
5088HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5089{
5090 /* Because control characters in aKey have caused problems in the settings
5091 * they are rejected unless the key should be deleted. */
5092 if (!aValue.isEmpty())
5093 {
5094 for (size_t i = 0; i < aKey.length(); ++i)
5095 {
5096 char ch = aKey[i];
5097 if (RTLocCIsCntrl(ch))
5098 return E_INVALIDARG;
5099 }
5100 }
5101
5102 Utf8Str strOldValue; // empty
5103
5104 // locking note: we only hold the read lock briefly to look up the old value,
5105 // then release it and call the onExtraCanChange callbacks. There is a small
5106 // chance of a race insofar as the callback might be called twice if two callers
5107 // change the same key at the same time, but that's a much better solution
5108 // than the deadlock we had here before. The actual changing of the extradata
5109 // is then performed under the write lock and race-free.
5110
5111 // look up the old value first; if nothing has changed then we need not do anything
5112 {
5113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5114
5115 // For snapshots don't even think about allowing changes, extradata
5116 // is global for a machine, so there is nothing snapshot specific.
5117 if (i_isSnapshotMachine())
5118 return setError(VBOX_E_INVALID_VM_STATE,
5119 tr("Cannot set extradata for a snapshot"));
5120
5121 // check if the right IMachine instance is used
5122 if (mData->mRegistered && !i_isSessionMachine())
5123 return setError(VBOX_E_INVALID_VM_STATE,
5124 tr("Cannot set extradata for an immutable machine"));
5125
5126 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5127 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5128 strOldValue = it->second;
5129 }
5130
5131 bool fChanged;
5132 if ((fChanged = (strOldValue != aValue)))
5133 {
5134 // ask for permission from all listeners outside the locks;
5135 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5136 // lock to copy the list of callbacks to invoke
5137 Bstr bstrError;
5138 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
5139 {
5140 const char *sep = bstrError.isEmpty() ? "" : ": ";
5141 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
5142 return setError(E_ACCESSDENIED,
5143 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5144 aKey.c_str(),
5145 aValue.c_str(),
5146 sep,
5147 bstrError.raw());
5148 }
5149
5150 // data is changing and change not vetoed: then write it out under the lock
5151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5152
5153 if (aValue.isEmpty())
5154 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5155 else
5156 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5157 // creates a new key if needed
5158
5159 bool fNeedsGlobalSaveSettings = false;
5160 // This saving of settings is tricky: there is no "old state" for the
5161 // extradata items at all (unlike all other settings), so the old/new
5162 // settings comparison would give a wrong result!
5163 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
5164
5165 if (fNeedsGlobalSaveSettings)
5166 {
5167 // save the global settings; for that we should hold only the VirtualBox lock
5168 alock.release();
5169 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5170 mParent->i_saveSettings();
5171 }
5172 }
5173
5174 // fire notification outside the lock
5175 if (fChanged)
5176 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
5177
5178 return S_OK;
5179}
5180
5181HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5182{
5183 aProgress = NULL;
5184 NOREF(aSettingsFilePath);
5185 ReturnComNotImplemented();
5186}
5187
5188HRESULT Machine::saveSettings()
5189{
5190 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5191
5192 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5193 if (FAILED(rc)) return rc;
5194
5195 /* the settings file path may never be null */
5196 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5197
5198 /* save all VM data excluding snapshots */
5199 bool fNeedsGlobalSaveSettings = false;
5200 rc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
5201 mlock.release();
5202
5203 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5204 {
5205 // save the global settings; for that we should hold only the VirtualBox lock
5206 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5207 rc = mParent->i_saveSettings();
5208 }
5209
5210 return rc;
5211}
5212
5213
5214HRESULT Machine::discardSettings()
5215{
5216 /*
5217 * We need to take the machine list lock here as well as the machine one
5218 * or we'll get into trouble should any media stuff require rolling back.
5219 *
5220 * Details:
5221 *
5222 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5223 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5224 * 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]
5225 * 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
5226 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5227 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5228 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5229 * 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
5230 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5231 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5232 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5233 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5234 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5235 * 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]
5236 * 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] (*)
5237 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5238 * 0:005> k
5239 * # Child-SP RetAddr Call Site
5240 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5241 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5242 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5243 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5244 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5245 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5246 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5247 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5248 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5249 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5250 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5251 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5252 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5253 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5254 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5255 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5256 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5257 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5258 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5259 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5260 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5261 *
5262 */
5263 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5264 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5265
5266 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5267 if (FAILED(rc)) return rc;
5268
5269 /*
5270 * during this rollback, the session will be notified if data has
5271 * been actually changed
5272 */
5273 i_rollback(true /* aNotify */);
5274
5275 return S_OK;
5276}
5277
5278/** @note Locks objects! */
5279HRESULT Machine::unregister(AutoCaller &autoCaller,
5280 CleanupMode_T aCleanupMode,
5281 std::vector<ComPtr<IMedium> > &aMedia)
5282{
5283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5284
5285 Guid id(i_getId());
5286
5287 if (mData->mSession.mState != SessionState_Unlocked)
5288 return setError(VBOX_E_INVALID_OBJECT_STATE,
5289 tr("Cannot unregister the machine '%s' while it is locked"),
5290 mUserData->s.strName.c_str());
5291
5292 // wait for state dependents to drop to zero
5293 i_ensureNoStateDependencies(alock);
5294
5295 if (!mData->mAccessible)
5296 {
5297 // inaccessible machines can only be unregistered; uninitialize ourselves
5298 // here because currently there may be no unregistered that are inaccessible
5299 // (this state combination is not supported). Note releasing the caller and
5300 // leaving the lock before calling uninit()
5301 alock.release();
5302 autoCaller.release();
5303
5304 uninit();
5305
5306 mParent->i_unregisterMachine(this, CleanupMode_UnregisterOnly, id);
5307 // calls VirtualBox::i_saveSettings()
5308
5309 return S_OK;
5310 }
5311
5312 HRESULT rc = S_OK;
5313 mData->llFilesToDelete.clear();
5314
5315 if (!mSSData->strStateFilePath.isEmpty())
5316 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5317
5318 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
5319 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
5320 mData->llFilesToDelete.push_back(strNVRAMFile);
5321
5322 // This list collects the medium objects from all medium attachments
5323 // which we will detach from the machine and its snapshots, in a specific
5324 // order which allows for closing all media without getting "media in use"
5325 // errors, simply by going through the list from the front to the back:
5326 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5327 // and must be closed before the parent media from the snapshots, or closing the parents
5328 // will fail because they still have children);
5329 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5330 // the root ("first") snapshot of the machine.
5331 MediaList llMedia;
5332
5333 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5334 && mMediumAttachments->size()
5335 )
5336 {
5337 // we have media attachments: detach them all and add the Medium objects to our list
5338 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5339 }
5340
5341 if (mData->mFirstSnapshot)
5342 {
5343 // add the media from the medium attachments of the snapshots to
5344 // llMedia as well, after the "main" machine media;
5345 // Snapshot::uninitAll() calls Machine::detachAllMedia() for each
5346 // snapshot machine, depth first.
5347
5348 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5349 MachineState_T oldState = mData->mMachineState;
5350 mData->mMachineState = MachineState_DeletingSnapshot;
5351
5352 // make a copy of the first snapshot reference so the refcount does not
5353 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
5354 // (would hang due to the AutoCaller voodoo)
5355 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5356
5357 // GO!
5358 pFirstSnapshot->i_uninitAll(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5359
5360 mData->mMachineState = oldState;
5361 }
5362
5363 if (FAILED(rc))
5364 {
5365 i_rollbackMedia();
5366 return rc;
5367 }
5368
5369 // commit all the media changes made above
5370 i_commitMedia();
5371
5372 mData->mRegistered = false;
5373
5374 // machine lock no longer needed
5375 alock.release();
5376
5377 /* Make sure that the settings of the current VM are not saved, because
5378 * they are rather crippled at this point to meet the cleanup expectations
5379 * and there's no point destroying the VM config on disk just because. */
5380 mParent->i_unmarkRegistryModified(id);
5381
5382 // return media to caller
5383 aMedia.resize(llMedia.size());
5384 size_t i = 0;
5385 for (MediaList::const_iterator
5386 it = llMedia.begin();
5387 it != llMedia.end();
5388 ++it, ++i)
5389 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5390
5391 mParent->i_unregisterMachine(this, aCleanupMode, id);
5392 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5393
5394 return S_OK;
5395}
5396
5397/**
5398 * Task record for deleting a machine config.
5399 */
5400class Machine::DeleteConfigTask
5401 : public Machine::Task
5402{
5403public:
5404 DeleteConfigTask(Machine *m,
5405 Progress *p,
5406 const Utf8Str &t,
5407 const RTCList<ComPtr<IMedium> > &llMediums,
5408 const StringsList &llFilesToDelete)
5409 : Task(m, p, t),
5410 m_llMediums(llMediums),
5411 m_llFilesToDelete(llFilesToDelete)
5412 {}
5413
5414private:
5415 void handler()
5416 {
5417 try
5418 {
5419 m_pMachine->i_deleteConfigHandler(*this);
5420 }
5421 catch (...)
5422 {
5423 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5424 }
5425 }
5426
5427 RTCList<ComPtr<IMedium> > m_llMediums;
5428 StringsList m_llFilesToDelete;
5429
5430 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5431};
5432
5433/**
5434 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5435 * SessionMachine::taskHandler().
5436 *
5437 * @note Locks this object for writing.
5438 *
5439 * @param task
5440 * @return
5441 */
5442void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5443{
5444 LogFlowThisFuncEnter();
5445
5446 AutoCaller autoCaller(this);
5447 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5448 if (FAILED(autoCaller.rc()))
5449 {
5450 /* we might have been uninitialized because the session was accidentally
5451 * closed by the client, so don't assert */
5452 HRESULT rc = setError(E_FAIL,
5453 tr("The session has been accidentally closed"));
5454 task.m_pProgress->i_notifyComplete(rc);
5455 LogFlowThisFuncLeave();
5456 return;
5457 }
5458
5459 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5460
5461 HRESULT rc = S_OK;
5462
5463 try
5464 {
5465 ULONG uLogHistoryCount = 3;
5466 ComPtr<ISystemProperties> systemProperties;
5467 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5468 if (FAILED(rc)) throw rc;
5469
5470 if (!systemProperties.isNull())
5471 {
5472 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5473 if (FAILED(rc)) throw rc;
5474 }
5475
5476 MachineState_T oldState = mData->mMachineState;
5477 i_setMachineState(MachineState_SettingUp);
5478 alock.release();
5479 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5480 {
5481 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5482 {
5483 AutoCaller mac(pMedium);
5484 if (FAILED(mac.rc())) throw mac.rc();
5485 Utf8Str strLocation = pMedium->i_getLocationFull();
5486 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5487 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5488 if (FAILED(rc)) throw rc;
5489 }
5490 if (pMedium->i_isMediumFormatFile())
5491 {
5492 ComPtr<IProgress> pProgress2;
5493 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5494 if (FAILED(rc)) throw rc;
5495 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5496 if (FAILED(rc)) throw rc;
5497 }
5498
5499 /* Close the medium, deliberately without checking the return
5500 * code, and without leaving any trace in the error info, as
5501 * a failure here is a very minor issue, which shouldn't happen
5502 * as above we even managed to delete the medium. */
5503 {
5504 ErrorInfoKeeper eik;
5505 pMedium->Close();
5506 }
5507 }
5508 i_setMachineState(oldState);
5509 alock.acquire();
5510
5511 // delete the files pushed on the task list by Machine::Delete()
5512 // (this includes saved states of the machine and snapshots and
5513 // medium storage files from the IMedium list passed in, and the
5514 // machine XML file)
5515 for (StringsList::const_iterator
5516 it = task.m_llFilesToDelete.begin();
5517 it != task.m_llFilesToDelete.end();
5518 ++it)
5519 {
5520 const Utf8Str &strFile = *it;
5521 LogFunc(("Deleting file %s\n", strFile.c_str()));
5522 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5523 if (FAILED(rc)) throw rc;
5524 i_deleteFile(strFile);
5525 }
5526
5527 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5528 if (FAILED(rc)) throw rc;
5529
5530 /* delete the settings only when the file actually exists */
5531 if (mData->pMachineConfigFile->fileExists())
5532 {
5533 /* Delete any backup or uncommitted XML files. Ignore failures.
5534 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5535 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5536 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5537 i_deleteFile(otherXml, true /* fIgnoreFailures */);
5538 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5539 i_deleteFile(otherXml, true /* fIgnoreFailures */);
5540
5541 /* delete the Logs folder, nothing important should be left
5542 * there (we don't check for errors because the user might have
5543 * some private files there that we don't want to delete) */
5544 Utf8Str logFolder;
5545 getLogFolder(logFolder);
5546 Assert(logFolder.length());
5547 if (RTDirExists(logFolder.c_str()))
5548 {
5549 /* Delete all VBox.log[.N] files from the Logs folder
5550 * (this must be in sync with the rotation logic in
5551 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5552 * files that may have been created by the GUI. */
5553 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5554 i_deleteFile(log, true /* fIgnoreFailures */);
5555 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5556 i_deleteFile(log, true /* fIgnoreFailures */);
5557 for (ULONG i = uLogHistoryCount; i > 0; i--)
5558 {
5559 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5560 i_deleteFile(log, true /* fIgnoreFailures */);
5561 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5562 i_deleteFile(log, true /* fIgnoreFailures */);
5563 }
5564 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5565 i_deleteFile(log, true /* fIgnoreFailures */);
5566#if defined(RT_OS_WINDOWS)
5567 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5568 i_deleteFile(log, true /* fIgnoreFailures */);
5569 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5570 i_deleteFile(log, true /* fIgnoreFailures */);
5571#endif
5572
5573 RTDirRemove(logFolder.c_str());
5574 }
5575
5576 /* delete the Snapshots folder, nothing important should be left
5577 * there (we don't check for errors because the user might have
5578 * some private files there that we don't want to delete) */
5579 Utf8Str strFullSnapshotFolder;
5580 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5581 Assert(!strFullSnapshotFolder.isEmpty());
5582 if (RTDirExists(strFullSnapshotFolder.c_str()))
5583 RTDirRemove(strFullSnapshotFolder.c_str());
5584
5585 // delete the directory that contains the settings file, but only
5586 // if it matches the VM name
5587 Utf8Str settingsDir;
5588 if (i_isInOwnDir(&settingsDir))
5589 RTDirRemove(settingsDir.c_str());
5590 }
5591
5592 alock.release();
5593
5594 mParent->i_saveModifiedRegistries();
5595 }
5596 catch (HRESULT aRC) { rc = aRC; }
5597
5598 task.m_pProgress->i_notifyComplete(rc);
5599
5600 LogFlowThisFuncLeave();
5601}
5602
5603HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5604{
5605 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5606
5607 HRESULT rc = i_checkStateDependency(MutableStateDep);
5608 if (FAILED(rc)) return rc;
5609
5610 if (mData->mRegistered)
5611 return setError(VBOX_E_INVALID_VM_STATE,
5612 tr("Cannot delete settings of a registered machine"));
5613
5614 // collect files to delete
5615 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5616 // machine config file
5617 if (mData->pMachineConfigFile->fileExists())
5618 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5619 // backup of machine config file
5620 Utf8Str strTmp(mData->m_strConfigFileFull);
5621 strTmp.append("-prev");
5622 if (RTFileExists(strTmp.c_str()))
5623 llFilesToDelete.push_back(strTmp);
5624
5625 RTCList<ComPtr<IMedium> > llMediums;
5626 for (size_t i = 0; i < aMedia.size(); ++i)
5627 {
5628 IMedium *pIMedium(aMedia[i]);
5629 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5630 if (pMedium.isNull())
5631 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
5632 SafeArray<BSTR> ids;
5633 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5634 if (FAILED(rc)) return rc;
5635 /* At this point the medium should not have any back references
5636 * anymore. If it has it is attached to another VM and *must* not
5637 * deleted. */
5638 if (ids.size() < 1)
5639 llMediums.append(pMedium);
5640 }
5641
5642 ComObjPtr<Progress> pProgress;
5643 pProgress.createObject();
5644 rc = pProgress->init(i_getVirtualBox(),
5645 static_cast<IMachine*>(this) /* aInitiator */,
5646 tr("Deleting files"),
5647 true /* fCancellable */,
5648 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5649 tr("Collecting file inventory"));
5650 if (FAILED(rc))
5651 return rc;
5652
5653 /* create and start the task on a separate thread (note that it will not
5654 * start working until we release alock) */
5655 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5656 rc = pTask->createThread();
5657 pTask = NULL;
5658 if (FAILED(rc))
5659 return rc;
5660
5661 pProgress.queryInterfaceTo(aProgress.asOutParam());
5662
5663 LogFlowFuncLeave();
5664
5665 return S_OK;
5666}
5667
5668HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5669{
5670 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5671
5672 ComObjPtr<Snapshot> pSnapshot;
5673 HRESULT rc;
5674
5675 if (aNameOrId.isEmpty())
5676 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5677 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5678 else
5679 {
5680 Guid uuid(aNameOrId);
5681 if (uuid.isValid())
5682 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5683 else
5684 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5685 }
5686 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5687
5688 return rc;
5689}
5690
5691HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5692 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5693{
5694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5695
5696 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5697 if (FAILED(rc)) return rc;
5698
5699 ComObjPtr<SharedFolder> sharedFolder;
5700 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5701 if (SUCCEEDED(rc))
5702 return setError(VBOX_E_OBJECT_IN_USE,
5703 tr("Shared folder named '%s' already exists"),
5704 aName.c_str());
5705
5706 sharedFolder.createObject();
5707 rc = sharedFolder->init(i_getMachine(),
5708 aName,
5709 aHostPath,
5710 !!aWritable,
5711 !!aAutomount,
5712 aAutoMountPoint,
5713 true /* fFailOnError */);
5714 if (FAILED(rc)) return rc;
5715
5716 i_setModified(IsModified_SharedFolders);
5717 mHWData.backup();
5718 mHWData->mSharedFolders.push_back(sharedFolder);
5719
5720 /* inform the direct session if any */
5721 alock.release();
5722 i_onSharedFolderChange();
5723
5724 return S_OK;
5725}
5726
5727HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5728{
5729 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5730
5731 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5732 if (FAILED(rc)) return rc;
5733
5734 ComObjPtr<SharedFolder> sharedFolder;
5735 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5736 if (FAILED(rc)) return rc;
5737
5738 i_setModified(IsModified_SharedFolders);
5739 mHWData.backup();
5740 mHWData->mSharedFolders.remove(sharedFolder);
5741
5742 /* inform the direct session if any */
5743 alock.release();
5744 i_onSharedFolderChange();
5745
5746 return S_OK;
5747}
5748
5749HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5750{
5751 /* start with No */
5752 *aCanShow = FALSE;
5753
5754 ComPtr<IInternalSessionControl> directControl;
5755 {
5756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5757
5758 if (mData->mSession.mState != SessionState_Locked)
5759 return setError(VBOX_E_INVALID_VM_STATE,
5760 tr("Machine is not locked for session (session state: %s)"),
5761 Global::stringifySessionState(mData->mSession.mState));
5762
5763 if (mData->mSession.mLockType == LockType_VM)
5764 directControl = mData->mSession.mDirectControl;
5765 }
5766
5767 /* ignore calls made after #OnSessionEnd() is called */
5768 if (!directControl)
5769 return S_OK;
5770
5771 LONG64 dummy;
5772 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5773}
5774
5775HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5776{
5777 ComPtr<IInternalSessionControl> directControl;
5778 {
5779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5780
5781 if (mData->mSession.mState != SessionState_Locked)
5782 return setError(E_FAIL,
5783 tr("Machine is not locked for session (session state: %s)"),
5784 Global::stringifySessionState(mData->mSession.mState));
5785
5786 if (mData->mSession.mLockType == LockType_VM)
5787 directControl = mData->mSession.mDirectControl;
5788 }
5789
5790 /* ignore calls made after #OnSessionEnd() is called */
5791 if (!directControl)
5792 return S_OK;
5793
5794 BOOL dummy;
5795 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5796}
5797
5798#ifdef VBOX_WITH_GUEST_PROPS
5799/**
5800 * Look up a guest property in VBoxSVC's internal structures.
5801 */
5802HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5803 com::Utf8Str &aValue,
5804 LONG64 *aTimestamp,
5805 com::Utf8Str &aFlags) const
5806{
5807 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5808
5809 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5810 if (it != mHWData->mGuestProperties.end())
5811 {
5812 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5813 aValue = it->second.strValue;
5814 *aTimestamp = it->second.mTimestamp;
5815 GuestPropWriteFlags(it->second.mFlags, szFlags);
5816 aFlags = Utf8Str(szFlags);
5817 }
5818
5819 return S_OK;
5820}
5821
5822/**
5823 * Query the VM that a guest property belongs to for the property.
5824 * @returns E_ACCESSDENIED if the VM process is not available or not
5825 * currently handling queries and the lookup should then be done in
5826 * VBoxSVC.
5827 */
5828HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5829 com::Utf8Str &aValue,
5830 LONG64 *aTimestamp,
5831 com::Utf8Str &aFlags) const
5832{
5833 HRESULT rc = S_OK;
5834 Bstr bstrValue;
5835 Bstr bstrFlags;
5836
5837 ComPtr<IInternalSessionControl> directControl;
5838 {
5839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5840 if (mData->mSession.mLockType == LockType_VM)
5841 directControl = mData->mSession.mDirectControl;
5842 }
5843
5844 /* ignore calls made after #OnSessionEnd() is called */
5845 if (!directControl)
5846 rc = E_ACCESSDENIED;
5847 else
5848 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5849 0 /* accessMode */,
5850 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5851
5852 aValue = bstrValue;
5853 aFlags = bstrFlags;
5854
5855 return rc;
5856}
5857#endif // VBOX_WITH_GUEST_PROPS
5858
5859HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5860 com::Utf8Str &aValue,
5861 LONG64 *aTimestamp,
5862 com::Utf8Str &aFlags)
5863{
5864#ifndef VBOX_WITH_GUEST_PROPS
5865 ReturnComNotImplemented();
5866#else // VBOX_WITH_GUEST_PROPS
5867
5868 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5869
5870 if (rc == E_ACCESSDENIED)
5871 /* The VM is not running or the service is not (yet) accessible */
5872 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5873 return rc;
5874#endif // VBOX_WITH_GUEST_PROPS
5875}
5876
5877HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5878{
5879 LONG64 dummyTimestamp;
5880 com::Utf8Str dummyFlags;
5881 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5882 return rc;
5883
5884}
5885HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5886{
5887 com::Utf8Str dummyFlags;
5888 com::Utf8Str dummyValue;
5889 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5890 return rc;
5891}
5892
5893#ifdef VBOX_WITH_GUEST_PROPS
5894/**
5895 * Set a guest property in VBoxSVC's internal structures.
5896 */
5897HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5898 const com::Utf8Str &aFlags, bool fDelete)
5899{
5900 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5901 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5902 if (FAILED(rc)) return rc;
5903
5904 try
5905 {
5906 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5907 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5908 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5909
5910 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
5911 return setError(E_INVALIDARG, tr("Properties with TRANSIENT or TRANSRESET flag cannot be set or modified if VM is not running"));
5912
5913 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5914 if (it == mHWData->mGuestProperties.end())
5915 {
5916 if (!fDelete)
5917 {
5918 i_setModified(IsModified_MachineData);
5919 mHWData.backupEx();
5920
5921 RTTIMESPEC time;
5922 HWData::GuestProperty prop;
5923 prop.strValue = aValue;
5924 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5925 prop.mFlags = fFlags;
5926 mHWData->mGuestProperties[aName] = prop;
5927 }
5928 }
5929 else
5930 {
5931 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5932 {
5933 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5934 }
5935 else
5936 {
5937 i_setModified(IsModified_MachineData);
5938 mHWData.backupEx();
5939
5940 /* The backupEx() operation invalidates our iterator,
5941 * so get a new one. */
5942 it = mHWData->mGuestProperties.find(aName);
5943 Assert(it != mHWData->mGuestProperties.end());
5944
5945 if (!fDelete)
5946 {
5947 RTTIMESPEC time;
5948 it->second.strValue = aValue;
5949 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5950 it->second.mFlags = fFlags;
5951 }
5952 else
5953 mHWData->mGuestProperties.erase(it);
5954 }
5955 }
5956
5957 if (SUCCEEDED(rc))
5958 {
5959 alock.release();
5960
5961 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fDelete);
5962 }
5963 }
5964 catch (std::bad_alloc &)
5965 {
5966 rc = E_OUTOFMEMORY;
5967 }
5968
5969 return rc;
5970}
5971
5972/**
5973 * Set a property on the VM that that property belongs to.
5974 * @returns E_ACCESSDENIED if the VM process is not available or not
5975 * currently handling queries and the setting should then be done in
5976 * VBoxSVC.
5977 */
5978HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5979 const com::Utf8Str &aFlags, bool fDelete)
5980{
5981 HRESULT rc;
5982
5983 try
5984 {
5985 ComPtr<IInternalSessionControl> directControl;
5986 {
5987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5988 if (mData->mSession.mLockType == LockType_VM)
5989 directControl = mData->mSession.mDirectControl;
5990 }
5991
5992 Bstr dummy1; /* will not be changed (setter) */
5993 Bstr dummy2; /* will not be changed (setter) */
5994 LONG64 dummy64;
5995 if (!directControl)
5996 rc = E_ACCESSDENIED;
5997 else
5998 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5999 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
6000 fDelete ? 2 : 1 /* accessMode */,
6001 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
6002 }
6003 catch (std::bad_alloc &)
6004 {
6005 rc = E_OUTOFMEMORY;
6006 }
6007
6008 return rc;
6009}
6010#endif // VBOX_WITH_GUEST_PROPS
6011
6012HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
6013 const com::Utf8Str &aFlags)
6014{
6015#ifndef VBOX_WITH_GUEST_PROPS
6016 ReturnComNotImplemented();
6017#else // VBOX_WITH_GUEST_PROPS
6018
6019 int vrc = GuestPropValidateName(aProperty.c_str(), aProperty.length() + 1 /* '\0' */);
6020 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
6021
6022 vrc = GuestPropValidateValue(aValue.c_str(), aValue.length() + 1 /* '\0' */);
6023 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG, vrc));
6024
6025 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
6026 if (rc == E_ACCESSDENIED)
6027 /* The VM is not running or the service is not (yet) accessible */
6028 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
6029 return rc;
6030#endif // VBOX_WITH_GUEST_PROPS
6031}
6032
6033HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
6034{
6035 return setGuestProperty(aProperty, aValue, "");
6036}
6037
6038HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
6039{
6040#ifndef VBOX_WITH_GUEST_PROPS
6041 ReturnComNotImplemented();
6042#else // VBOX_WITH_GUEST_PROPS
6043 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
6044 if (rc == E_ACCESSDENIED)
6045 /* The VM is not running or the service is not (yet) accessible */
6046 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
6047 return rc;
6048#endif // VBOX_WITH_GUEST_PROPS
6049}
6050
6051#ifdef VBOX_WITH_GUEST_PROPS
6052/**
6053 * Enumerate the guest properties in VBoxSVC's internal structures.
6054 */
6055HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
6056 std::vector<com::Utf8Str> &aNames,
6057 std::vector<com::Utf8Str> &aValues,
6058 std::vector<LONG64> &aTimestamps,
6059 std::vector<com::Utf8Str> &aFlags)
6060{
6061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6062 Utf8Str strPatterns(aPatterns);
6063
6064 /*
6065 * Look for matching patterns and build up a list.
6066 */
6067 HWData::GuestPropertyMap propMap;
6068 for (HWData::GuestPropertyMap::const_iterator
6069 it = mHWData->mGuestProperties.begin();
6070 it != mHWData->mGuestProperties.end();
6071 ++it)
6072 {
6073 if ( strPatterns.isEmpty()
6074 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6075 RTSTR_MAX,
6076 it->first.c_str(),
6077 RTSTR_MAX,
6078 NULL)
6079 )
6080 propMap.insert(*it);
6081 }
6082
6083 alock.release();
6084
6085 /*
6086 * And build up the arrays for returning the property information.
6087 */
6088 size_t cEntries = propMap.size();
6089
6090 aNames.resize(cEntries);
6091 aValues.resize(cEntries);
6092 aTimestamps.resize(cEntries);
6093 aFlags.resize(cEntries);
6094
6095 size_t i = 0;
6096 for (HWData::GuestPropertyMap::const_iterator
6097 it = propMap.begin();
6098 it != propMap.end();
6099 ++it, ++i)
6100 {
6101 aNames[i] = it->first;
6102 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
6103 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
6104
6105 aValues[i] = it->second.strValue;
6106 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
6107 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /*bad choice for internal error*/, vrc));
6108
6109 aTimestamps[i] = it->second.mTimestamp;
6110
6111 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6112 GuestPropWriteFlags(it->second.mFlags, szFlags);
6113 aFlags[i] = szFlags;
6114 }
6115
6116 return S_OK;
6117}
6118
6119/**
6120 * Enumerate the properties managed by a VM.
6121 * @returns E_ACCESSDENIED if the VM process is not available or not
6122 * currently handling queries and the setting should then be done in
6123 * VBoxSVC.
6124 */
6125HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6126 std::vector<com::Utf8Str> &aNames,
6127 std::vector<com::Utf8Str> &aValues,
6128 std::vector<LONG64> &aTimestamps,
6129 std::vector<com::Utf8Str> &aFlags)
6130{
6131 HRESULT rc;
6132 ComPtr<IInternalSessionControl> directControl;
6133 {
6134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6135 if (mData->mSession.mLockType == LockType_VM)
6136 directControl = mData->mSession.mDirectControl;
6137 }
6138
6139 com::SafeArray<BSTR> bNames;
6140 com::SafeArray<BSTR> bValues;
6141 com::SafeArray<LONG64> bTimestamps;
6142 com::SafeArray<BSTR> bFlags;
6143
6144 if (!directControl)
6145 rc = E_ACCESSDENIED;
6146 else
6147 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6148 ComSafeArrayAsOutParam(bNames),
6149 ComSafeArrayAsOutParam(bValues),
6150 ComSafeArrayAsOutParam(bTimestamps),
6151 ComSafeArrayAsOutParam(bFlags));
6152 size_t i;
6153 aNames.resize(bNames.size());
6154 for (i = 0; i < bNames.size(); ++i)
6155 aNames[i] = Utf8Str(bNames[i]);
6156 aValues.resize(bValues.size());
6157 for (i = 0; i < bValues.size(); ++i)
6158 aValues[i] = Utf8Str(bValues[i]);
6159 aTimestamps.resize(bTimestamps.size());
6160 for (i = 0; i < bTimestamps.size(); ++i)
6161 aTimestamps[i] = bTimestamps[i];
6162 aFlags.resize(bFlags.size());
6163 for (i = 0; i < bFlags.size(); ++i)
6164 aFlags[i] = Utf8Str(bFlags[i]);
6165
6166 return rc;
6167}
6168#endif // VBOX_WITH_GUEST_PROPS
6169HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6170 std::vector<com::Utf8Str> &aNames,
6171 std::vector<com::Utf8Str> &aValues,
6172 std::vector<LONG64> &aTimestamps,
6173 std::vector<com::Utf8Str> &aFlags)
6174{
6175#ifndef VBOX_WITH_GUEST_PROPS
6176 ReturnComNotImplemented();
6177#else // VBOX_WITH_GUEST_PROPS
6178
6179 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6180
6181 if (rc == E_ACCESSDENIED)
6182 /* The VM is not running or the service is not (yet) accessible */
6183 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6184 return rc;
6185#endif // VBOX_WITH_GUEST_PROPS
6186}
6187
6188HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6189 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6190{
6191 MediumAttachmentList atts;
6192
6193 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6194 if (FAILED(rc)) return rc;
6195
6196 aMediumAttachments.resize(atts.size());
6197 size_t i = 0;
6198 for (MediumAttachmentList::const_iterator
6199 it = atts.begin();
6200 it != atts.end();
6201 ++it, ++i)
6202 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6203
6204 return S_OK;
6205}
6206
6207HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6208 LONG aControllerPort,
6209 LONG aDevice,
6210 ComPtr<IMediumAttachment> &aAttachment)
6211{
6212 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6213 aName.c_str(), aControllerPort, aDevice));
6214
6215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6216
6217 aAttachment = NULL;
6218
6219 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6220 aName,
6221 aControllerPort,
6222 aDevice);
6223 if (pAttach.isNull())
6224 return setError(VBOX_E_OBJECT_NOT_FOUND,
6225 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6226 aDevice, aControllerPort, aName.c_str());
6227
6228 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6229
6230 return S_OK;
6231}
6232
6233
6234HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6235 StorageBus_T aConnectionType,
6236 ComPtr<IStorageController> &aController)
6237{
6238 if ( (aConnectionType <= StorageBus_Null)
6239 || (aConnectionType > StorageBus_VirtioSCSI))
6240 return setError(E_INVALIDARG,
6241 tr("Invalid connection type: %d"),
6242 aConnectionType);
6243
6244 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6245
6246 HRESULT rc = i_checkStateDependency(MutableStateDep);
6247 if (FAILED(rc)) return rc;
6248
6249 /* try to find one with the name first. */
6250 ComObjPtr<StorageController> ctrl;
6251
6252 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6253 if (SUCCEEDED(rc))
6254 return setError(VBOX_E_OBJECT_IN_USE,
6255 tr("Storage controller named '%s' already exists"),
6256 aName.c_str());
6257
6258 ctrl.createObject();
6259
6260 /* get a new instance number for the storage controller */
6261 ULONG ulInstance = 0;
6262 bool fBootable = true;
6263 for (StorageControllerList::const_iterator
6264 it = mStorageControllers->begin();
6265 it != mStorageControllers->end();
6266 ++it)
6267 {
6268 if ((*it)->i_getStorageBus() == aConnectionType)
6269 {
6270 ULONG ulCurInst = (*it)->i_getInstance();
6271
6272 if (ulCurInst >= ulInstance)
6273 ulInstance = ulCurInst + 1;
6274
6275 /* Only one controller of each type can be marked as bootable. */
6276 if ((*it)->i_getBootable())
6277 fBootable = false;
6278 }
6279 }
6280
6281 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6282 if (FAILED(rc)) return rc;
6283
6284 i_setModified(IsModified_Storage);
6285 mStorageControllers.backup();
6286 mStorageControllers->push_back(ctrl);
6287
6288 ctrl.queryInterfaceTo(aController.asOutParam());
6289
6290 /* inform the direct session if any */
6291 alock.release();
6292 i_onStorageControllerChange(i_getId(), aName);
6293
6294 return S_OK;
6295}
6296
6297HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6298 ComPtr<IStorageController> &aStorageController)
6299{
6300 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6301
6302 ComObjPtr<StorageController> ctrl;
6303
6304 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6305 if (SUCCEEDED(rc))
6306 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6307
6308 return rc;
6309}
6310
6311HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6312 ULONG aInstance,
6313 ComPtr<IStorageController> &aStorageController)
6314{
6315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6316
6317 for (StorageControllerList::const_iterator
6318 it = mStorageControllers->begin();
6319 it != mStorageControllers->end();
6320 ++it)
6321 {
6322 if ( (*it)->i_getStorageBus() == aConnectionType
6323 && (*it)->i_getInstance() == aInstance)
6324 {
6325 (*it).queryInterfaceTo(aStorageController.asOutParam());
6326 return S_OK;
6327 }
6328 }
6329
6330 return setError(VBOX_E_OBJECT_NOT_FOUND,
6331 tr("Could not find a storage controller with instance number '%lu'"),
6332 aInstance);
6333}
6334
6335HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6336{
6337 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6338
6339 HRESULT rc = i_checkStateDependency(MutableStateDep);
6340 if (FAILED(rc)) return rc;
6341
6342 ComObjPtr<StorageController> ctrl;
6343
6344 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6345 if (SUCCEEDED(rc))
6346 {
6347 /* Ensure that only one controller of each type is marked as bootable. */
6348 if (aBootable == TRUE)
6349 {
6350 for (StorageControllerList::const_iterator
6351 it = mStorageControllers->begin();
6352 it != mStorageControllers->end();
6353 ++it)
6354 {
6355 ComObjPtr<StorageController> aCtrl = (*it);
6356
6357 if ( (aCtrl->i_getName() != aName)
6358 && aCtrl->i_getBootable() == TRUE
6359 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6360 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6361 {
6362 aCtrl->i_setBootable(FALSE);
6363 break;
6364 }
6365 }
6366 }
6367
6368 if (SUCCEEDED(rc))
6369 {
6370 ctrl->i_setBootable(aBootable);
6371 i_setModified(IsModified_Storage);
6372 }
6373 }
6374
6375 if (SUCCEEDED(rc))
6376 {
6377 /* inform the direct session if any */
6378 alock.release();
6379 i_onStorageControllerChange(i_getId(), aName);
6380 }
6381
6382 return rc;
6383}
6384
6385HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6386{
6387 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6388
6389 HRESULT rc = i_checkStateDependency(MutableStateDep);
6390 if (FAILED(rc)) return rc;
6391
6392 ComObjPtr<StorageController> ctrl;
6393 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6394 if (FAILED(rc)) return rc;
6395
6396 MediumAttachmentList llDetachedAttachments;
6397 {
6398 /* find all attached devices to the appropriate storage controller and detach them all */
6399 // make a temporary list because detachDevice invalidates iterators into
6400 // mMediumAttachments
6401 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6402
6403 for (MediumAttachmentList::const_iterator
6404 it = llAttachments2.begin();
6405 it != llAttachments2.end();
6406 ++it)
6407 {
6408 MediumAttachment *pAttachTemp = *it;
6409
6410 AutoCaller localAutoCaller(pAttachTemp);
6411 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6412
6413 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6414
6415 if (pAttachTemp->i_getControllerName() == aName)
6416 {
6417 llDetachedAttachments.push_back(pAttachTemp);
6418 rc = i_detachDevice(pAttachTemp, alock, NULL);
6419 if (FAILED(rc)) return rc;
6420 }
6421 }
6422 }
6423
6424 /* send event about detached devices before removing parent controller */
6425 for (MediumAttachmentList::const_iterator
6426 it = llDetachedAttachments.begin();
6427 it != llDetachedAttachments.end();
6428 ++it)
6429 {
6430 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6431 }
6432
6433 /* We can remove it now. */
6434 i_setModified(IsModified_Storage);
6435 mStorageControllers.backup();
6436
6437 ctrl->i_unshare();
6438
6439 mStorageControllers->remove(ctrl);
6440
6441 /* inform the direct session if any */
6442 alock.release();
6443 i_onStorageControllerChange(i_getId(), aName);
6444
6445 return S_OK;
6446}
6447
6448HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6449 ComPtr<IUSBController> &aController)
6450{
6451 if ( (aType <= USBControllerType_Null)
6452 || (aType >= USBControllerType_Last))
6453 return setError(E_INVALIDARG,
6454 tr("Invalid USB controller type: %d"),
6455 aType);
6456
6457 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6458
6459 HRESULT rc = i_checkStateDependency(MutableStateDep);
6460 if (FAILED(rc)) return rc;
6461
6462 /* try to find one with the same type first. */
6463 ComObjPtr<USBController> ctrl;
6464
6465 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6466 if (SUCCEEDED(rc))
6467 return setError(VBOX_E_OBJECT_IN_USE,
6468 tr("USB controller named '%s' already exists"),
6469 aName.c_str());
6470
6471 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6472 ULONG maxInstances;
6473 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6474 if (FAILED(rc))
6475 return rc;
6476
6477 ULONG cInstances = i_getUSBControllerCountByType(aType);
6478 if (cInstances >= maxInstances)
6479 return setError(E_INVALIDARG,
6480 tr("Too many USB controllers of this type"));
6481
6482 ctrl.createObject();
6483
6484 rc = ctrl->init(this, aName, aType);
6485 if (FAILED(rc)) return rc;
6486
6487 i_setModified(IsModified_USB);
6488 mUSBControllers.backup();
6489 mUSBControllers->push_back(ctrl);
6490
6491 ctrl.queryInterfaceTo(aController.asOutParam());
6492
6493 /* inform the direct session if any */
6494 alock.release();
6495 i_onUSBControllerChange();
6496
6497 return S_OK;
6498}
6499
6500HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6501{
6502 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6503
6504 ComObjPtr<USBController> ctrl;
6505
6506 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6507 if (SUCCEEDED(rc))
6508 ctrl.queryInterfaceTo(aController.asOutParam());
6509
6510 return rc;
6511}
6512
6513HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6514 ULONG *aControllers)
6515{
6516 if ( (aType <= USBControllerType_Null)
6517 || (aType >= USBControllerType_Last))
6518 return setError(E_INVALIDARG,
6519 tr("Invalid USB controller type: %d"),
6520 aType);
6521
6522 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6523
6524 ComObjPtr<USBController> ctrl;
6525
6526 *aControllers = i_getUSBControllerCountByType(aType);
6527
6528 return S_OK;
6529}
6530
6531HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6532{
6533
6534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6535
6536 HRESULT rc = i_checkStateDependency(MutableStateDep);
6537 if (FAILED(rc)) return rc;
6538
6539 ComObjPtr<USBController> ctrl;
6540 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6541 if (FAILED(rc)) return rc;
6542
6543 i_setModified(IsModified_USB);
6544 mUSBControllers.backup();
6545
6546 ctrl->i_unshare();
6547
6548 mUSBControllers->remove(ctrl);
6549
6550 /* inform the direct session if any */
6551 alock.release();
6552 i_onUSBControllerChange();
6553
6554 return S_OK;
6555}
6556
6557HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6558 ULONG *aOriginX,
6559 ULONG *aOriginY,
6560 ULONG *aWidth,
6561 ULONG *aHeight,
6562 BOOL *aEnabled)
6563{
6564 uint32_t u32OriginX= 0;
6565 uint32_t u32OriginY= 0;
6566 uint32_t u32Width = 0;
6567 uint32_t u32Height = 0;
6568 uint16_t u16Flags = 0;
6569
6570#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6571 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6572#else
6573 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6574#endif
6575 int vrc = readSavedGuestScreenInfo(SavedStateStream, mSSData->strStateFilePath, aScreenId,
6576 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6577 if (RT_FAILURE(vrc))
6578 {
6579#ifdef RT_OS_WINDOWS
6580 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6581 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6582 * So just assign fEnable to TRUE again.
6583 * The right fix would be to change GUI API wrappers to make sure that parameters
6584 * are changed only if API succeeds.
6585 */
6586 *aEnabled = TRUE;
6587#endif
6588 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6589 tr("Saved guest size is not available (%Rrc)"),
6590 vrc);
6591 }
6592
6593 *aOriginX = u32OriginX;
6594 *aOriginY = u32OriginY;
6595 *aWidth = u32Width;
6596 *aHeight = u32Height;
6597 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6598
6599 return S_OK;
6600}
6601
6602HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6603 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6604{
6605 if (aScreenId != 0)
6606 return E_NOTIMPL;
6607
6608 if ( aBitmapFormat != BitmapFormat_BGR0
6609 && aBitmapFormat != BitmapFormat_BGRA
6610 && aBitmapFormat != BitmapFormat_RGBA
6611 && aBitmapFormat != BitmapFormat_PNG)
6612 return setError(E_NOTIMPL,
6613 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6614
6615 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6616
6617 uint8_t *pu8Data = NULL;
6618 uint32_t cbData = 0;
6619 uint32_t u32Width = 0;
6620 uint32_t u32Height = 0;
6621
6622#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6623 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6624#else
6625 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6626#endif
6627 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 0 /* u32Type */,
6628 &pu8Data, &cbData, &u32Width, &u32Height);
6629 if (RT_FAILURE(vrc))
6630 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6631 tr("Saved thumbnail data is not available (%Rrc)"),
6632 vrc);
6633
6634 HRESULT hr = S_OK;
6635
6636 *aWidth = u32Width;
6637 *aHeight = u32Height;
6638
6639 if (cbData > 0)
6640 {
6641 /* Convert pixels to the format expected by the API caller. */
6642 if (aBitmapFormat == BitmapFormat_BGR0)
6643 {
6644 /* [0] B, [1] G, [2] R, [3] 0. */
6645 aData.resize(cbData);
6646 memcpy(&aData.front(), pu8Data, cbData);
6647 }
6648 else if (aBitmapFormat == BitmapFormat_BGRA)
6649 {
6650 /* [0] B, [1] G, [2] R, [3] A. */
6651 aData.resize(cbData);
6652 for (uint32_t i = 0; i < cbData; i += 4)
6653 {
6654 aData[i] = pu8Data[i];
6655 aData[i + 1] = pu8Data[i + 1];
6656 aData[i + 2] = pu8Data[i + 2];
6657 aData[i + 3] = 0xff;
6658 }
6659 }
6660 else if (aBitmapFormat == BitmapFormat_RGBA)
6661 {
6662 /* [0] R, [1] G, [2] B, [3] A. */
6663 aData.resize(cbData);
6664 for (uint32_t i = 0; i < cbData; i += 4)
6665 {
6666 aData[i] = pu8Data[i + 2];
6667 aData[i + 1] = pu8Data[i + 1];
6668 aData[i + 2] = pu8Data[i];
6669 aData[i + 3] = 0xff;
6670 }
6671 }
6672 else if (aBitmapFormat == BitmapFormat_PNG)
6673 {
6674 uint8_t *pu8PNG = NULL;
6675 uint32_t cbPNG = 0;
6676 uint32_t cxPNG = 0;
6677 uint32_t cyPNG = 0;
6678
6679 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6680
6681 if (RT_SUCCESS(vrc))
6682 {
6683 aData.resize(cbPNG);
6684 if (cbPNG)
6685 memcpy(&aData.front(), pu8PNG, cbPNG);
6686 }
6687 else
6688 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6689 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6690 vrc);
6691
6692 RTMemFree(pu8PNG);
6693 }
6694 }
6695
6696 freeSavedDisplayScreenshot(pu8Data);
6697
6698 return hr;
6699}
6700
6701HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6702 ULONG *aWidth,
6703 ULONG *aHeight,
6704 std::vector<BitmapFormat_T> &aBitmapFormats)
6705{
6706 if (aScreenId != 0)
6707 return E_NOTIMPL;
6708
6709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6710
6711 uint8_t *pu8Data = NULL;
6712 uint32_t cbData = 0;
6713 uint32_t u32Width = 0;
6714 uint32_t u32Height = 0;
6715
6716#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6717 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6718#else
6719 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6720#endif
6721 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6722 &pu8Data, &cbData, &u32Width, &u32Height);
6723
6724 if (RT_FAILURE(vrc))
6725 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6726 tr("Saved screenshot data is not available (%Rrc)"),
6727 vrc);
6728
6729 *aWidth = u32Width;
6730 *aHeight = u32Height;
6731 aBitmapFormats.resize(1);
6732 aBitmapFormats[0] = BitmapFormat_PNG;
6733
6734 freeSavedDisplayScreenshot(pu8Data);
6735
6736 return S_OK;
6737}
6738
6739HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6740 BitmapFormat_T aBitmapFormat,
6741 ULONG *aWidth,
6742 ULONG *aHeight,
6743 std::vector<BYTE> &aData)
6744{
6745 if (aScreenId != 0)
6746 return E_NOTIMPL;
6747
6748 if (aBitmapFormat != BitmapFormat_PNG)
6749 return E_NOTIMPL;
6750
6751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6752
6753 uint8_t *pu8Data = NULL;
6754 uint32_t cbData = 0;
6755 uint32_t u32Width = 0;
6756 uint32_t u32Height = 0;
6757
6758#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6759 SsmStream SavedStateStream(mParent, mData->mpKeyStore, mSSData->strStateKeyId, mSSData->strStateKeyStore);
6760#else
6761 SsmStream SavedStateStream(mParent, NULL /*pKeyStore*/, Utf8Str::Empty, Utf8Str::Empty);
6762#endif
6763 int vrc = readSavedDisplayScreenshot(SavedStateStream, mSSData->strStateFilePath, 1 /* u32Type */,
6764 &pu8Data, &cbData, &u32Width, &u32Height);
6765
6766 if (RT_FAILURE(vrc))
6767 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6768 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6769 vrc);
6770
6771 *aWidth = u32Width;
6772 *aHeight = u32Height;
6773
6774 aData.resize(cbData);
6775 if (cbData)
6776 memcpy(&aData.front(), pu8Data, cbData);
6777
6778 freeSavedDisplayScreenshot(pu8Data);
6779
6780 return S_OK;
6781}
6782
6783HRESULT Machine::hotPlugCPU(ULONG aCpu)
6784{
6785 HRESULT rc = S_OK;
6786 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6787
6788 if (!mHWData->mCPUHotPlugEnabled)
6789 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6790
6791 if (aCpu >= mHWData->mCPUCount)
6792 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6793
6794 if (mHWData->mCPUAttached[aCpu])
6795 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6796
6797 rc = i_checkStateDependency(MutableOrRunningStateDep);
6798 if (FAILED(rc)) return rc;
6799
6800 alock.release();
6801 rc = i_onCPUChange(aCpu, false);
6802 alock.acquire();
6803 if (FAILED(rc)) return rc;
6804
6805 i_setModified(IsModified_MachineData);
6806 mHWData.backup();
6807 mHWData->mCPUAttached[aCpu] = true;
6808
6809 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6810 if (Global::IsOnline(mData->mMachineState))
6811 i_saveSettings(NULL, alock);
6812
6813 return S_OK;
6814}
6815
6816HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6817{
6818 HRESULT rc = S_OK;
6819
6820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6821
6822 if (!mHWData->mCPUHotPlugEnabled)
6823 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6824
6825 if (aCpu >= SchemaDefs::MaxCPUCount)
6826 return setError(E_INVALIDARG,
6827 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6828 SchemaDefs::MaxCPUCount);
6829
6830 if (!mHWData->mCPUAttached[aCpu])
6831 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6832
6833 /* CPU 0 can't be detached */
6834 if (aCpu == 0)
6835 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6836
6837 rc = i_checkStateDependency(MutableOrRunningStateDep);
6838 if (FAILED(rc)) return rc;
6839
6840 alock.release();
6841 rc = i_onCPUChange(aCpu, true);
6842 alock.acquire();
6843 if (FAILED(rc)) return rc;
6844
6845 i_setModified(IsModified_MachineData);
6846 mHWData.backup();
6847 mHWData->mCPUAttached[aCpu] = false;
6848
6849 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6850 if (Global::IsOnline(mData->mMachineState))
6851 i_saveSettings(NULL, alock);
6852
6853 return S_OK;
6854}
6855
6856HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6857{
6858 *aAttached = false;
6859
6860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6861
6862 /* If hotplug is enabled the CPU is always enabled. */
6863 if (!mHWData->mCPUHotPlugEnabled)
6864 {
6865 if (aCpu < mHWData->mCPUCount)
6866 *aAttached = true;
6867 }
6868 else
6869 {
6870 if (aCpu < SchemaDefs::MaxCPUCount)
6871 *aAttached = mHWData->mCPUAttached[aCpu];
6872 }
6873
6874 return S_OK;
6875}
6876
6877HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6878{
6879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6880
6881 Utf8Str log = i_getLogFilename(aIdx);
6882 if (!RTFileExists(log.c_str()))
6883 log.setNull();
6884 aFilename = log;
6885
6886 return S_OK;
6887}
6888
6889HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6890{
6891 if (aSize < 0)
6892 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6893
6894 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6895
6896 HRESULT rc = S_OK;
6897 Utf8Str log = i_getLogFilename(aIdx);
6898
6899 /* do not unnecessarily hold the lock while doing something which does
6900 * not need the lock and potentially takes a long time. */
6901 alock.release();
6902
6903 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6904 * keeps the SOAP reply size under 1M for the webservice (we're using
6905 * base64 encoded strings for binary data for years now, avoiding the
6906 * expansion of each byte array element to approx. 25 bytes of XML. */
6907 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6908 aData.resize(cbData);
6909
6910 int vrc = VINF_SUCCESS;
6911 RTVFSIOSTREAM hVfsIosLog = NIL_RTVFSIOSTREAM;
6912
6913#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
6914 if (mData->mstrLogKeyId.isNotEmpty() && mData->mstrLogKeyStore.isNotEmpty())
6915 {
6916 PCVBOXCRYPTOIF pCryptoIf = NULL;
6917 rc = i_getVirtualBox()->i_retainCryptoIf(&pCryptoIf);
6918 if (SUCCEEDED(rc))
6919 {
6920 alock.acquire();
6921
6922 SecretKey *pKey = NULL;
6923 vrc = mData->mpKeyStore->retainSecretKey(mData->mstrLogKeyId, &pKey);
6924 alock.release();
6925
6926 if (RT_SUCCESS(vrc))
6927 {
6928 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6929 if (RT_SUCCESS(vrc))
6930 {
6931 RTVFSIOSTREAM hVfsIosLogDec = NIL_RTVFSIOSTREAM;
6932 vrc = pCryptoIf->pfnCryptoIoStrmFromVfsIoStrmDecrypt(hVfsIosLog, mData->mstrLogKeyStore.c_str(),
6933 (const char *)pKey->getKeyBuffer(), &hVfsIosLogDec);
6934 if (RT_SUCCESS(vrc))
6935 {
6936 RTVfsIoStrmRelease(hVfsIosLog);
6937 hVfsIosLog = hVfsIosLogDec;
6938 }
6939 }
6940
6941 pKey->release();
6942 }
6943
6944 i_getVirtualBox()->i_releaseCryptoIf(pCryptoIf);
6945 }
6946 }
6947 else
6948 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6949#else
6950 vrc = RTVfsIoStrmOpenNormal(log.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, &hVfsIosLog);
6951#endif
6952 if (RT_SUCCESS(vrc))
6953 {
6954 vrc = RTVfsIoStrmReadAt(hVfsIosLog, aOffset,
6955 cbData ? &aData.front() : NULL, cbData,
6956 true /*fBlocking*/, &cbData);
6957 if (RT_SUCCESS(vrc))
6958 aData.resize(cbData);
6959 else
6960 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6961 tr("Could not read log file '%s' (%Rrc)"),
6962 log.c_str(), vrc);
6963
6964 RTVfsIoStrmRelease(hVfsIosLog);
6965 }
6966 else
6967 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6968 tr("Could not open log file '%s' (%Rrc)"),
6969 log.c_str(), vrc);
6970
6971 if (FAILED(rc))
6972 aData.resize(0);
6973
6974 return rc;
6975}
6976
6977
6978/**
6979 * Currently this method doesn't attach device to the running VM,
6980 * just makes sure it's plugged on next VM start.
6981 */
6982HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6983{
6984 // lock scope
6985 {
6986 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6987
6988 HRESULT rc = i_checkStateDependency(MutableStateDep);
6989 if (FAILED(rc)) return rc;
6990
6991 ChipsetType_T aChipset = ChipsetType_PIIX3;
6992 COMGETTER(ChipsetType)(&aChipset);
6993
6994 if (aChipset != ChipsetType_ICH9)
6995 {
6996 return setError(E_INVALIDARG,
6997 tr("Host PCI attachment only supported with ICH9 chipset"));
6998 }
6999
7000 // check if device with this host PCI address already attached
7001 for (HWData::PCIDeviceAssignmentList::const_iterator
7002 it = mHWData->mPCIDeviceAssignments.begin();
7003 it != mHWData->mPCIDeviceAssignments.end();
7004 ++it)
7005 {
7006 LONG iHostAddress = -1;
7007 ComPtr<PCIDeviceAttachment> pAttach;
7008 pAttach = *it;
7009 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7010 if (iHostAddress == aHostAddress)
7011 return setError(E_INVALIDARG,
7012 tr("Device with host PCI address already attached to this VM"));
7013 }
7014
7015 ComObjPtr<PCIDeviceAttachment> pda;
7016 char name[32];
7017
7018 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
7019 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
7020 pda.createObject();
7021 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
7022 i_setModified(IsModified_MachineData);
7023 mHWData.backup();
7024 mHWData->mPCIDeviceAssignments.push_back(pda);
7025 }
7026
7027 return S_OK;
7028}
7029
7030/**
7031 * Currently this method doesn't detach device from the running VM,
7032 * just makes sure it's not plugged on next VM start.
7033 */
7034HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
7035{
7036 ComObjPtr<PCIDeviceAttachment> pAttach;
7037 bool fRemoved = false;
7038 HRESULT rc;
7039
7040 // lock scope
7041 {
7042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7043
7044 rc = i_checkStateDependency(MutableStateDep);
7045 if (FAILED(rc)) return rc;
7046
7047 for (HWData::PCIDeviceAssignmentList::const_iterator
7048 it = mHWData->mPCIDeviceAssignments.begin();
7049 it != mHWData->mPCIDeviceAssignments.end();
7050 ++it)
7051 {
7052 LONG iHostAddress = -1;
7053 pAttach = *it;
7054 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7055 if (iHostAddress != -1 && iHostAddress == aHostAddress)
7056 {
7057 i_setModified(IsModified_MachineData);
7058 mHWData.backup();
7059 mHWData->mPCIDeviceAssignments.remove(pAttach);
7060 fRemoved = true;
7061 break;
7062 }
7063 }
7064 }
7065
7066
7067 /* Fire event outside of the lock */
7068 if (fRemoved)
7069 {
7070 Assert(!pAttach.isNull());
7071 ComPtr<IEventSource> es;
7072 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7073 Assert(SUCCEEDED(rc));
7074 Bstr mid;
7075 rc = this->COMGETTER(Id)(mid.asOutParam());
7076 Assert(SUCCEEDED(rc));
7077 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7078 }
7079
7080 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7081 tr("No host PCI device %08x attached"),
7082 aHostAddress
7083 );
7084}
7085
7086HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
7087{
7088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7089
7090 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
7091 size_t i = 0;
7092 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
7093 it = mHWData->mPCIDeviceAssignments.begin();
7094 it != mHWData->mPCIDeviceAssignments.end();
7095 ++it, ++i)
7096 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
7097
7098 return S_OK;
7099}
7100
7101HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
7102{
7103 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
7104
7105 return S_OK;
7106}
7107
7108HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
7109{
7110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7111
7112 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
7113
7114 return S_OK;
7115}
7116
7117HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
7118{
7119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7120 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7121 if (SUCCEEDED(hrc))
7122 {
7123 hrc = mHWData.backupEx();
7124 if (SUCCEEDED(hrc))
7125 {
7126 i_setModified(IsModified_MachineData);
7127 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
7128 }
7129 }
7130 return hrc;
7131}
7132
7133HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
7134{
7135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7136 aTracingConfig = mHWData->mDebugging.strTracingConfig;
7137 return S_OK;
7138}
7139
7140HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
7141{
7142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7143 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7144 if (SUCCEEDED(hrc))
7145 {
7146 hrc = mHWData.backupEx();
7147 if (SUCCEEDED(hrc))
7148 {
7149 mHWData->mDebugging.strTracingConfig = aTracingConfig;
7150 if (SUCCEEDED(hrc))
7151 i_setModified(IsModified_MachineData);
7152 }
7153 }
7154 return hrc;
7155}
7156
7157HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
7158{
7159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7160
7161 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
7162
7163 return S_OK;
7164}
7165
7166HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
7167{
7168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7169 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7170 if (SUCCEEDED(hrc))
7171 {
7172 hrc = mHWData.backupEx();
7173 if (SUCCEEDED(hrc))
7174 {
7175 i_setModified(IsModified_MachineData);
7176 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
7177 }
7178 }
7179 return hrc;
7180}
7181
7182HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7183{
7184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7185
7186 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7187
7188 return S_OK;
7189}
7190
7191HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7192{
7193 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7194
7195 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7196 if ( SUCCEEDED(hrc)
7197 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7198 {
7199 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7200 int vrc;
7201
7202 if (aAutostartEnabled)
7203 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7204 else
7205 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7206
7207 if (RT_SUCCESS(vrc))
7208 {
7209 hrc = mHWData.backupEx();
7210 if (SUCCEEDED(hrc))
7211 {
7212 i_setModified(IsModified_MachineData);
7213 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7214 }
7215 }
7216 else if (vrc == VERR_NOT_SUPPORTED)
7217 hrc = setError(VBOX_E_NOT_SUPPORTED,
7218 tr("The VM autostart feature is not supported on this platform"));
7219 else if (vrc == VERR_PATH_NOT_FOUND)
7220 hrc = setError(E_FAIL,
7221 tr("The path to the autostart database is not set"));
7222 else
7223 hrc = setError(E_UNEXPECTED,
7224 aAutostartEnabled ?
7225 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
7226 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
7227 mUserData->s.strName.c_str(), vrc);
7228 }
7229 return hrc;
7230}
7231
7232HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7233{
7234 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7235
7236 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7237
7238 return S_OK;
7239}
7240
7241HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7242{
7243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7244 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7245 if (SUCCEEDED(hrc))
7246 {
7247 hrc = mHWData.backupEx();
7248 if (SUCCEEDED(hrc))
7249 {
7250 i_setModified(IsModified_MachineData);
7251 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7252 }
7253 }
7254 return hrc;
7255}
7256
7257HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7258{
7259 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7260
7261 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7262
7263 return S_OK;
7264}
7265
7266HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7267{
7268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7269 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7270 if ( SUCCEEDED(hrc)
7271 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7272 {
7273 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7274 int vrc;
7275
7276 if (aAutostopType != AutostopType_Disabled)
7277 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7278 else
7279 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7280
7281 if (RT_SUCCESS(vrc))
7282 {
7283 hrc = mHWData.backupEx();
7284 if (SUCCEEDED(hrc))
7285 {
7286 i_setModified(IsModified_MachineData);
7287 mHWData->mAutostart.enmAutostopType = aAutostopType;
7288 }
7289 }
7290 else if (vrc == VERR_NOT_SUPPORTED)
7291 hrc = setError(VBOX_E_NOT_SUPPORTED,
7292 tr("The VM autostop feature is not supported on this platform"));
7293 else if (vrc == VERR_PATH_NOT_FOUND)
7294 hrc = setError(E_FAIL,
7295 tr("The path to the autostart database is not set"));
7296 else
7297 hrc = setError(E_UNEXPECTED,
7298 aAutostopType != AutostopType_Disabled ?
7299 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
7300 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
7301 mUserData->s.strName.c_str(), vrc);
7302 }
7303 return hrc;
7304}
7305
7306HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7307{
7308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7309
7310 aDefaultFrontend = mHWData->mDefaultFrontend;
7311
7312 return S_OK;
7313}
7314
7315HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7316{
7317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7318 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7319 if (SUCCEEDED(hrc))
7320 {
7321 hrc = mHWData.backupEx();
7322 if (SUCCEEDED(hrc))
7323 {
7324 i_setModified(IsModified_MachineData);
7325 mHWData->mDefaultFrontend = aDefaultFrontend;
7326 }
7327 }
7328 return hrc;
7329}
7330
7331HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7332{
7333 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7334 size_t cbIcon = mUserData->s.ovIcon.size();
7335 aIcon.resize(cbIcon);
7336 if (cbIcon)
7337 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7338 return S_OK;
7339}
7340
7341HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7342{
7343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7344 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7345 if (SUCCEEDED(hrc))
7346 {
7347 i_setModified(IsModified_MachineData);
7348 mUserData.backup();
7349 size_t cbIcon = aIcon.size();
7350 mUserData->s.ovIcon.resize(cbIcon);
7351 if (cbIcon)
7352 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7353 }
7354 return hrc;
7355}
7356
7357HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7358{
7359#ifdef VBOX_WITH_USB
7360 *aUSBProxyAvailable = true;
7361#else
7362 *aUSBProxyAvailable = false;
7363#endif
7364 return S_OK;
7365}
7366
7367HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
7368{
7369 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7370
7371 *aVMProcessPriority = mUserData->s.enmVMPriority;
7372
7373 return S_OK;
7374}
7375
7376HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
7377{
7378 RT_NOREF(aVMProcessPriority);
7379 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7380 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7381 if (SUCCEEDED(hrc))
7382 {
7383 hrc = mUserData.backupEx();
7384 if (SUCCEEDED(hrc))
7385 {
7386 i_setModified(IsModified_MachineData);
7387 mUserData->s.enmVMPriority = aVMProcessPriority;
7388 }
7389 }
7390 alock.release();
7391 if (SUCCEEDED(hrc))
7392 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
7393 return hrc;
7394}
7395
7396HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7397 ComPtr<IProgress> &aProgress)
7398{
7399 ComObjPtr<Progress> pP;
7400 Progress *ppP = pP;
7401 IProgress *iP = static_cast<IProgress *>(ppP);
7402 IProgress **pProgress = &iP;
7403
7404 IMachine *pTarget = aTarget;
7405
7406 /* Convert the options. */
7407 RTCList<CloneOptions_T> optList;
7408 if (aOptions.size())
7409 for (size_t i = 0; i < aOptions.size(); ++i)
7410 optList.append(aOptions[i]);
7411
7412 if (optList.contains(CloneOptions_Link))
7413 {
7414 if (!i_isSnapshotMachine())
7415 return setError(E_INVALIDARG,
7416 tr("Linked clone can only be created from a snapshot"));
7417 if (aMode != CloneMode_MachineState)
7418 return setError(E_INVALIDARG,
7419 tr("Linked clone can only be created for a single machine state"));
7420 }
7421 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7422
7423 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7424
7425 HRESULT rc = pWorker->start(pProgress);
7426
7427 pP = static_cast<Progress *>(*pProgress);
7428 pP.queryInterfaceTo(aProgress.asOutParam());
7429
7430 return rc;
7431
7432}
7433
7434HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7435 const com::Utf8Str &aType,
7436 ComPtr<IProgress> &aProgress)
7437{
7438 LogFlowThisFuncEnter();
7439
7440 ComObjPtr<Progress> ptrProgress;
7441 HRESULT hrc = ptrProgress.createObject();
7442 if (SUCCEEDED(hrc))
7443 {
7444 com::Utf8Str strDefaultPath;
7445 if (aTargetPath.isEmpty())
7446 i_calculateFullPath(".", strDefaultPath);
7447
7448 /* Initialize our worker task */
7449 MachineMoveVM *pTask = NULL;
7450 try
7451 {
7452 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7453 }
7454 catch (std::bad_alloc &)
7455 {
7456 return E_OUTOFMEMORY;
7457 }
7458
7459 hrc = pTask->init();//no exceptions are thrown
7460
7461 if (SUCCEEDED(hrc))
7462 {
7463 hrc = pTask->createThread();
7464 pTask = NULL; /* Consumed by createThread(). */
7465 if (SUCCEEDED(hrc))
7466 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7467 else
7468 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7469 }
7470 else
7471 delete pTask;
7472 }
7473
7474 LogFlowThisFuncLeave();
7475 return hrc;
7476
7477}
7478
7479HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7480{
7481 NOREF(aProgress);
7482 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7483
7484 // This check should always fail.
7485 HRESULT rc = i_checkStateDependency(MutableStateDep);
7486 if (FAILED(rc)) return rc;
7487
7488 AssertFailedReturn(E_NOTIMPL);
7489}
7490
7491HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7492{
7493 NOREF(aSavedStateFile);
7494 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7495
7496 // This check should always fail.
7497 HRESULT rc = i_checkStateDependency(MutableStateDep);
7498 if (FAILED(rc)) return rc;
7499
7500 AssertFailedReturn(E_NOTIMPL);
7501}
7502
7503HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7504{
7505 NOREF(aFRemoveFile);
7506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7507
7508 // This check should always fail.
7509 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7510 if (FAILED(rc)) return rc;
7511
7512 AssertFailedReturn(E_NOTIMPL);
7513}
7514
7515// public methods for internal purposes
7516/////////////////////////////////////////////////////////////////////////////
7517
7518/**
7519 * Adds the given IsModified_* flag to the dirty flags of the machine.
7520 * This must be called either during i_loadSettings or under the machine write lock.
7521 * @param fl Flag
7522 * @param fAllowStateModification If state modifications are allowed.
7523 */
7524void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7525{
7526 mData->flModifications |= fl;
7527 if (fAllowStateModification && i_isStateModificationAllowed())
7528 mData->mCurrentStateModified = true;
7529}
7530
7531/**
7532 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7533 * care of the write locking.
7534 *
7535 * @param fModification The flag to add.
7536 * @param fAllowStateModification If state modifications are allowed.
7537 */
7538void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7539{
7540 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7541 i_setModified(fModification, fAllowStateModification);
7542}
7543
7544/**
7545 * Saves the registry entry of this machine to the given configuration node.
7546 *
7547 * @param data Machine registry data.
7548 *
7549 * @note locks this object for reading.
7550 */
7551HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7552{
7553 AutoLimitedCaller autoCaller(this);
7554 AssertComRCReturnRC(autoCaller.rc());
7555
7556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7557
7558 data.uuid = mData->mUuid;
7559 data.strSettingsFile = mData->m_strConfigFile;
7560
7561 return S_OK;
7562}
7563
7564/**
7565 * Calculates the absolute path of the given path taking the directory of the
7566 * machine settings file as the current directory.
7567 *
7568 * @param strPath Path to calculate the absolute path for.
7569 * @param aResult Where to put the result (used only on success, can be the
7570 * same Utf8Str instance as passed in @a aPath).
7571 * @return IPRT result.
7572 *
7573 * @note Locks this object for reading.
7574 */
7575int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7576{
7577 AutoCaller autoCaller(this);
7578 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7579
7580 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7581
7582 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7583
7584 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7585
7586 strSettingsDir.stripFilename();
7587 char szFolder[RTPATH_MAX];
7588 size_t cbFolder = sizeof(szFolder);
7589 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7590 if (RT_SUCCESS(vrc))
7591 aResult = szFolder;
7592
7593 return vrc;
7594}
7595
7596/**
7597 * Copies strSource to strTarget, making it relative to the machine folder
7598 * if it is a subdirectory thereof, or simply copying it otherwise.
7599 *
7600 * @param strSource Path to evaluate and copy.
7601 * @param strTarget Buffer to receive target path.
7602 *
7603 * @note Locks this object for reading.
7604 */
7605void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7606 Utf8Str &strTarget)
7607{
7608 AutoCaller autoCaller(this);
7609 AssertComRCReturn(autoCaller.rc(), (void)0);
7610
7611 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7612
7613 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7614 // use strTarget as a temporary buffer to hold the machine settings dir
7615 strTarget = mData->m_strConfigFileFull;
7616 strTarget.stripFilename();
7617 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7618 {
7619 // is relative: then append what's left
7620 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7621 // for empty paths (only possible for subdirs) use "." to avoid
7622 // triggering default settings for not present config attributes.
7623 if (strTarget.isEmpty())
7624 strTarget = ".";
7625 }
7626 else
7627 // is not relative: then overwrite
7628 strTarget = strSource;
7629}
7630
7631/**
7632 * Returns the full path to the machine's log folder in the
7633 * \a aLogFolder argument.
7634 */
7635void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7636{
7637 AutoCaller autoCaller(this);
7638 AssertComRCReturnVoid(autoCaller.rc());
7639
7640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7641
7642 char szTmp[RTPATH_MAX];
7643 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7644 if (RT_SUCCESS(vrc))
7645 {
7646 if (szTmp[0] && !mUserData.isNull())
7647 {
7648 char szTmp2[RTPATH_MAX];
7649 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7650 if (RT_SUCCESS(vrc))
7651 aLogFolder.printf("%s%c%s",
7652 szTmp2,
7653 RTPATH_DELIMITER,
7654 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7655 }
7656 else
7657 vrc = VERR_PATH_IS_RELATIVE;
7658 }
7659
7660 if (RT_FAILURE(vrc))
7661 {
7662 // fallback if VBOX_USER_LOGHOME is not set or invalid
7663 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7664 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7665 aLogFolder.append(RTPATH_DELIMITER);
7666 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7667 }
7668}
7669
7670/**
7671 * Returns the full path to the machine's log file for an given index.
7672 */
7673Utf8Str Machine::i_getLogFilename(ULONG idx)
7674{
7675 Utf8Str logFolder;
7676 getLogFolder(logFolder);
7677 Assert(logFolder.length());
7678
7679 Utf8Str log;
7680 if (idx == 0)
7681 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7682#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7683 else if (idx == 1)
7684 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7685 else
7686 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7687#else
7688 else
7689 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7690#endif
7691 return log;
7692}
7693
7694/**
7695 * Returns the full path to the machine's hardened log file.
7696 */
7697Utf8Str Machine::i_getHardeningLogFilename(void)
7698{
7699 Utf8Str strFilename;
7700 getLogFolder(strFilename);
7701 Assert(strFilename.length());
7702 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7703 return strFilename;
7704}
7705
7706/**
7707 * Returns the default NVRAM filename based on the location of the VM config.
7708 * Note that this is a relative path.
7709 */
7710Utf8Str Machine::i_getDefaultNVRAMFilename()
7711{
7712 AutoCaller autoCaller(this);
7713 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7714
7715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7716
7717 if (i_isSnapshotMachine())
7718 return Utf8Str::Empty;
7719
7720 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7721 strNVRAMFilePath.stripPath();
7722 strNVRAMFilePath.stripSuffix();
7723 strNVRAMFilePath += ".nvram";
7724
7725 return strNVRAMFilePath;
7726}
7727
7728/**
7729 * Returns the NVRAM filename for a new snapshot. This intentionally works
7730 * similarly to the saved state file naming. Note that this is usually
7731 * a relative path, unless the snapshot folder is absolute.
7732 */
7733Utf8Str Machine::i_getSnapshotNVRAMFilename()
7734{
7735 AutoCaller autoCaller(this);
7736 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7737
7738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7739
7740 RTTIMESPEC ts;
7741 RTTimeNow(&ts);
7742 RTTIME time;
7743 RTTimeExplode(&time, &ts);
7744
7745 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7746 strNVRAMFilePath += RTPATH_DELIMITER;
7747 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7748 time.i32Year, time.u8Month, time.u8MonthDay,
7749 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7750
7751 return strNVRAMFilePath;
7752}
7753
7754/**
7755 * Returns the version of the settings file.
7756 */
7757SettingsVersion_T Machine::i_getSettingsVersion(void)
7758{
7759 AutoCaller autoCaller(this);
7760 AssertComRCReturn(autoCaller.rc(), SettingsVersion_Null);
7761
7762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7763
7764 return mData->pMachineConfigFile->getSettingsVersion();
7765}
7766
7767/**
7768 * Composes a unique saved state filename based on the current system time. The filename is
7769 * granular to the second so this will work so long as no more than one snapshot is taken on
7770 * a machine per second.
7771 *
7772 * Before version 4.1, we used this formula for saved state files:
7773 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7774 * which no longer works because saved state files can now be shared between the saved state of the
7775 * "saved" machine and an online snapshot, and the following would cause problems:
7776 * 1) save machine
7777 * 2) create online snapshot from that machine state --> reusing saved state file
7778 * 3) save machine again --> filename would be reused, breaking the online snapshot
7779 *
7780 * So instead we now use a timestamp.
7781 *
7782 * @param strStateFilePath
7783 */
7784
7785void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7786{
7787 AutoCaller autoCaller(this);
7788 AssertComRCReturnVoid(autoCaller.rc());
7789
7790 {
7791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7792 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7793 }
7794
7795 RTTIMESPEC ts;
7796 RTTimeNow(&ts);
7797 RTTIME time;
7798 RTTimeExplode(&time, &ts);
7799
7800 strStateFilePath += RTPATH_DELIMITER;
7801 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7802 time.i32Year, time.u8Month, time.u8MonthDay,
7803 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7804}
7805
7806/**
7807 * Returns whether at least one USB controller is present for the VM.
7808 */
7809bool Machine::i_isUSBControllerPresent()
7810{
7811 AutoCaller autoCaller(this);
7812 AssertComRCReturn(autoCaller.rc(), false);
7813
7814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7815
7816 return (mUSBControllers->size() > 0);
7817}
7818
7819
7820/**
7821 * @note Locks this object for writing, calls the client process
7822 * (inside the lock).
7823 */
7824HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7825 const Utf8Str &strFrontend,
7826 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7827 ProgressProxy *aProgress)
7828{
7829 LogFlowThisFuncEnter();
7830
7831 AssertReturn(aControl, E_FAIL);
7832 AssertReturn(aProgress, E_FAIL);
7833 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7834
7835 AutoCaller autoCaller(this);
7836 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7837
7838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7839
7840 if (!mData->mRegistered)
7841 return setError(E_UNEXPECTED,
7842 tr("The machine '%s' is not registered"),
7843 mUserData->s.strName.c_str());
7844
7845 LogFlowThisFunc(("mSession.mState=%s\n", ::stringifySessionState(mData->mSession.mState)));
7846
7847 /* The process started when launching a VM with separate UI/VM processes is always
7848 * the UI process, i.e. needs special handling as it won't claim the session. */
7849 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7850
7851 if (fSeparate)
7852 {
7853 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7854 return setError(VBOX_E_INVALID_OBJECT_STATE,
7855 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7856 mUserData->s.strName.c_str());
7857 }
7858 else
7859 {
7860 if ( mData->mSession.mState == SessionState_Locked
7861 || mData->mSession.mState == SessionState_Spawning
7862 || mData->mSession.mState == SessionState_Unlocking)
7863 return setError(VBOX_E_INVALID_OBJECT_STATE,
7864 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7865 mUserData->s.strName.c_str());
7866
7867 /* may not be busy */
7868 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7869 }
7870
7871 /* Hardening logging */
7872#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7873 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7874 {
7875 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7876 int vrc2;
7877 /* ignore rc */ i_deleteFile(strHardeningLogFile, false /* fIgnoreFailures */, tr("hardening log file"), &vrc2);
7878 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7879 {
7880 Utf8Str strStartupLogDir = strHardeningLogFile;
7881 strStartupLogDir.stripFilename();
7882 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7883 file without stripping the file. */
7884 }
7885 strSupHardeningLogArg.append(strHardeningLogFile);
7886
7887 /* Remove legacy log filename to avoid confusion. */
7888 Utf8Str strOldStartupLogFile;
7889 getLogFolder(strOldStartupLogFile);
7890 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7891 i_deleteFile(strOldStartupLogFile, true /* fIgnoreFailures */);
7892 }
7893#else
7894 Utf8Str strSupHardeningLogArg;
7895#endif
7896
7897 Utf8Str strAppOverride;
7898#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7899 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7900#endif
7901
7902 bool fUseVBoxSDS = false;
7903 Utf8Str strCanonicalName;
7904 if (false)
7905 { }
7906#ifdef VBOX_WITH_QTGUI
7907 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7908 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7909 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7910 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7911 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7912 {
7913 strCanonicalName = "GUI/Qt";
7914 fUseVBoxSDS = true;
7915 }
7916#endif
7917#ifdef VBOX_WITH_VBOXSDL
7918 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7919 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7920 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7921 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7922 {
7923 strCanonicalName = "GUI/SDL";
7924 fUseVBoxSDS = true;
7925 }
7926#endif
7927#ifdef VBOX_WITH_HEADLESS
7928 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7929 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7930 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7931 {
7932 strCanonicalName = "headless";
7933 }
7934#endif
7935 else
7936 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7937
7938 Utf8Str idStr = mData->mUuid.toString();
7939 Utf8Str const &strMachineName = mUserData->s.strName;
7940 RTPROCESS pid = NIL_RTPROCESS;
7941
7942#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7943 RT_NOREF(fUseVBoxSDS);
7944#else
7945 DWORD idCallerSession = ~(DWORD)0;
7946 if (fUseVBoxSDS)
7947 {
7948 /*
7949 * The VBoxSDS should be used for process launching the VM with
7950 * GUI only if the caller and the VBoxSDS are in different Windows
7951 * sessions and the caller in the interactive one.
7952 */
7953 fUseVBoxSDS = false;
7954
7955 /* Get windows session of the current process. The process token used
7956 due to several reasons:
7957 1. The token is absent for the current thread except someone set it
7958 for us.
7959 2. Needs to get the id of the session where the process is started.
7960 We only need to do this once, though. */
7961 static DWORD s_idCurrentSession = ~(DWORD)0;
7962 DWORD idCurrentSession = s_idCurrentSession;
7963 if (idCurrentSession == ~(DWORD)0)
7964 {
7965 HANDLE hCurrentProcessToken = NULL;
7966 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7967 {
7968 DWORD cbIgn = 0;
7969 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7970 s_idCurrentSession = idCurrentSession;
7971 else
7972 {
7973 idCurrentSession = ~(DWORD)0;
7974 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7975 }
7976 CloseHandle(hCurrentProcessToken);
7977 }
7978 else
7979 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7980 }
7981
7982 /* get the caller's session */
7983 HRESULT hrc = CoImpersonateClient();
7984 if (SUCCEEDED(hrc))
7985 {
7986 HANDLE hCallerThreadToken;
7987 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7988 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7989 &hCallerThreadToken))
7990 {
7991 SetLastError(NO_ERROR);
7992 DWORD cbIgn = 0;
7993 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7994 {
7995 /* Only need to use SDS if the session ID differs: */
7996 if (idCurrentSession != idCallerSession)
7997 {
7998 fUseVBoxSDS = false;
7999
8000 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
8001 DWORD cbTokenGroups = 0;
8002 PTOKEN_GROUPS pTokenGroups = NULL;
8003 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
8004 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
8005 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
8006 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
8007 {
8008 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
8009 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
8010 PSID pInteractiveSid = NULL;
8011 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
8012 {
8013 /* Iterate over the groups looking for the interactive SID: */
8014 fUseVBoxSDS = false;
8015 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
8016 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
8017 {
8018 fUseVBoxSDS = true;
8019 break;
8020 }
8021 FreeSid(pInteractiveSid);
8022 }
8023 }
8024 else
8025 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
8026 RTMemTmpFree(pTokenGroups);
8027 }
8028 }
8029 else
8030 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
8031 CloseHandle(hCallerThreadToken);
8032 }
8033 else
8034 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
8035 CoRevertToSelf();
8036 }
8037 else
8038 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
8039 }
8040 if (fUseVBoxSDS)
8041 {
8042 /* connect to VBoxSDS */
8043 ComPtr<IVirtualBoxSDS> pVBoxSDS;
8044 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
8045 if (FAILED(rc))
8046 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
8047 strMachineName.c_str());
8048
8049 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
8050 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
8051 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
8052 service to access the files. */
8053 rc = CoSetProxyBlanket(pVBoxSDS,
8054 RPC_C_AUTHN_DEFAULT,
8055 RPC_C_AUTHZ_DEFAULT,
8056 COLE_DEFAULT_PRINCIPAL,
8057 RPC_C_AUTHN_LEVEL_DEFAULT,
8058 RPC_C_IMP_LEVEL_IMPERSONATE,
8059 NULL,
8060 EOAC_DEFAULT);
8061 if (FAILED(rc))
8062 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
8063
8064 size_t const cEnvVars = aEnvironmentChanges.size();
8065 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
8066 for (size_t i = 0; i < cEnvVars; i++)
8067 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
8068
8069 ULONG uPid = 0;
8070 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
8071 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
8072 idCallerSession, &uPid);
8073 if (FAILED(rc))
8074 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
8075 pid = (RTPROCESS)uPid;
8076 }
8077 else
8078#endif /* VBOX_WITH_SDS && RT_OS_WINDOWS */
8079 {
8080 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
8081 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
8082 if (RT_FAILURE(vrc))
8083 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
8084 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
8085 }
8086
8087 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
8088 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
8089
8090 if (!fSeparate)
8091 {
8092 /*
8093 * Note that we don't release the lock here before calling the client,
8094 * because it doesn't need to call us back if called with a NULL argument.
8095 * Releasing the lock here is dangerous because we didn't prepare the
8096 * launch data yet, but the client we've just started may happen to be
8097 * too fast and call LockMachine() that will fail (because of PID, etc.),
8098 * so that the Machine will never get out of the Spawning session state.
8099 */
8100
8101 /* inform the session that it will be a remote one */
8102 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8103#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8104 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8105#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8106 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8107#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8108 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8109
8110 if (FAILED(rc))
8111 {
8112 /* restore the session state */
8113 mData->mSession.mState = SessionState_Unlocked;
8114 alock.release();
8115 mParent->i_addProcessToReap(pid);
8116 /* The failure may occur w/o any error info (from RPC), so provide one */
8117 return setError(VBOX_E_VM_ERROR,
8118 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8119 }
8120
8121 /* attach launch data to the machine */
8122 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8123 mData->mSession.mRemoteControls.push_back(aControl);
8124 mData->mSession.mProgress = aProgress;
8125 mData->mSession.mPID = pid;
8126 mData->mSession.mState = SessionState_Spawning;
8127 Assert(strCanonicalName.isNotEmpty());
8128 mData->mSession.mName = strCanonicalName;
8129 }
8130 else
8131 {
8132 /* For separate UI process we declare the launch as completed instantly, as the
8133 * actual headless VM start may or may not come. No point in remembering anything
8134 * yet, as what matters for us is when the headless VM gets started. */
8135 aProgress->i_notifyComplete(S_OK);
8136 }
8137
8138 alock.release();
8139 mParent->i_addProcessToReap(pid);
8140
8141 LogFlowThisFuncLeave();
8142 return S_OK;
8143}
8144
8145/**
8146 * Returns @c true if the given session machine instance has an open direct
8147 * session (and optionally also for direct sessions which are closing) and
8148 * returns the session control machine instance if so.
8149 *
8150 * Note that when the method returns @c false, the arguments remain unchanged.
8151 *
8152 * @param aMachine Session machine object.
8153 * @param aControl Direct session control object (optional).
8154 * @param aRequireVM If true then only allow VM sessions.
8155 * @param aAllowClosing If true then additionally a session which is currently
8156 * being closed will also be allowed.
8157 *
8158 * @note locks this object for reading.
8159 */
8160bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8161 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8162 bool aRequireVM /*= false*/,
8163 bool aAllowClosing /*= false*/)
8164{
8165 AutoLimitedCaller autoCaller(this);
8166 AssertComRCReturn(autoCaller.rc(), false);
8167
8168 /* just return false for inaccessible machines */
8169 if (getObjectState().getState() != ObjectState::Ready)
8170 return false;
8171
8172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8173
8174 if ( ( mData->mSession.mState == SessionState_Locked
8175 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
8176 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8177 )
8178 {
8179 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8180
8181 aMachine = mData->mSession.mMachine;
8182
8183 if (aControl != NULL)
8184 *aControl = mData->mSession.mDirectControl;
8185
8186 return true;
8187 }
8188
8189 return false;
8190}
8191
8192/**
8193 * Returns @c true if the given machine has an spawning direct session.
8194 *
8195 * @note locks this object for reading.
8196 */
8197bool Machine::i_isSessionSpawning()
8198{
8199 AutoLimitedCaller autoCaller(this);
8200 AssertComRCReturn(autoCaller.rc(), false);
8201
8202 /* just return false for inaccessible machines */
8203 if (getObjectState().getState() != ObjectState::Ready)
8204 return false;
8205
8206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8207
8208 if (mData->mSession.mState == SessionState_Spawning)
8209 return true;
8210
8211 return false;
8212}
8213
8214/**
8215 * Called from the client watcher thread to check for unexpected client process
8216 * death during Session_Spawning state (e.g. before it successfully opened a
8217 * direct session).
8218 *
8219 * On Win32 and on OS/2, this method is called only when we've got the
8220 * direct client's process termination notification, so it always returns @c
8221 * true.
8222 *
8223 * On other platforms, this method returns @c true if the client process is
8224 * terminated and @c false if it's still alive.
8225 *
8226 * @note Locks this object for writing.
8227 */
8228bool Machine::i_checkForSpawnFailure()
8229{
8230 AutoCaller autoCaller(this);
8231 if (!autoCaller.isOk())
8232 {
8233 /* nothing to do */
8234 LogFlowThisFunc(("Already uninitialized!\n"));
8235 return true;
8236 }
8237
8238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8239
8240 if (mData->mSession.mState != SessionState_Spawning)
8241 {
8242 /* nothing to do */
8243 LogFlowThisFunc(("Not spawning any more!\n"));
8244 return true;
8245 }
8246
8247 HRESULT rc = S_OK;
8248
8249 /* PID not yet initialized, skip check. */
8250 if (mData->mSession.mPID == NIL_RTPROCESS)
8251 return false;
8252
8253 RTPROCSTATUS status;
8254 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8255
8256 if (vrc != VERR_PROCESS_RUNNING)
8257 {
8258 Utf8Str strExtraInfo;
8259
8260#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8261 /* If the startup logfile exists and is of non-zero length, tell the
8262 user to look there for more details to encourage them to attach it
8263 when reporting startup issues. */
8264 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8265 uint64_t cbStartupLogFile = 0;
8266 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
8267 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8268 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
8269#endif
8270
8271 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8272 rc = setError(E_FAIL,
8273 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8274 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8275 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8276 rc = setError(E_FAIL,
8277 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8278 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8279 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8280 rc = setError(E_FAIL,
8281 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8282 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8283 else
8284 rc = setErrorBoth(E_FAIL, vrc,
8285 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8286 i_getName().c_str(), vrc, strExtraInfo.c_str());
8287 }
8288
8289 if (FAILED(rc))
8290 {
8291 /* Close the remote session, remove the remote control from the list
8292 * and reset session state to Closed (@note keep the code in sync with
8293 * the relevant part in LockMachine()). */
8294
8295 Assert(mData->mSession.mRemoteControls.size() == 1);
8296 if (mData->mSession.mRemoteControls.size() == 1)
8297 {
8298 ErrorInfoKeeper eik;
8299 mData->mSession.mRemoteControls.front()->Uninitialize();
8300 }
8301
8302 mData->mSession.mRemoteControls.clear();
8303 mData->mSession.mState = SessionState_Unlocked;
8304
8305 /* finalize the progress after setting the state */
8306 if (!mData->mSession.mProgress.isNull())
8307 {
8308 mData->mSession.mProgress->notifyComplete(rc);
8309 mData->mSession.mProgress.setNull();
8310 }
8311
8312 mData->mSession.mPID = NIL_RTPROCESS;
8313
8314 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
8315 return true;
8316 }
8317
8318 return false;
8319}
8320
8321/**
8322 * Checks whether the machine can be registered. If so, commits and saves
8323 * all settings.
8324 *
8325 * @note Must be called from mParent's write lock. Locks this object and
8326 * children for writing.
8327 */
8328HRESULT Machine::i_prepareRegister()
8329{
8330 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8331
8332 AutoLimitedCaller autoCaller(this);
8333 AssertComRCReturnRC(autoCaller.rc());
8334
8335 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8336
8337 /* wait for state dependents to drop to zero */
8338 i_ensureNoStateDependencies(alock);
8339
8340 if (!mData->mAccessible)
8341 return setError(VBOX_E_INVALID_OBJECT_STATE,
8342 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8343 mUserData->s.strName.c_str(),
8344 mData->mUuid.toString().c_str());
8345
8346 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8347
8348 if (mData->mRegistered)
8349 return setError(VBOX_E_INVALID_OBJECT_STATE,
8350 tr("The machine '%s' with UUID {%s} is already registered"),
8351 mUserData->s.strName.c_str(),
8352 mData->mUuid.toString().c_str());
8353
8354 HRESULT rc = S_OK;
8355
8356 // Ensure the settings are saved. If we are going to be registered and
8357 // no config file exists yet, create it by calling i_saveSettings() too.
8358 if ( (mData->flModifications)
8359 || (!mData->pMachineConfigFile->fileExists())
8360 )
8361 {
8362 rc = i_saveSettings(NULL, alock);
8363 // no need to check whether VirtualBox.xml needs saving too since
8364 // we can't have a machine XML file rename pending
8365 if (FAILED(rc)) return rc;
8366 }
8367
8368 /* more config checking goes here */
8369
8370 if (SUCCEEDED(rc))
8371 {
8372 /* we may have had implicit modifications we want to fix on success */
8373 i_commit();
8374
8375 mData->mRegistered = true;
8376 }
8377 else
8378 {
8379 /* we may have had implicit modifications we want to cancel on failure*/
8380 i_rollback(false /* aNotify */);
8381 }
8382
8383 return rc;
8384}
8385
8386/**
8387 * Increases the number of objects dependent on the machine state or on the
8388 * registered state. Guarantees that these two states will not change at least
8389 * until #i_releaseStateDependency() is called.
8390 *
8391 * Depending on the @a aDepType value, additional state checks may be made.
8392 * These checks will set extended error info on failure. See
8393 * #i_checkStateDependency() for more info.
8394 *
8395 * If this method returns a failure, the dependency is not added and the caller
8396 * is not allowed to rely on any particular machine state or registration state
8397 * value and may return the failed result code to the upper level.
8398 *
8399 * @param aDepType Dependency type to add.
8400 * @param aState Current machine state (NULL if not interested).
8401 * @param aRegistered Current registered state (NULL if not interested).
8402 *
8403 * @note Locks this object for writing.
8404 */
8405HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8406 MachineState_T *aState /* = NULL */,
8407 BOOL *aRegistered /* = NULL */)
8408{
8409 AutoCaller autoCaller(this);
8410 AssertComRCReturnRC(autoCaller.rc());
8411
8412 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8413
8414 HRESULT rc = i_checkStateDependency(aDepType);
8415 if (FAILED(rc)) return rc;
8416
8417 {
8418 if (mData->mMachineStateChangePending != 0)
8419 {
8420 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8421 * drop to zero so don't add more. It may make sense to wait a bit
8422 * and retry before reporting an error (since the pending state
8423 * transition should be really quick) but let's just assert for
8424 * now to see if it ever happens on practice. */
8425
8426 AssertFailed();
8427
8428 return setError(E_ACCESSDENIED,
8429 tr("Machine state change is in progress. Please retry the operation later."));
8430 }
8431
8432 ++mData->mMachineStateDeps;
8433 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8434 }
8435
8436 if (aState)
8437 *aState = mData->mMachineState;
8438 if (aRegistered)
8439 *aRegistered = mData->mRegistered;
8440
8441 return S_OK;
8442}
8443
8444/**
8445 * Decreases the number of objects dependent on the machine state.
8446 * Must always complete the #i_addStateDependency() call after the state
8447 * dependency is no more necessary.
8448 */
8449void Machine::i_releaseStateDependency()
8450{
8451 AutoCaller autoCaller(this);
8452 AssertComRCReturnVoid(autoCaller.rc());
8453
8454 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8455
8456 /* releaseStateDependency() w/o addStateDependency()? */
8457 AssertReturnVoid(mData->mMachineStateDeps != 0);
8458 -- mData->mMachineStateDeps;
8459
8460 if (mData->mMachineStateDeps == 0)
8461 {
8462 /* inform i_ensureNoStateDependencies() that there are no more deps */
8463 if (mData->mMachineStateChangePending != 0)
8464 {
8465 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8466 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8467 }
8468 }
8469}
8470
8471Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8472{
8473 /* start with nothing found */
8474 Utf8Str strResult("");
8475
8476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8477
8478 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8479 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8480 // found:
8481 strResult = it->second; // source is a Utf8Str
8482
8483 return strResult;
8484}
8485
8486// protected methods
8487/////////////////////////////////////////////////////////////////////////////
8488
8489/**
8490 * Performs machine state checks based on the @a aDepType value. If a check
8491 * fails, this method will set extended error info, otherwise it will return
8492 * S_OK. It is supposed, that on failure, the caller will immediately return
8493 * the return value of this method to the upper level.
8494 *
8495 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8496 *
8497 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8498 * current state of this machine object allows to change settings of the
8499 * machine (i.e. the machine is not registered, or registered but not running
8500 * and not saved). It is useful to call this method from Machine setters
8501 * before performing any change.
8502 *
8503 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8504 * as for MutableStateDep except that if the machine is saved, S_OK is also
8505 * returned. This is useful in setters which allow changing machine
8506 * properties when it is in the saved state.
8507 *
8508 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8509 * if the current state of this machine object allows to change runtime
8510 * changeable settings of the machine (i.e. the machine is not registered, or
8511 * registered but either running or not running and not saved). It is useful
8512 * to call this method from Machine setters before performing any changes to
8513 * runtime changeable settings.
8514 *
8515 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8516 * the same as for MutableOrRunningStateDep except that if the machine is
8517 * saved, S_OK is also returned. This is useful in setters which allow
8518 * changing runtime and saved state changeable machine properties.
8519 *
8520 * @param aDepType Dependency type to check.
8521 *
8522 * @note Non Machine based classes should use #i_addStateDependency() and
8523 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8524 * template.
8525 *
8526 * @note This method must be called from under this object's read or write
8527 * lock.
8528 */
8529HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8530{
8531 switch (aDepType)
8532 {
8533 case AnyStateDep:
8534 {
8535 break;
8536 }
8537 case MutableStateDep:
8538 {
8539 if ( mData->mRegistered
8540 && ( !i_isSessionMachine()
8541 || ( mData->mMachineState != MachineState_Aborted
8542 && mData->mMachineState != MachineState_Teleported
8543 && mData->mMachineState != MachineState_PoweredOff
8544 )
8545 )
8546 )
8547 return setError(VBOX_E_INVALID_VM_STATE,
8548 tr("The machine is not mutable (state is %s)"),
8549 Global::stringifyMachineState(mData->mMachineState));
8550 break;
8551 }
8552 case MutableOrSavedStateDep:
8553 {
8554 if ( mData->mRegistered
8555 && ( !i_isSessionMachine()
8556 || ( mData->mMachineState != MachineState_Aborted
8557 && mData->mMachineState != MachineState_Teleported
8558 && mData->mMachineState != MachineState_Saved
8559 && mData->mMachineState != MachineState_AbortedSaved
8560 && mData->mMachineState != MachineState_PoweredOff
8561 )
8562 )
8563 )
8564 return setError(VBOX_E_INVALID_VM_STATE,
8565 tr("The machine is not mutable or saved (state is %s)"),
8566 Global::stringifyMachineState(mData->mMachineState));
8567 break;
8568 }
8569 case MutableOrRunningStateDep:
8570 {
8571 if ( mData->mRegistered
8572 && ( !i_isSessionMachine()
8573 || ( mData->mMachineState != MachineState_Aborted
8574 && mData->mMachineState != MachineState_Teleported
8575 && mData->mMachineState != MachineState_PoweredOff
8576 && !Global::IsOnline(mData->mMachineState)
8577 )
8578 )
8579 )
8580 return setError(VBOX_E_INVALID_VM_STATE,
8581 tr("The machine is not mutable or running (state is %s)"),
8582 Global::stringifyMachineState(mData->mMachineState));
8583 break;
8584 }
8585 case MutableOrSavedOrRunningStateDep:
8586 {
8587 if ( mData->mRegistered
8588 && ( !i_isSessionMachine()
8589 || ( mData->mMachineState != MachineState_Aborted
8590 && mData->mMachineState != MachineState_Teleported
8591 && mData->mMachineState != MachineState_Saved
8592 && mData->mMachineState != MachineState_AbortedSaved
8593 && mData->mMachineState != MachineState_PoweredOff
8594 && !Global::IsOnline(mData->mMachineState)
8595 )
8596 )
8597 )
8598 return setError(VBOX_E_INVALID_VM_STATE,
8599 tr("The machine is not mutable, saved or running (state is %s)"),
8600 Global::stringifyMachineState(mData->mMachineState));
8601 break;
8602 }
8603 }
8604
8605 return S_OK;
8606}
8607
8608/**
8609 * Helper to initialize all associated child objects and allocate data
8610 * structures.
8611 *
8612 * This method must be called as a part of the object's initialization procedure
8613 * (usually done in the #init() method).
8614 *
8615 * @note Must be called only from #init() or from #i_registeredInit().
8616 */
8617HRESULT Machine::initDataAndChildObjects()
8618{
8619 AutoCaller autoCaller(this);
8620 AssertComRCReturnRC(autoCaller.rc());
8621 AssertReturn( getObjectState().getState() == ObjectState::InInit
8622 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8623
8624 AssertReturn(!mData->mAccessible, E_FAIL);
8625
8626 /* allocate data structures */
8627 mSSData.allocate();
8628 mUserData.allocate();
8629 mHWData.allocate();
8630 mMediumAttachments.allocate();
8631 mStorageControllers.allocate();
8632 mUSBControllers.allocate();
8633
8634 /* initialize mOSTypeId */
8635 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8636
8637/** @todo r=bird: init() methods never fails, right? Why don't we make them
8638 * return void then! */
8639
8640 /* create associated BIOS settings object */
8641 unconst(mBIOSSettings).createObject();
8642 mBIOSSettings->init(this);
8643
8644 /* create associated recording settings object */
8645 unconst(mRecordingSettings).createObject();
8646 mRecordingSettings->init(this);
8647
8648 /* create associated trusted platform module object */
8649 unconst(mTrustedPlatformModule).createObject();
8650 mTrustedPlatformModule->init(this);
8651
8652 /* create associated NVRAM store object */
8653 unconst(mNvramStore).createObject();
8654 mNvramStore->init(this);
8655
8656 /* create the graphics adapter object (always present) */
8657 unconst(mGraphicsAdapter).createObject();
8658 mGraphicsAdapter->init(this);
8659
8660 /* create an associated VRDE object (default is disabled) */
8661 unconst(mVRDEServer).createObject();
8662 mVRDEServer->init(this);
8663
8664 /* create associated serial port objects */
8665 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8666 {
8667 unconst(mSerialPorts[slot]).createObject();
8668 mSerialPorts[slot]->init(this, slot);
8669 }
8670
8671 /* create associated parallel port objects */
8672 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8673 {
8674 unconst(mParallelPorts[slot]).createObject();
8675 mParallelPorts[slot]->init(this, slot);
8676 }
8677
8678 /* create the audio settings object */
8679 unconst(mAudioSettings).createObject();
8680 mAudioSettings->init(this);
8681
8682 /* create the USB device filters object (always present) */
8683 unconst(mUSBDeviceFilters).createObject();
8684 mUSBDeviceFilters->init(this);
8685
8686 /* create associated network adapter objects */
8687 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8688 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8689 {
8690 unconst(mNetworkAdapters[slot]).createObject();
8691 mNetworkAdapters[slot]->init(this, slot);
8692 }
8693
8694 /* create the bandwidth control */
8695 unconst(mBandwidthControl).createObject();
8696 mBandwidthControl->init(this);
8697
8698 return S_OK;
8699}
8700
8701/**
8702 * Helper to uninitialize all associated child objects and to free all data
8703 * structures.
8704 *
8705 * This method must be called as a part of the object's uninitialization
8706 * procedure (usually done in the #uninit() method).
8707 *
8708 * @note Must be called only from #uninit() or from #i_registeredInit().
8709 */
8710void Machine::uninitDataAndChildObjects()
8711{
8712 AutoCaller autoCaller(this);
8713 AssertComRCReturnVoid(autoCaller.rc());
8714 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8715 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8716 || getObjectState().getState() == ObjectState::InUninit
8717 || getObjectState().getState() == ObjectState::Limited);
8718
8719 /* tell all our other child objects we've been uninitialized */
8720 if (mBandwidthControl)
8721 {
8722 mBandwidthControl->uninit();
8723 unconst(mBandwidthControl).setNull();
8724 }
8725
8726 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8727 {
8728 if (mNetworkAdapters[slot])
8729 {
8730 mNetworkAdapters[slot]->uninit();
8731 unconst(mNetworkAdapters[slot]).setNull();
8732 }
8733 }
8734
8735 if (mUSBDeviceFilters)
8736 {
8737 mUSBDeviceFilters->uninit();
8738 unconst(mUSBDeviceFilters).setNull();
8739 }
8740
8741 if (mAudioSettings)
8742 {
8743 mAudioSettings->uninit();
8744 unconst(mAudioSettings).setNull();
8745 }
8746
8747 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8748 {
8749 if (mParallelPorts[slot])
8750 {
8751 mParallelPorts[slot]->uninit();
8752 unconst(mParallelPorts[slot]).setNull();
8753 }
8754 }
8755
8756 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8757 {
8758 if (mSerialPorts[slot])
8759 {
8760 mSerialPorts[slot]->uninit();
8761 unconst(mSerialPorts[slot]).setNull();
8762 }
8763 }
8764
8765 if (mVRDEServer)
8766 {
8767 mVRDEServer->uninit();
8768 unconst(mVRDEServer).setNull();
8769 }
8770
8771 if (mGraphicsAdapter)
8772 {
8773 mGraphicsAdapter->uninit();
8774 unconst(mGraphicsAdapter).setNull();
8775 }
8776
8777 if (mBIOSSettings)
8778 {
8779 mBIOSSettings->uninit();
8780 unconst(mBIOSSettings).setNull();
8781 }
8782
8783 if (mRecordingSettings)
8784 {
8785 mRecordingSettings->uninit();
8786 unconst(mRecordingSettings).setNull();
8787 }
8788
8789 if (mTrustedPlatformModule)
8790 {
8791 mTrustedPlatformModule->uninit();
8792 unconst(mTrustedPlatformModule).setNull();
8793 }
8794
8795 if (mNvramStore)
8796 {
8797 mNvramStore->uninit();
8798 unconst(mNvramStore).setNull();
8799 }
8800
8801 /* Deassociate media (only when a real Machine or a SnapshotMachine
8802 * instance is uninitialized; SessionMachine instances refer to real
8803 * Machine media). This is necessary for a clean re-initialization of
8804 * the VM after successfully re-checking the accessibility state. Note
8805 * that in case of normal Machine or SnapshotMachine uninitialization (as
8806 * a result of unregistering or deleting the snapshot), outdated media
8807 * attachments will already be uninitialized and deleted, so this
8808 * code will not affect them. */
8809 if ( !mMediumAttachments.isNull()
8810 && !i_isSessionMachine()
8811 )
8812 {
8813 for (MediumAttachmentList::const_iterator
8814 it = mMediumAttachments->begin();
8815 it != mMediumAttachments->end();
8816 ++it)
8817 {
8818 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8819 if (pMedium.isNull())
8820 continue;
8821 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8822 AssertComRC(rc);
8823 }
8824 }
8825
8826 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8827 {
8828 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children)
8829 if (mData->mFirstSnapshot)
8830 {
8831 // Snapshots tree is protected by machine write lock.
8832 // Otherwise we assert in Snapshot::uninit()
8833 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8834 mData->mFirstSnapshot->uninit();
8835 mData->mFirstSnapshot.setNull();
8836 }
8837
8838 mData->mCurrentSnapshot.setNull();
8839 }
8840
8841 /* free data structures (the essential mData structure is not freed here
8842 * since it may be still in use) */
8843 mMediumAttachments.free();
8844 mStorageControllers.free();
8845 mUSBControllers.free();
8846 mHWData.free();
8847 mUserData.free();
8848 mSSData.free();
8849}
8850
8851/**
8852 * Returns a pointer to the Machine object for this machine that acts like a
8853 * parent for complex machine data objects such as shared folders, etc.
8854 *
8855 * For primary Machine objects and for SnapshotMachine objects, returns this
8856 * object's pointer itself. For SessionMachine objects, returns the peer
8857 * (primary) machine pointer.
8858 */
8859Machine *Machine::i_getMachine()
8860{
8861 if (i_isSessionMachine())
8862 return (Machine*)mPeer;
8863 return this;
8864}
8865
8866/**
8867 * Makes sure that there are no machine state dependents. If necessary, waits
8868 * for the number of dependents to drop to zero.
8869 *
8870 * Make sure this method is called from under this object's write lock to
8871 * guarantee that no new dependents may be added when this method returns
8872 * control to the caller.
8873 *
8874 * @note Receives a lock to this object for writing. The lock will be released
8875 * while waiting (if necessary).
8876 *
8877 * @warning To be used only in methods that change the machine state!
8878 */
8879void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8880{
8881 AssertReturnVoid(isWriteLockOnCurrentThread());
8882
8883 /* Wait for all state dependents if necessary */
8884 if (mData->mMachineStateDeps != 0)
8885 {
8886 /* lazy semaphore creation */
8887 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8888 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8889
8890 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8891 mData->mMachineStateDeps));
8892
8893 ++mData->mMachineStateChangePending;
8894
8895 /* reset the semaphore before waiting, the last dependent will signal
8896 * it */
8897 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8898
8899 alock.release();
8900
8901 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8902
8903 alock.acquire();
8904
8905 -- mData->mMachineStateChangePending;
8906 }
8907}
8908
8909/**
8910 * Changes the machine state and informs callbacks.
8911 *
8912 * This method is not intended to fail so it either returns S_OK or asserts (and
8913 * returns a failure).
8914 *
8915 * @note Locks this object for writing.
8916 */
8917HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8918{
8919 LogFlowThisFuncEnter();
8920 LogFlowThisFunc(("aMachineState=%s\n", ::stringifyMachineState(aMachineState) ));
8921 Assert(aMachineState != MachineState_Null);
8922
8923 AutoCaller autoCaller(this);
8924 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8925
8926 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8927
8928 /* wait for state dependents to drop to zero */
8929 i_ensureNoStateDependencies(alock);
8930
8931 MachineState_T const enmOldState = mData->mMachineState;
8932 if (enmOldState != aMachineState)
8933 {
8934 mData->mMachineState = aMachineState;
8935 RTTimeNow(&mData->mLastStateChange);
8936
8937#ifdef VBOX_WITH_DTRACE_R3_MAIN
8938 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8939#endif
8940 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8941 }
8942
8943 LogFlowThisFuncLeave();
8944 return S_OK;
8945}
8946
8947/**
8948 * Searches for a shared folder with the given logical name
8949 * in the collection of shared folders.
8950 *
8951 * @param aName logical name of the shared folder
8952 * @param aSharedFolder where to return the found object
8953 * @param aSetError whether to set the error info if the folder is
8954 * not found
8955 * @return
8956 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8957 *
8958 * @note
8959 * must be called from under the object's lock!
8960 */
8961HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8962 ComObjPtr<SharedFolder> &aSharedFolder,
8963 bool aSetError /* = false */)
8964{
8965 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8966 for (HWData::SharedFolderList::const_iterator
8967 it = mHWData->mSharedFolders.begin();
8968 it != mHWData->mSharedFolders.end();
8969 ++it)
8970 {
8971 SharedFolder *pSF = *it;
8972 AutoCaller autoCaller(pSF);
8973 if (pSF->i_getName() == aName)
8974 {
8975 aSharedFolder = pSF;
8976 rc = S_OK;
8977 break;
8978 }
8979 }
8980
8981 if (aSetError && FAILED(rc))
8982 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8983
8984 return rc;
8985}
8986
8987/**
8988 * Initializes all machine instance data from the given settings structures
8989 * from XML. The exception is the machine UUID which needs special handling
8990 * depending on the caller's use case, so the caller needs to set that herself.
8991 *
8992 * This gets called in several contexts during machine initialization:
8993 *
8994 * -- When machine XML exists on disk already and needs to be loaded into memory,
8995 * for example, from #i_registeredInit() to load all registered machines on
8996 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8997 * attached to the machine should be part of some media registry already.
8998 *
8999 * -- During OVF import, when a machine config has been constructed from an
9000 * OVF file. In this case, puuidRegistry is set to the machine UUID to
9001 * ensure that the media listed as attachments in the config (which have
9002 * been imported from the OVF) receive the correct registry ID.
9003 *
9004 * -- During VM cloning.
9005 *
9006 * @param config Machine settings from XML.
9007 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
9008 * for each attached medium in the config.
9009 * @return
9010 */
9011HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
9012 const Guid *puuidRegistry)
9013{
9014 // copy name, description, OS type, teleporter, UTC etc.
9015 mUserData->s = config.machineUserData;
9016
9017 // look up the object by Id to check it is valid
9018 ComObjPtr<GuestOSType> pGuestOSType;
9019 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9020 if (!pGuestOSType.isNull())
9021 mUserData->s.strOsType = pGuestOSType->i_id();
9022
9023#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
9024 // stateFile encryption (optional)
9025 mSSData->strStateKeyId = config.strStateKeyId;
9026 mSSData->strStateKeyStore = config.strStateKeyStore;
9027 mData->mstrLogKeyId = config.strLogKeyId;
9028 mData->mstrLogKeyStore = config.strLogKeyStore;
9029#endif
9030
9031 // stateFile (optional)
9032 if (config.strStateFile.isEmpty())
9033 mSSData->strStateFilePath.setNull();
9034 else
9035 {
9036 Utf8Str stateFilePathFull(config.strStateFile);
9037 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
9038 if (RT_FAILURE(vrc))
9039 return setErrorBoth(E_FAIL, vrc,
9040 tr("Invalid saved state file path '%s' (%Rrc)"),
9041 config.strStateFile.c_str(),
9042 vrc);
9043 mSSData->strStateFilePath = stateFilePathFull;
9044 }
9045
9046 // snapshot folder needs special processing so set it again
9047 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
9048 if (FAILED(rc)) return rc;
9049
9050 /* Copy the extra data items (config may or may not be the same as
9051 * mData->pMachineConfigFile) if necessary. When loading the XML files
9052 * from disk they are the same, but not for OVF import. */
9053 if (mData->pMachineConfigFile != &config)
9054 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
9055
9056 /* currentStateModified (optional, default is true) */
9057 mData->mCurrentStateModified = config.fCurrentStateModified;
9058
9059 mData->mLastStateChange = config.timeLastStateChange;
9060
9061 /*
9062 * note: all mUserData members must be assigned prior this point because
9063 * we need to commit changes in order to let mUserData be shared by all
9064 * snapshot machine instances.
9065 */
9066 mUserData.commitCopy();
9067
9068 // machine registry, if present (must be loaded before snapshots)
9069 if (config.canHaveOwnMediaRegistry())
9070 {
9071 // determine machine folder
9072 Utf8Str strMachineFolder = i_getSettingsFileFull();
9073 strMachineFolder.stripFilename();
9074 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
9075 config.mediaRegistry,
9076 strMachineFolder);
9077 if (FAILED(rc)) return rc;
9078 }
9079
9080 /* Snapshot node (optional) */
9081 size_t cRootSnapshots;
9082 if ((cRootSnapshots = config.llFirstSnapshot.size()))
9083 {
9084 // there must be only one root snapshot
9085 Assert(cRootSnapshots == 1);
9086 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9087
9088 rc = i_loadSnapshot(snap,
9089 config.uuidCurrentSnapshot);
9090 if (FAILED(rc)) return rc;
9091 }
9092
9093 // hardware data
9094 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart,
9095 config.recordingSettings);
9096 if (FAILED(rc)) return rc;
9097
9098 /*
9099 * NOTE: the assignment below must be the last thing to do,
9100 * otherwise it will be not possible to change the settings
9101 * somewhere in the code above because all setters will be
9102 * blocked by i_checkStateDependency(MutableStateDep).
9103 */
9104
9105 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
9106 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
9107 {
9108 /* no need to use i_setMachineState() during init() */
9109 mData->mMachineState = MachineState_AbortedSaved;
9110 }
9111 else if (config.fAborted)
9112 {
9113 mSSData->strStateFilePath.setNull();
9114
9115 /* no need to use i_setMachineState() during init() */
9116 mData->mMachineState = MachineState_Aborted;
9117 }
9118 else if (!mSSData->strStateFilePath.isEmpty())
9119 {
9120 /* no need to use i_setMachineState() during init() */
9121 mData->mMachineState = MachineState_Saved;
9122 }
9123
9124 // after loading settings, we are no longer different from the XML on disk
9125 mData->flModifications = 0;
9126
9127 return S_OK;
9128}
9129
9130/**
9131 * Loads all snapshots starting from the given settings.
9132 *
9133 * @param data snapshot settings.
9134 * @param aCurSnapshotId Current snapshot ID from the settings file.
9135 */
9136HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
9137 const Guid &aCurSnapshotId)
9138{
9139 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
9140 AssertReturn(!i_isSessionMachine(), E_FAIL);
9141
9142 HRESULT rc = S_OK;
9143
9144 std::list<const settings::Snapshot *> llSettingsTodo;
9145 llSettingsTodo.push_back(&data);
9146 std::list<Snapshot *> llParentsTodo;
9147 llParentsTodo.push_back(NULL);
9148
9149 while (llSettingsTodo.size() > 0)
9150 {
9151 const settings::Snapshot *current = llSettingsTodo.front();
9152 llSettingsTodo.pop_front();
9153 Snapshot *pParent = llParentsTodo.front();
9154 llParentsTodo.pop_front();
9155
9156 Utf8Str strStateFile;
9157 if (!current->strStateFile.isEmpty())
9158 {
9159 /* optional */
9160 strStateFile = current->strStateFile;
9161 int vrc = i_calculateFullPath(strStateFile, strStateFile);
9162 if (RT_FAILURE(vrc))
9163 {
9164 setErrorBoth(E_FAIL, vrc,
9165 tr("Invalid saved state file path '%s' (%Rrc)"),
9166 strStateFile.c_str(), vrc);
9167 }
9168 }
9169
9170 /* create a snapshot machine object */
9171 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9172 pSnapshotMachine.createObject();
9173 rc = pSnapshotMachine->initFromSettings(this,
9174 current->hardware,
9175 &current->debugging,
9176 &current->autostart,
9177 current->recordingSettings,
9178 current->uuid.ref(),
9179 strStateFile);
9180 if (FAILED(rc)) break;
9181
9182 /* create a snapshot object */
9183 ComObjPtr<Snapshot> pSnapshot;
9184 pSnapshot.createObject();
9185 /* initialize the snapshot */
9186 rc = pSnapshot->init(mParent, // VirtualBox object
9187 current->uuid,
9188 current->strName,
9189 current->strDescription,
9190 current->timestamp,
9191 pSnapshotMachine,
9192 pParent);
9193 if (FAILED(rc)) break;
9194
9195 /* memorize the first snapshot if necessary */
9196 if (!mData->mFirstSnapshot)
9197 {
9198 Assert(pParent == NULL);
9199 mData->mFirstSnapshot = pSnapshot;
9200 }
9201
9202 /* memorize the current snapshot when appropriate */
9203 if ( !mData->mCurrentSnapshot
9204 && pSnapshot->i_getId() == aCurSnapshotId
9205 )
9206 mData->mCurrentSnapshot = pSnapshot;
9207
9208 /* create all children */
9209 std::list<settings::Snapshot>::const_iterator itBegin = current->llChildSnapshots.begin();
9210 std::list<settings::Snapshot>::const_iterator itEnd = current->llChildSnapshots.end();
9211 for (std::list<settings::Snapshot>::const_iterator it = itBegin; it != itEnd; ++it)
9212 {
9213 llSettingsTodo.push_back(&*it);
9214 llParentsTodo.push_back(pSnapshot);
9215 }
9216 }
9217
9218 return rc;
9219}
9220
9221/**
9222 * Loads settings into mHWData.
9223 *
9224 * @param puuidRegistry Registry ID.
9225 * @param puuidSnapshot Snapshot ID
9226 * @param data Reference to the hardware settings.
9227 * @param pDbg Pointer to the debugging settings.
9228 * @param pAutostart Pointer to the autostart settings
9229 * @param recording Reference to recording settings.
9230 */
9231HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
9232 const Guid *puuidSnapshot,
9233 const settings::Hardware &data,
9234 const settings::Debugging *pDbg,
9235 const settings::Autostart *pAutostart,
9236 const settings::RecordingSettings &recording)
9237{
9238 AssertReturn(!i_isSessionMachine(), E_FAIL);
9239
9240 HRESULT rc = S_OK;
9241
9242 try
9243 {
9244 ComObjPtr<GuestOSType> pGuestOSType;
9245 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9246
9247 /* The hardware version attribute (optional). */
9248 mHWData->mHWVersion = data.strVersion;
9249 mHWData->mHardwareUUID = data.uuid;
9250
9251 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9252 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9253 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9254 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9255 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9256 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9257 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
9258 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
9259 mHWData->mPAEEnabled = data.fPAE;
9260 mHWData->mLongMode = data.enmLongMode;
9261 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9262 mHWData->mAPIC = data.fAPIC;
9263 mHWData->mX2APIC = data.fX2APIC;
9264 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9265 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9266 mHWData->mSpecCtrl = data.fSpecCtrl;
9267 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
9268 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
9269 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
9270 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
9271 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
9272 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9273 mHWData->mCPUCount = data.cCPUs;
9274 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9275 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9276 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9277 mHWData->mCpuProfile = data.strCpuProfile;
9278
9279 // cpu
9280 if (mHWData->mCPUHotPlugEnabled)
9281 {
9282 for (settings::CpuList::const_iterator
9283 it = data.llCpus.begin();
9284 it != data.llCpus.end();
9285 ++it)
9286 {
9287 const settings::Cpu &cpu = *it;
9288
9289 mHWData->mCPUAttached[cpu.ulId] = true;
9290 }
9291 }
9292
9293 // cpuid leafs
9294 for (settings::CpuIdLeafsList::const_iterator
9295 it = data.llCpuIdLeafs.begin();
9296 it != data.llCpuIdLeafs.end();
9297 ++it)
9298 {
9299 const settings::CpuIdLeaf &rLeaf= *it;
9300 if ( rLeaf.idx < UINT32_C(0x20)
9301 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9302 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9303 mHWData->mCpuIdLeafList.push_back(rLeaf);
9304 /* else: just ignore */
9305 }
9306
9307 mHWData->mMemorySize = data.ulMemorySizeMB;
9308 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9309
9310 // boot order
9311 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9312 {
9313 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9314 if (it == data.mapBootOrder.end())
9315 mHWData->mBootOrder[i] = DeviceType_Null;
9316 else
9317 mHWData->mBootOrder[i] = it->second;
9318 }
9319
9320 mHWData->mFirmwareType = data.firmwareType;
9321 mHWData->mPointingHIDType = data.pointingHIDType;
9322 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9323 mHWData->mChipsetType = data.chipsetType;
9324 mHWData->mIommuType = data.iommuType;
9325 mHWData->mParavirtProvider = data.paravirtProvider;
9326 mHWData->mParavirtDebug = data.strParavirtDebug;
9327 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9328 mHWData->mHPETEnabled = data.fHPETEnabled;
9329
9330 /* GraphicsAdapter */
9331 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
9332 if (FAILED(rc)) return rc;
9333
9334 /* VRDEServer */
9335 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9336 if (FAILED(rc)) return rc;
9337
9338 /* BIOS */
9339 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9340 if (FAILED(rc)) return rc;
9341
9342 /* Recording */
9343 rc = mRecordingSettings->i_loadSettings(recording);
9344 if (FAILED(rc)) return rc;
9345
9346 /* Trusted Platform Module */
9347 rc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
9348 if (FAILED(rc)) return rc;
9349
9350 rc = mNvramStore->i_loadSettings(data.nvramSettings);
9351 if (FAILED(rc)) return rc;
9352
9353 // Bandwidth control (must come before network adapters)
9354 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9355 if (FAILED(rc)) return rc;
9356
9357 /* USB controllers */
9358 for (settings::USBControllerList::const_iterator
9359 it = data.usbSettings.llUSBControllers.begin();
9360 it != data.usbSettings.llUSBControllers.end();
9361 ++it)
9362 {
9363 const settings::USBController &settingsCtrl = *it;
9364 ComObjPtr<USBController> newCtrl;
9365
9366 newCtrl.createObject();
9367 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9368 mUSBControllers->push_back(newCtrl);
9369 }
9370
9371 /* USB device filters */
9372 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9373 if (FAILED(rc)) return rc;
9374
9375 // network adapters (establish array size first and apply defaults, to
9376 // ensure reading the same settings as we saved, since the list skips
9377 // adapters having defaults)
9378 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9379 size_t oldCount = mNetworkAdapters.size();
9380 if (newCount > oldCount)
9381 {
9382 mNetworkAdapters.resize(newCount);
9383 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9384 {
9385 unconst(mNetworkAdapters[slot]).createObject();
9386 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9387 }
9388 }
9389 else if (newCount < oldCount)
9390 mNetworkAdapters.resize(newCount);
9391 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9392 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9393 for (settings::NetworkAdaptersList::const_iterator
9394 it = data.llNetworkAdapters.begin();
9395 it != data.llNetworkAdapters.end();
9396 ++it)
9397 {
9398 const settings::NetworkAdapter &nic = *it;
9399
9400 /* slot uniqueness is guaranteed by XML Schema */
9401 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9402 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9403 if (FAILED(rc)) return rc;
9404 }
9405
9406 // serial ports (establish defaults first, to ensure reading the same
9407 // settings as we saved, since the list skips ports having defaults)
9408 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9409 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9410 for (settings::SerialPortsList::const_iterator
9411 it = data.llSerialPorts.begin();
9412 it != data.llSerialPorts.end();
9413 ++it)
9414 {
9415 const settings::SerialPort &s = *it;
9416
9417 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9418 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9419 if (FAILED(rc)) return rc;
9420 }
9421
9422 // parallel ports (establish defaults first, to ensure reading the same
9423 // settings as we saved, since the list skips ports having defaults)
9424 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9425 mParallelPorts[i]->i_applyDefaults();
9426 for (settings::ParallelPortsList::const_iterator
9427 it = data.llParallelPorts.begin();
9428 it != data.llParallelPorts.end();
9429 ++it)
9430 {
9431 const settings::ParallelPort &p = *it;
9432
9433 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9434 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9435 if (FAILED(rc)) return rc;
9436 }
9437
9438 /* Audio settings */
9439 rc = mAudioSettings->i_loadSettings(data.audioAdapter);
9440 if (FAILED(rc)) return rc;
9441
9442 /* storage controllers */
9443 rc = i_loadStorageControllers(data.storage,
9444 puuidRegistry,
9445 puuidSnapshot);
9446 if (FAILED(rc)) return rc;
9447
9448 /* Shared folders */
9449 for (settings::SharedFoldersList::const_iterator
9450 it = data.llSharedFolders.begin();
9451 it != data.llSharedFolders.end();
9452 ++it)
9453 {
9454 const settings::SharedFolder &sf = *it;
9455
9456 ComObjPtr<SharedFolder> sharedFolder;
9457 /* Check for double entries. Not allowed! */
9458 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9459 if (SUCCEEDED(rc))
9460 return setError(VBOX_E_OBJECT_IN_USE,
9461 tr("Shared folder named '%s' already exists"),
9462 sf.strName.c_str());
9463
9464 /* Create the new shared folder. Don't break on error. This will be
9465 * reported when the machine starts. */
9466 sharedFolder.createObject();
9467 rc = sharedFolder->init(i_getMachine(),
9468 sf.strName,
9469 sf.strHostPath,
9470 RT_BOOL(sf.fWritable),
9471 RT_BOOL(sf.fAutoMount),
9472 sf.strAutoMountPoint,
9473 false /* fFailOnError */);
9474 if (FAILED(rc)) return rc;
9475 mHWData->mSharedFolders.push_back(sharedFolder);
9476 }
9477
9478 // Clipboard
9479 mHWData->mClipboardMode = data.clipboardMode;
9480 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9481
9482 // drag'n'drop
9483 mHWData->mDnDMode = data.dndMode;
9484
9485 // guest settings
9486 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9487
9488 // IO settings
9489 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9490 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9491
9492 // Host PCI devices
9493 for (settings::HostPCIDeviceAttachmentList::const_iterator
9494 it = data.pciAttachments.begin();
9495 it != data.pciAttachments.end();
9496 ++it)
9497 {
9498 const settings::HostPCIDeviceAttachment &hpda = *it;
9499 ComObjPtr<PCIDeviceAttachment> pda;
9500
9501 pda.createObject();
9502 pda->i_loadSettings(this, hpda);
9503 mHWData->mPCIDeviceAssignments.push_back(pda);
9504 }
9505
9506 /*
9507 * (The following isn't really real hardware, but it lives in HWData
9508 * for reasons of convenience.)
9509 */
9510
9511#ifdef VBOX_WITH_GUEST_PROPS
9512 /* Guest properties (optional) */
9513
9514 /* Only load transient guest properties for configs which have saved
9515 * state, because there shouldn't be any for powered off VMs. The same
9516 * logic applies for snapshots, as offline snapshots shouldn't have
9517 * any such properties. They confuse the code in various places.
9518 * Note: can't rely on the machine state, as it isn't set yet. */
9519 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9520 /* apologies for the hacky unconst() usage, but this needs hacking
9521 * actually inconsistent settings into consistency, otherwise there
9522 * will be some corner cases where the inconsistency survives
9523 * surprisingly long without getting fixed, especially for snapshots
9524 * as there are no config changes. */
9525 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9526 for (settings::GuestPropertiesList::iterator
9527 it = llGuestProperties.begin();
9528 it != llGuestProperties.end();
9529 /*nothing*/)
9530 {
9531 const settings::GuestProperty &prop = *it;
9532 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9533 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9534 if ( fSkipTransientGuestProperties
9535 && ( fFlags & GUEST_PROP_F_TRANSIENT
9536 || fFlags & GUEST_PROP_F_TRANSRESET))
9537 {
9538 it = llGuestProperties.erase(it);
9539 continue;
9540 }
9541 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9542 mHWData->mGuestProperties[prop.strName] = property;
9543 ++it;
9544 }
9545#endif /* VBOX_WITH_GUEST_PROPS defined */
9546
9547 rc = i_loadDebugging(pDbg);
9548 if (FAILED(rc))
9549 return rc;
9550
9551 mHWData->mAutostart = *pAutostart;
9552
9553 /* default frontend */
9554 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9555 }
9556 catch (std::bad_alloc &)
9557 {
9558 return E_OUTOFMEMORY;
9559 }
9560
9561 AssertComRC(rc);
9562 return rc;
9563}
9564
9565/**
9566 * Called from i_loadHardware() to load the debugging settings of the
9567 * machine.
9568 *
9569 * @param pDbg Pointer to the settings.
9570 */
9571HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9572{
9573 mHWData->mDebugging = *pDbg;
9574 /* no more processing currently required, this will probably change. */
9575 return S_OK;
9576}
9577
9578/**
9579 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9580 *
9581 * @param data storage settings.
9582 * @param puuidRegistry media registry ID to set media to or NULL;
9583 * see Machine::i_loadMachineDataFromSettings()
9584 * @param puuidSnapshot snapshot ID
9585 * @return
9586 */
9587HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9588 const Guid *puuidRegistry,
9589 const Guid *puuidSnapshot)
9590{
9591 AssertReturn(!i_isSessionMachine(), E_FAIL);
9592
9593 HRESULT rc = S_OK;
9594
9595 for (settings::StorageControllersList::const_iterator
9596 it = data.llStorageControllers.begin();
9597 it != data.llStorageControllers.end();
9598 ++it)
9599 {
9600 const settings::StorageController &ctlData = *it;
9601
9602 ComObjPtr<StorageController> pCtl;
9603 /* Try to find one with the name first. */
9604 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9605 if (SUCCEEDED(rc))
9606 return setError(VBOX_E_OBJECT_IN_USE,
9607 tr("Storage controller named '%s' already exists"),
9608 ctlData.strName.c_str());
9609
9610 pCtl.createObject();
9611 rc = pCtl->init(this,
9612 ctlData.strName,
9613 ctlData.storageBus,
9614 ctlData.ulInstance,
9615 ctlData.fBootable);
9616 if (FAILED(rc)) return rc;
9617
9618 mStorageControllers->push_back(pCtl);
9619
9620 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9621 if (FAILED(rc)) return rc;
9622
9623 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9624 if (FAILED(rc)) return rc;
9625
9626 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9627 if (FAILED(rc)) return rc;
9628
9629 /* Load the attached devices now. */
9630 rc = i_loadStorageDevices(pCtl,
9631 ctlData,
9632 puuidRegistry,
9633 puuidSnapshot);
9634 if (FAILED(rc)) return rc;
9635 }
9636
9637 return S_OK;
9638}
9639
9640/**
9641 * Called from i_loadStorageControllers for a controller's devices.
9642 *
9643 * @param aStorageController
9644 * @param data
9645 * @param puuidRegistry media registry ID to set media to or NULL; see
9646 * Machine::i_loadMachineDataFromSettings()
9647 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9648 * @return
9649 */
9650HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9651 const settings::StorageController &data,
9652 const Guid *puuidRegistry,
9653 const Guid *puuidSnapshot)
9654{
9655 HRESULT rc = S_OK;
9656
9657 /* paranoia: detect duplicate attachments */
9658 for (settings::AttachedDevicesList::const_iterator
9659 it = data.llAttachedDevices.begin();
9660 it != data.llAttachedDevices.end();
9661 ++it)
9662 {
9663 const settings::AttachedDevice &ad = *it;
9664
9665 for (settings::AttachedDevicesList::const_iterator it2 = it;
9666 it2 != data.llAttachedDevices.end();
9667 ++it2)
9668 {
9669 if (it == it2)
9670 continue;
9671
9672 const settings::AttachedDevice &ad2 = *it2;
9673
9674 if ( ad.lPort == ad2.lPort
9675 && ad.lDevice == ad2.lDevice)
9676 {
9677 return setError(E_FAIL,
9678 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9679 aStorageController->i_getName().c_str(),
9680 ad.lPort,
9681 ad.lDevice,
9682 mUserData->s.strName.c_str());
9683 }
9684 }
9685 }
9686
9687 for (settings::AttachedDevicesList::const_iterator
9688 it = data.llAttachedDevices.begin();
9689 it != data.llAttachedDevices.end();
9690 ++it)
9691 {
9692 const settings::AttachedDevice &dev = *it;
9693 ComObjPtr<Medium> medium;
9694
9695 switch (dev.deviceType)
9696 {
9697 case DeviceType_Floppy:
9698 case DeviceType_DVD:
9699 if (dev.strHostDriveSrc.isNotEmpty())
9700 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9701 false /* fRefresh */, medium);
9702 else
9703 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9704 dev.uuid,
9705 false /* fRefresh */,
9706 false /* aSetError */,
9707 medium);
9708 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9709 // This is not an error. The host drive or UUID might have vanished, so just go
9710 // ahead without this removeable medium attachment
9711 rc = S_OK;
9712 break;
9713
9714 case DeviceType_HardDisk:
9715 {
9716 /* find a hard disk by UUID */
9717 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9718 if (FAILED(rc))
9719 {
9720 if (i_isSnapshotMachine())
9721 {
9722 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9723 // so the user knows that the bad disk is in a snapshot somewhere
9724 com::ErrorInfo info;
9725 return setError(E_FAIL,
9726 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9727 puuidSnapshot->raw(),
9728 info.getText().raw());
9729 }
9730 else
9731 return rc;
9732 }
9733
9734 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9735
9736 if (medium->i_getType() == MediumType_Immutable)
9737 {
9738 if (i_isSnapshotMachine())
9739 return setError(E_FAIL,
9740 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9741 "of the virtual machine '%s' ('%s')"),
9742 medium->i_getLocationFull().c_str(),
9743 dev.uuid.raw(),
9744 puuidSnapshot->raw(),
9745 mUserData->s.strName.c_str(),
9746 mData->m_strConfigFileFull.c_str());
9747
9748 return setError(E_FAIL,
9749 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9750 medium->i_getLocationFull().c_str(),
9751 dev.uuid.raw(),
9752 mUserData->s.strName.c_str(),
9753 mData->m_strConfigFileFull.c_str());
9754 }
9755
9756 if (medium->i_getType() == MediumType_MultiAttach)
9757 {
9758 if (i_isSnapshotMachine())
9759 return setError(E_FAIL,
9760 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9761 "of the virtual machine '%s' ('%s')"),
9762 medium->i_getLocationFull().c_str(),
9763 dev.uuid.raw(),
9764 puuidSnapshot->raw(),
9765 mUserData->s.strName.c_str(),
9766 mData->m_strConfigFileFull.c_str());
9767
9768 return setError(E_FAIL,
9769 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9770 medium->i_getLocationFull().c_str(),
9771 dev.uuid.raw(),
9772 mUserData->s.strName.c_str(),
9773 mData->m_strConfigFileFull.c_str());
9774 }
9775
9776 if ( !i_isSnapshotMachine()
9777 && medium->i_getChildren().size() != 0
9778 )
9779 return setError(E_FAIL,
9780 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9781 "because it has %d differencing child hard disks"),
9782 medium->i_getLocationFull().c_str(),
9783 dev.uuid.raw(),
9784 mUserData->s.strName.c_str(),
9785 mData->m_strConfigFileFull.c_str(),
9786 medium->i_getChildren().size());
9787
9788 if (i_findAttachment(*mMediumAttachments.data(),
9789 medium))
9790 return setError(E_FAIL,
9791 tr("Hard disk '%s' with UUID {%RTuuid} is already 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 break;
9798 }
9799
9800 default:
9801 return setError(E_FAIL,
9802 tr("Controller '%s' port %u unit %u has device with unknown type (%d) - virtual machine '%s' ('%s')"),
9803 data.strName.c_str(), dev.lPort, dev.lDevice, dev.deviceType,
9804 mUserData->s.strName.c_str(), mData->m_strConfigFileFull.c_str());
9805 }
9806
9807 if (FAILED(rc))
9808 break;
9809
9810 /* Bandwidth groups are loaded at this point. */
9811 ComObjPtr<BandwidthGroup> pBwGroup;
9812
9813 if (!dev.strBwGroup.isEmpty())
9814 {
9815 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9816 if (FAILED(rc))
9817 return setError(E_FAIL,
9818 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9819 medium->i_getLocationFull().c_str(),
9820 dev.strBwGroup.c_str(),
9821 mUserData->s.strName.c_str(),
9822 mData->m_strConfigFileFull.c_str());
9823 pBwGroup->i_reference();
9824 }
9825
9826 const Utf8Str controllerName = aStorageController->i_getName();
9827 ComObjPtr<MediumAttachment> pAttachment;
9828 pAttachment.createObject();
9829 rc = pAttachment->init(this,
9830 medium,
9831 controllerName,
9832 dev.lPort,
9833 dev.lDevice,
9834 dev.deviceType,
9835 false,
9836 dev.fPassThrough,
9837 dev.fTempEject,
9838 dev.fNonRotational,
9839 dev.fDiscard,
9840 dev.fHotPluggable,
9841 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9842 if (FAILED(rc)) break;
9843
9844 /* associate the medium with this machine and snapshot */
9845 if (!medium.isNull())
9846 {
9847 AutoCaller medCaller(medium);
9848 if (FAILED(medCaller.rc())) return medCaller.rc();
9849 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9850
9851 if (i_isSnapshotMachine())
9852 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9853 else
9854 rc = medium->i_addBackReference(mData->mUuid);
9855 /* If the medium->addBackReference fails it sets an appropriate
9856 * error message, so no need to do any guesswork here. */
9857
9858 if (puuidRegistry)
9859 // caller wants registry ID to be set on all attached media (OVF import case)
9860 medium->i_addRegistry(*puuidRegistry);
9861 }
9862
9863 if (FAILED(rc))
9864 break;
9865
9866 /* back up mMediumAttachments to let registeredInit() properly rollback
9867 * on failure (= limited accessibility) */
9868 i_setModified(IsModified_Storage);
9869 mMediumAttachments.backup();
9870 mMediumAttachments->push_back(pAttachment);
9871 }
9872
9873 return rc;
9874}
9875
9876/**
9877 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9878 *
9879 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9880 * @param aSnapshot where to return the found snapshot
9881 * @param aSetError true to set extended error info on failure
9882 */
9883HRESULT Machine::i_findSnapshotById(const Guid &aId,
9884 ComObjPtr<Snapshot> &aSnapshot,
9885 bool aSetError /* = false */)
9886{
9887 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9888
9889 if (!mData->mFirstSnapshot)
9890 {
9891 if (aSetError)
9892 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9893 return E_FAIL;
9894 }
9895
9896 if (aId.isZero())
9897 aSnapshot = mData->mFirstSnapshot;
9898 else
9899 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9900
9901 if (!aSnapshot)
9902 {
9903 if (aSetError)
9904 return setError(E_FAIL,
9905 tr("Could not find a snapshot with UUID {%s}"),
9906 aId.toString().c_str());
9907 return E_FAIL;
9908 }
9909
9910 return S_OK;
9911}
9912
9913/**
9914 * Returns the snapshot with the given name or fails of no such snapshot.
9915 *
9916 * @param strName snapshot name to find
9917 * @param aSnapshot where to return the found snapshot
9918 * @param aSetError true to set extended error info on failure
9919 */
9920HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9921 ComObjPtr<Snapshot> &aSnapshot,
9922 bool aSetError /* = false */)
9923{
9924 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9925
9926 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9927
9928 if (!mData->mFirstSnapshot)
9929 {
9930 if (aSetError)
9931 return setError(VBOX_E_OBJECT_NOT_FOUND,
9932 tr("This machine does not have any snapshots"));
9933 return VBOX_E_OBJECT_NOT_FOUND;
9934 }
9935
9936 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9937
9938 if (!aSnapshot)
9939 {
9940 if (aSetError)
9941 return setError(VBOX_E_OBJECT_NOT_FOUND,
9942 tr("Could not find a snapshot named '%s'"), strName.c_str());
9943 return VBOX_E_OBJECT_NOT_FOUND;
9944 }
9945
9946 return S_OK;
9947}
9948
9949/**
9950 * Returns a storage controller object with the given name.
9951 *
9952 * @param aName storage controller name to find
9953 * @param aStorageController where to return the found storage controller
9954 * @param aSetError true to set extended error info on failure
9955 */
9956HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9957 ComObjPtr<StorageController> &aStorageController,
9958 bool aSetError /* = false */)
9959{
9960 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9961
9962 for (StorageControllerList::const_iterator
9963 it = mStorageControllers->begin();
9964 it != mStorageControllers->end();
9965 ++it)
9966 {
9967 if ((*it)->i_getName() == aName)
9968 {
9969 aStorageController = (*it);
9970 return S_OK;
9971 }
9972 }
9973
9974 if (aSetError)
9975 return setError(VBOX_E_OBJECT_NOT_FOUND,
9976 tr("Could not find a storage controller named '%s'"),
9977 aName.c_str());
9978 return VBOX_E_OBJECT_NOT_FOUND;
9979}
9980
9981/**
9982 * Returns a USB controller object with the given name.
9983 *
9984 * @param aName USB controller name to find
9985 * @param aUSBController where to return the found USB controller
9986 * @param aSetError true to set extended error info on failure
9987 */
9988HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9989 ComObjPtr<USBController> &aUSBController,
9990 bool aSetError /* = false */)
9991{
9992 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9993
9994 for (USBControllerList::const_iterator
9995 it = mUSBControllers->begin();
9996 it != mUSBControllers->end();
9997 ++it)
9998 {
9999 if ((*it)->i_getName() == aName)
10000 {
10001 aUSBController = (*it);
10002 return S_OK;
10003 }
10004 }
10005
10006 if (aSetError)
10007 return setError(VBOX_E_OBJECT_NOT_FOUND,
10008 tr("Could not find a storage controller named '%s'"),
10009 aName.c_str());
10010 return VBOX_E_OBJECT_NOT_FOUND;
10011}
10012
10013/**
10014 * Returns the number of USB controller instance of the given type.
10015 *
10016 * @param enmType USB controller type.
10017 */
10018ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
10019{
10020 ULONG cCtrls = 0;
10021
10022 for (USBControllerList::const_iterator
10023 it = mUSBControllers->begin();
10024 it != mUSBControllers->end();
10025 ++it)
10026 {
10027 if ((*it)->i_getControllerType() == enmType)
10028 cCtrls++;
10029 }
10030
10031 return cCtrls;
10032}
10033
10034HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
10035 MediumAttachmentList &atts)
10036{
10037 AutoCaller autoCaller(this);
10038 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10039
10040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10041
10042 for (MediumAttachmentList::const_iterator
10043 it = mMediumAttachments->begin();
10044 it != mMediumAttachments->end();
10045 ++it)
10046 {
10047 const ComObjPtr<MediumAttachment> &pAtt = *it;
10048 // should never happen, but deal with NULL pointers in the list.
10049 AssertContinue(!pAtt.isNull());
10050
10051 // getControllerName() needs caller+read lock
10052 AutoCaller autoAttCaller(pAtt);
10053 if (FAILED(autoAttCaller.rc()))
10054 {
10055 atts.clear();
10056 return autoAttCaller.rc();
10057 }
10058 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
10059
10060 if (pAtt->i_getControllerName() == aName)
10061 atts.push_back(pAtt);
10062 }
10063
10064 return S_OK;
10065}
10066
10067
10068/**
10069 * Helper for #i_saveSettings. Cares about renaming the settings directory and
10070 * file if the machine name was changed and about creating a new settings file
10071 * if this is a new machine.
10072 *
10073 * @note Must be never called directly but only from #saveSettings().
10074 */
10075HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings,
10076 bool *pfSettingsFileIsNew)
10077{
10078 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10079
10080 HRESULT rc = S_OK;
10081
10082 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
10083 /// @todo need to handle primary group change, too
10084
10085 /* attempt to rename the settings file if machine name is changed */
10086 if ( mUserData->s.fNameSync
10087 && mUserData.isBackedUp()
10088 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
10089 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
10090 )
10091 {
10092 bool dirRenamed = false;
10093 bool fileRenamed = false;
10094
10095 Utf8Str configFile, newConfigFile;
10096 Utf8Str configFilePrev, newConfigFilePrev;
10097 Utf8Str NVRAMFile, newNVRAMFile;
10098 Utf8Str configDir, newConfigDir;
10099
10100 do
10101 {
10102 int vrc = VINF_SUCCESS;
10103
10104 Utf8Str name = mUserData.backedUpData()->s.strName;
10105 Utf8Str newName = mUserData->s.strName;
10106 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
10107 if (group == "/")
10108 group.setNull();
10109 Utf8Str newGroup = mUserData->s.llGroups.front();
10110 if (newGroup == "/")
10111 newGroup.setNull();
10112
10113 configFile = mData->m_strConfigFileFull;
10114
10115 /* first, rename the directory if it matches the group and machine name */
10116 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
10117 /** @todo hack, make somehow use of ComposeMachineFilename */
10118 if (mUserData->s.fDirectoryIncludesUUID)
10119 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10120 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
10121 /** @todo hack, make somehow use of ComposeMachineFilename */
10122 if (mUserData->s.fDirectoryIncludesUUID)
10123 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
10124 configDir = configFile;
10125 configDir.stripFilename();
10126 newConfigDir = configDir;
10127 if ( configDir.length() >= groupPlusName.length()
10128 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
10129 groupPlusName.c_str()))
10130 {
10131 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10132 Utf8Str newConfigBaseDir(newConfigDir);
10133 newConfigDir.append(newGroupPlusName);
10134 /* consistency: use \ if appropriate on the platform */
10135 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10136 /* new dir and old dir cannot be equal here because of 'if'
10137 * above and because name != newName */
10138 Assert(configDir != newConfigDir);
10139 if (!fSettingsFileIsNew)
10140 {
10141 /* perform real rename only if the machine is not new */
10142 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10143 if ( vrc == VERR_FILE_NOT_FOUND
10144 || vrc == VERR_PATH_NOT_FOUND)
10145 {
10146 /* create the parent directory, then retry renaming */
10147 Utf8Str parent(newConfigDir);
10148 parent.stripFilename();
10149 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10150 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10151 }
10152 if (RT_FAILURE(vrc))
10153 {
10154 rc = setErrorBoth(E_FAIL, vrc,
10155 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10156 configDir.c_str(),
10157 newConfigDir.c_str(),
10158 vrc);
10159 break;
10160 }
10161 /* delete subdirectories which are no longer needed */
10162 Utf8Str dir(configDir);
10163 dir.stripFilename();
10164 while (dir != newConfigBaseDir && dir != ".")
10165 {
10166 vrc = RTDirRemove(dir.c_str());
10167 if (RT_FAILURE(vrc))
10168 break;
10169 dir.stripFilename();
10170 }
10171 dirRenamed = true;
10172 }
10173 }
10174
10175 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10176
10177 /* then try to rename the settings file itself */
10178 if (newConfigFile != configFile)
10179 {
10180 /* get the path to old settings file in renamed directory */
10181 Assert(mData->m_strConfigFileFull == configFile);
10182 configFile.printf("%s%c%s",
10183 newConfigDir.c_str(),
10184 RTPATH_DELIMITER,
10185 RTPathFilename(mData->m_strConfigFileFull.c_str()));
10186 if (!fSettingsFileIsNew)
10187 {
10188 /* perform real rename only if the machine is not new */
10189 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10190 if (RT_FAILURE(vrc))
10191 {
10192 rc = setErrorBoth(E_FAIL, vrc,
10193 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10194 configFile.c_str(),
10195 newConfigFile.c_str(),
10196 vrc);
10197 break;
10198 }
10199 fileRenamed = true;
10200 configFilePrev = configFile;
10201 configFilePrev += "-prev";
10202 newConfigFilePrev = newConfigFile;
10203 newConfigFilePrev += "-prev";
10204 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10205 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
10206 if (NVRAMFile.isNotEmpty())
10207 {
10208 // in the NVRAM file path, replace the old directory with the new directory
10209 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
10210 {
10211 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
10212 NVRAMFile = newConfigDir + strNVRAMFile;
10213 }
10214 newNVRAMFile = newConfigFile;
10215 newNVRAMFile.stripSuffix();
10216 newNVRAMFile += ".nvram";
10217 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
10218 }
10219 }
10220 }
10221
10222 // update m_strConfigFileFull amd mConfigFile
10223 mData->m_strConfigFileFull = newConfigFile;
10224 // compute the relative path too
10225 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10226
10227 // store the old and new so that VirtualBox::i_saveSettings() can update
10228 // the media registry
10229 if ( mData->mRegistered
10230 && (configDir != newConfigDir || configFile != newConfigFile))
10231 {
10232 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10233
10234 if (pfNeedsGlobalSaveSettings)
10235 *pfNeedsGlobalSaveSettings = true;
10236 }
10237
10238 // in the saved state file path, replace the old directory with the new directory
10239 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10240 {
10241 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
10242 mSSData->strStateFilePath = newConfigDir + strStateFileName;
10243 }
10244 if (newNVRAMFile.isNotEmpty())
10245 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
10246
10247 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
10248 if (mData->mFirstSnapshot)
10249 {
10250 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10251 newConfigDir.c_str());
10252 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
10253 newConfigDir.c_str());
10254 }
10255 }
10256 while (0);
10257
10258 if (FAILED(rc))
10259 {
10260 /* silently try to rename everything back */
10261 if (fileRenamed)
10262 {
10263 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10264 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10265 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
10266 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
10267 }
10268 if (dirRenamed)
10269 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10270 }
10271
10272 if (FAILED(rc)) return rc;
10273 }
10274
10275 if (fSettingsFileIsNew)
10276 {
10277 /* create a virgin config file */
10278 int vrc = VINF_SUCCESS;
10279
10280 /* ensure the settings directory exists */
10281 Utf8Str path(mData->m_strConfigFileFull);
10282 path.stripFilename();
10283 if (!RTDirExists(path.c_str()))
10284 {
10285 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10286 if (RT_FAILURE(vrc))
10287 {
10288 return setErrorBoth(E_FAIL, vrc,
10289 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10290 path.c_str(),
10291 vrc);
10292 }
10293 }
10294
10295 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10296 path = mData->m_strConfigFileFull;
10297 RTFILE f = NIL_RTFILE;
10298 vrc = RTFileOpen(&f, path.c_str(),
10299 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10300 if (RT_FAILURE(vrc))
10301 return setErrorBoth(E_FAIL, vrc,
10302 tr("Could not create the settings file '%s' (%Rrc)"),
10303 path.c_str(),
10304 vrc);
10305 RTFileClose(f);
10306 }
10307 if (pfSettingsFileIsNew)
10308 *pfSettingsFileIsNew = fSettingsFileIsNew;
10309
10310 return rc;
10311}
10312
10313/**
10314 * Saves and commits machine data, user data and hardware data.
10315 *
10316 * Note that on failure, the data remains uncommitted.
10317 *
10318 * @a aFlags may combine the following flags:
10319 *
10320 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10321 * Used when saving settings after an operation that makes them 100%
10322 * correspond to the settings from the current snapshot.
10323 * - SaveS_Force: settings will be saved without doing a deep compare of the
10324 * settings structures. This is used when this is called because snapshots
10325 * have changed to avoid the overhead of the deep compare.
10326 *
10327 * @note Must be called from under this object's write lock. Locks children for
10328 * writing.
10329 *
10330 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10331 * initialized to false and that will be set to true by this function if
10332 * the caller must invoke VirtualBox::i_saveSettings() because the global
10333 * settings have changed. This will happen if a machine rename has been
10334 * saved and the global machine and media registries will therefore need
10335 * updating.
10336 * @param alock Reference to the lock for this machine object.
10337 * @param aFlags Flags.
10338 */
10339HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10340 AutoWriteLock &alock,
10341 int aFlags /*= 0*/)
10342{
10343 LogFlowThisFuncEnter();
10344
10345 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10346
10347 /* make sure child objects are unable to modify the settings while we are
10348 * saving them */
10349 i_ensureNoStateDependencies(alock);
10350
10351 AssertReturn(!i_isSnapshotMachine(),
10352 E_FAIL);
10353
10354 if (!mData->mAccessible)
10355 return setError(VBOX_E_INVALID_VM_STATE,
10356 tr("The machine is not accessible, so cannot save settings"));
10357
10358 HRESULT rc = S_OK;
10359 PCVBOXCRYPTOIF pCryptoIf = NULL;
10360 const char *pszPassword = NULL;
10361 SecretKey *pKey = NULL;
10362
10363#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10364 if (mData->mstrKeyId.isNotEmpty())
10365 {
10366 /* VM is going to be encrypted. */
10367 alock.release(); /** @todo Revise the locking. */
10368 rc = mParent->i_retainCryptoIf(&pCryptoIf);
10369 alock.acquire();
10370 if (FAILED(rc)) return rc; /* Error is set. */
10371
10372 int vrc = mData->mpKeyStore->retainSecretKey(mData->mstrKeyId, &pKey);
10373 if (RT_SUCCESS(vrc))
10374 pszPassword = (const char *)pKey->getKeyBuffer();
10375 else
10376 {
10377 mParent->i_releaseCryptoIf(pCryptoIf);
10378 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
10379 tr("Failed to retain VM encryption password using ID '%s' with %Rrc"),
10380 mData->mstrKeyId.c_str(), vrc);
10381 }
10382 }
10383#else
10384 RT_NOREF(pKey);
10385#endif
10386
10387 bool fNeedsWrite = false;
10388 bool fSettingsFileIsNew = false;
10389
10390 /* First, prepare to save settings. It will care about renaming the
10391 * settings directory and file if the machine name was changed and about
10392 * creating a new settings file if this is a new machine. */
10393 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings,
10394 &fSettingsFileIsNew);
10395 if (FAILED(rc))
10396 {
10397#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10398 if (pCryptoIf)
10399 {
10400 alock.release(); /** @todo Revise the locking. */
10401 mParent->i_releaseCryptoIf(pCryptoIf);
10402 alock.acquire();
10403 }
10404 if (pKey)
10405 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10406#endif
10407 return rc;
10408 }
10409
10410 // keep a pointer to the current settings structures
10411 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10412 settings::MachineConfigFile *pNewConfig = NULL;
10413
10414 try
10415 {
10416 // make a fresh one to have everyone write stuff into
10417 pNewConfig = new settings::MachineConfigFile(NULL);
10418 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10419#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10420 pNewConfig->strKeyId = mData->mstrKeyId;
10421 pNewConfig->strKeyStore = mData->mstrKeyStore;
10422#endif
10423
10424 // now go and copy all the settings data from COM to the settings structures
10425 // (this calls i_saveSettings() on all the COM objects in the machine)
10426 i_copyMachineDataToSettings(*pNewConfig);
10427
10428 if (aFlags & SaveS_ResetCurStateModified)
10429 {
10430 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10431 mData->mCurrentStateModified = FALSE;
10432 fNeedsWrite = true; // always, no need to compare
10433 }
10434 else if (aFlags & SaveS_Force)
10435 {
10436 fNeedsWrite = true; // always, no need to compare
10437 }
10438 else
10439 {
10440 if (!mData->mCurrentStateModified)
10441 {
10442 // do a deep compare of the settings that we just saved with the settings
10443 // previously stored in the config file; this invokes MachineConfigFile::operator==
10444 // which does a deep compare of all the settings, which is expensive but less expensive
10445 // than writing out XML in vain
10446 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10447
10448 // could still be modified if any settings changed
10449 mData->mCurrentStateModified = fAnySettingsChanged;
10450
10451 fNeedsWrite = fAnySettingsChanged;
10452 }
10453 else
10454 fNeedsWrite = true;
10455 }
10456
10457 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10458
10459 if (fNeedsWrite)
10460 {
10461 // now spit it all out!
10462 pNewConfig->write(mData->m_strConfigFileFull, pCryptoIf, pszPassword);
10463 if (aFlags & SaveS_RemoveBackup)
10464 i_deleteFile(mData->m_strConfigFileFull + "-prev", true /* fIgnoreFailures */);
10465 }
10466
10467 mData->pMachineConfigFile = pNewConfig;
10468 delete pOldConfig;
10469 i_commit();
10470
10471 // after saving settings, we are no longer different from the XML on disk
10472 mData->flModifications = 0;
10473 }
10474 catch (HRESULT err)
10475 {
10476 // we assume that error info is set by the thrower
10477 rc = err;
10478
10479 // delete any newly created settings file
10480 if (fSettingsFileIsNew)
10481 i_deleteFile(mData->m_strConfigFileFull, true /* fIgnoreFailures */);
10482
10483 // restore old config
10484 delete pNewConfig;
10485 mData->pMachineConfigFile = pOldConfig;
10486 }
10487 catch (...)
10488 {
10489 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10490 }
10491
10492#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10493 if (pCryptoIf)
10494 {
10495 alock.release(); /** @todo Revise the locking. */
10496 mParent->i_releaseCryptoIf(pCryptoIf);
10497 alock.acquire();
10498 }
10499 if (pKey)
10500 mData->mpKeyStore->releaseSecretKey(mData->mstrKeyId);
10501#endif
10502
10503 if (fNeedsWrite)
10504 {
10505 /* Fire the data change event, even on failure (since we've already
10506 * committed all data). This is done only for SessionMachines because
10507 * mutable Machine instances are always not registered (i.e. private
10508 * to the client process that creates them) and thus don't need to
10509 * inform callbacks. */
10510 if (i_isSessionMachine())
10511 mParent->i_onMachineDataChanged(mData->mUuid);
10512 }
10513
10514 LogFlowThisFunc(("rc=%08X\n", rc));
10515 LogFlowThisFuncLeave();
10516 return rc;
10517}
10518
10519/**
10520 * Implementation for saving the machine settings into the given
10521 * settings::MachineConfigFile instance. This copies machine extradata
10522 * from the previous machine config file in the instance data, if any.
10523 *
10524 * This gets called from two locations:
10525 *
10526 * -- Machine::i_saveSettings(), during the regular XML writing;
10527 *
10528 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10529 * exported to OVF and we write the VirtualBox proprietary XML
10530 * into a <vbox:Machine> tag.
10531 *
10532 * This routine fills all the fields in there, including snapshots, *except*
10533 * for the following:
10534 *
10535 * -- fCurrentStateModified. There is some special logic associated with that.
10536 *
10537 * The caller can then call MachineConfigFile::write() or do something else
10538 * with it.
10539 *
10540 * Caller must hold the machine lock!
10541 *
10542 * This throws XML errors and HRESULT, so the caller must have a catch block!
10543 */
10544void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10545{
10546 // deep copy extradata, being extra careful with self assignment (the STL
10547 // map assignment on Mac OS X clang based Xcode isn't checking)
10548 if (&config != mData->pMachineConfigFile)
10549 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10550
10551 config.uuid = mData->mUuid;
10552
10553 // copy name, description, OS type, teleport, UTC etc.
10554 config.machineUserData = mUserData->s;
10555
10556#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
10557 config.strStateKeyId = mSSData->strStateKeyId;
10558 config.strStateKeyStore = mSSData->strStateKeyStore;
10559 config.strLogKeyId = mData->mstrLogKeyId;
10560 config.strLogKeyStore = mData->mstrLogKeyStore;
10561#endif
10562
10563 if ( mData->mMachineState == MachineState_Saved
10564 || mData->mMachineState == MachineState_AbortedSaved
10565 || mData->mMachineState == MachineState_Restoring
10566 // when doing certain snapshot operations we may or may not have
10567 // a saved state in the current state, so keep everything as is
10568 || ( ( mData->mMachineState == MachineState_Snapshotting
10569 || mData->mMachineState == MachineState_DeletingSnapshot
10570 || mData->mMachineState == MachineState_RestoringSnapshot)
10571 && (!mSSData->strStateFilePath.isEmpty())
10572 )
10573 )
10574 {
10575 Assert(!mSSData->strStateFilePath.isEmpty());
10576 /* try to make the file name relative to the settings file dir */
10577 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10578 }
10579 else
10580 {
10581 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10582 config.strStateFile.setNull();
10583 }
10584
10585 if (mData->mCurrentSnapshot)
10586 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10587 else
10588 config.uuidCurrentSnapshot.clear();
10589
10590 config.timeLastStateChange = mData->mLastStateChange;
10591 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10592 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10593
10594 HRESULT hrc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart, config.recordingSettings);
10595 if (FAILED(hrc)) throw hrc;
10596
10597 // save machine's media registry if this is VirtualBox 4.0 or later
10598 if (config.canHaveOwnMediaRegistry())
10599 {
10600 // determine machine folder
10601 Utf8Str strMachineFolder = i_getSettingsFileFull();
10602 strMachineFolder.stripFilename();
10603 mParent->i_saveMediaRegistry(config.mediaRegistry,
10604 i_getId(), // only media with registry ID == machine UUID
10605 strMachineFolder);
10606 // this throws HRESULT
10607 }
10608
10609 // save snapshots
10610 hrc = i_saveAllSnapshots(config);
10611 if (FAILED(hrc)) throw hrc;
10612}
10613
10614/**
10615 * Saves all snapshots of the machine into the given machine config file. Called
10616 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10617 * @param config
10618 * @return
10619 */
10620HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10621{
10622 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10623
10624 HRESULT rc = S_OK;
10625
10626 try
10627 {
10628 config.llFirstSnapshot.clear();
10629
10630 if (mData->mFirstSnapshot)
10631 {
10632 // the settings use a list for "the first snapshot"
10633 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10634
10635 // get reference to the snapshot on the list and work on that
10636 // element straight in the list to avoid excessive copying later
10637 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10638 if (FAILED(rc)) throw rc;
10639 }
10640
10641// if (mType == IsSessionMachine)
10642// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10643
10644 }
10645 catch (HRESULT err)
10646 {
10647 /* we assume that error info is set by the thrower */
10648 rc = err;
10649 }
10650 catch (...)
10651 {
10652 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10653 }
10654
10655 return rc;
10656}
10657
10658/**
10659 * Saves the VM hardware configuration. It is assumed that the
10660 * given node is empty.
10661 *
10662 * @param data Reference to the settings object for the hardware config.
10663 * @param pDbg Pointer to the settings object for the debugging config
10664 * which happens to live in mHWData.
10665 * @param pAutostart Pointer to the settings object for the autostart config
10666 * which happens to live in mHWData.
10667 * @param recording Reference to reecording settings.
10668 */
10669HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10670 settings::Autostart *pAutostart, settings::RecordingSettings &recording)
10671{
10672 HRESULT rc = S_OK;
10673
10674 try
10675 {
10676 /* The hardware version attribute (optional).
10677 Automatically upgrade from 1 to current default hardware version
10678 when there is no saved state. (ugly!) */
10679 if ( mHWData->mHWVersion == "1"
10680 && mSSData->strStateFilePath.isEmpty()
10681 )
10682 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10683
10684 data.strVersion = mHWData->mHWVersion;
10685 data.uuid = mHWData->mHardwareUUID;
10686
10687 // CPU
10688 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10689 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10690 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10691 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10692 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10693 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10694 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10695 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10696 data.fPAE = !!mHWData->mPAEEnabled;
10697 data.enmLongMode = mHWData->mLongMode;
10698 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10699 data.fAPIC = !!mHWData->mAPIC;
10700 data.fX2APIC = !!mHWData->mX2APIC;
10701 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10702 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10703 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10704 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10705 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10706 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10707 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10708 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10709 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10710 data.cCPUs = mHWData->mCPUCount;
10711 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10712 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10713 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10714 data.strCpuProfile = mHWData->mCpuProfile;
10715
10716 data.llCpus.clear();
10717 if (data.fCpuHotPlug)
10718 {
10719 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10720 {
10721 if (mHWData->mCPUAttached[idx])
10722 {
10723 settings::Cpu cpu;
10724 cpu.ulId = idx;
10725 data.llCpus.push_back(cpu);
10726 }
10727 }
10728 }
10729
10730 /* Standard and Extended CPUID leafs. */
10731 data.llCpuIdLeafs.clear();
10732 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10733
10734 // memory
10735 data.ulMemorySizeMB = mHWData->mMemorySize;
10736 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10737
10738 // firmware
10739 data.firmwareType = mHWData->mFirmwareType;
10740
10741 // HID
10742 data.pointingHIDType = mHWData->mPointingHIDType;
10743 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10744
10745 // chipset
10746 data.chipsetType = mHWData->mChipsetType;
10747
10748 // iommu
10749 data.iommuType = mHWData->mIommuType;
10750
10751 // paravirt
10752 data.paravirtProvider = mHWData->mParavirtProvider;
10753 data.strParavirtDebug = mHWData->mParavirtDebug;
10754
10755 // emulated USB card reader
10756 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10757
10758 // HPET
10759 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10760
10761 // boot order
10762 data.mapBootOrder.clear();
10763 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10764 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10765
10766 /* VRDEServer settings (optional) */
10767 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10768 if (FAILED(rc)) throw rc;
10769
10770 /* BIOS settings (required) */
10771 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10772 if (FAILED(rc)) throw rc;
10773
10774 /* Recording settings. */
10775 rc = mRecordingSettings->i_saveSettings(recording);
10776 if (FAILED(rc)) throw rc;
10777
10778 /* Trusted Platform Module settings (required) */
10779 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10780 if (FAILED(rc)) throw rc;
10781
10782 /* NVRAM settings (required) */
10783 rc = mNvramStore->i_saveSettings(data.nvramSettings);
10784 if (FAILED(rc)) throw rc;
10785
10786 /* GraphicsAdapter settings (required) */
10787 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10788 if (FAILED(rc)) throw rc;
10789
10790 /* USB Controller (required) */
10791 data.usbSettings.llUSBControllers.clear();
10792 for (USBControllerList::const_iterator
10793 it = mUSBControllers->begin();
10794 it != mUSBControllers->end();
10795 ++it)
10796 {
10797 ComObjPtr<USBController> ctrl = *it;
10798 settings::USBController settingsCtrl;
10799
10800 settingsCtrl.strName = ctrl->i_getName();
10801 settingsCtrl.enmType = ctrl->i_getControllerType();
10802
10803 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10804 }
10805
10806 /* USB device filters (required) */
10807 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10808 if (FAILED(rc)) throw rc;
10809
10810 /* Network adapters (required) */
10811 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10812 data.llNetworkAdapters.clear();
10813 /* Write out only the nominal number of network adapters for this
10814 * chipset type. Since Machine::commit() hasn't been called there
10815 * may be extra NIC settings in the vector. */
10816 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10817 {
10818 settings::NetworkAdapter nic;
10819 nic.ulSlot = (uint32_t)slot;
10820 /* paranoia check... must not be NULL, but must not crash either. */
10821 if (mNetworkAdapters[slot])
10822 {
10823 if (mNetworkAdapters[slot]->i_hasDefaults())
10824 continue;
10825
10826 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10827 if (FAILED(rc)) throw rc;
10828
10829 data.llNetworkAdapters.push_back(nic);
10830 }
10831 }
10832
10833 /* Serial ports */
10834 data.llSerialPorts.clear();
10835 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10836 {
10837 if (mSerialPorts[slot]->i_hasDefaults())
10838 continue;
10839
10840 settings::SerialPort s;
10841 s.ulSlot = slot;
10842 rc = mSerialPorts[slot]->i_saveSettings(s);
10843 if (FAILED(rc)) return rc;
10844
10845 data.llSerialPorts.push_back(s);
10846 }
10847
10848 /* Parallel ports */
10849 data.llParallelPorts.clear();
10850 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10851 {
10852 if (mParallelPorts[slot]->i_hasDefaults())
10853 continue;
10854
10855 settings::ParallelPort p;
10856 p.ulSlot = slot;
10857 rc = mParallelPorts[slot]->i_saveSettings(p);
10858 if (FAILED(rc)) return rc;
10859
10860 data.llParallelPorts.push_back(p);
10861 }
10862
10863 /* Audio settings */
10864 rc = mAudioSettings->i_saveSettings(data.audioAdapter);
10865 if (FAILED(rc)) return rc;
10866
10867 rc = i_saveStorageControllers(data.storage);
10868 if (FAILED(rc)) return rc;
10869
10870 /* Shared folders */
10871 data.llSharedFolders.clear();
10872 for (HWData::SharedFolderList::const_iterator
10873 it = mHWData->mSharedFolders.begin();
10874 it != mHWData->mSharedFolders.end();
10875 ++it)
10876 {
10877 SharedFolder *pSF = *it;
10878 AutoCaller sfCaller(pSF);
10879 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10880 settings::SharedFolder sf;
10881 sf.strName = pSF->i_getName();
10882 sf.strHostPath = pSF->i_getHostPath();
10883 sf.fWritable = !!pSF->i_isWritable();
10884 sf.fAutoMount = !!pSF->i_isAutoMounted();
10885 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10886
10887 data.llSharedFolders.push_back(sf);
10888 }
10889
10890 // clipboard
10891 data.clipboardMode = mHWData->mClipboardMode;
10892 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10893
10894 // drag'n'drop
10895 data.dndMode = mHWData->mDnDMode;
10896
10897 /* Guest */
10898 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10899
10900 // IO settings
10901 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10902 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10903
10904 /* BandwidthControl (required) */
10905 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10906 if (FAILED(rc)) throw rc;
10907
10908 /* Host PCI devices */
10909 data.pciAttachments.clear();
10910 for (HWData::PCIDeviceAssignmentList::const_iterator
10911 it = mHWData->mPCIDeviceAssignments.begin();
10912 it != mHWData->mPCIDeviceAssignments.end();
10913 ++it)
10914 {
10915 ComObjPtr<PCIDeviceAttachment> pda = *it;
10916 settings::HostPCIDeviceAttachment hpda;
10917
10918 rc = pda->i_saveSettings(hpda);
10919 if (FAILED(rc)) throw rc;
10920
10921 data.pciAttachments.push_back(hpda);
10922 }
10923
10924 // guest properties
10925 data.llGuestProperties.clear();
10926#ifdef VBOX_WITH_GUEST_PROPS
10927 for (HWData::GuestPropertyMap::const_iterator
10928 it = mHWData->mGuestProperties.begin();
10929 it != mHWData->mGuestProperties.end();
10930 ++it)
10931 {
10932 HWData::GuestProperty property = it->second;
10933
10934 /* Remove transient guest properties at shutdown unless we
10935 * are saving state. Note that restoring snapshot intentionally
10936 * keeps them, they will be removed if appropriate once the final
10937 * machine state is set (as crashes etc. need to work). */
10938 if ( ( mData->mMachineState == MachineState_PoweredOff
10939 || mData->mMachineState == MachineState_Aborted
10940 || mData->mMachineState == MachineState_Teleported)
10941 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10942 continue;
10943 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10944 prop.strName = it->first;
10945 prop.strValue = property.strValue;
10946 prop.timestamp = (uint64_t)property.mTimestamp;
10947 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10948 GuestPropWriteFlags(property.mFlags, szFlags);
10949 prop.strFlags = szFlags;
10950
10951 data.llGuestProperties.push_back(prop);
10952 }
10953
10954 /* I presume this doesn't require a backup(). */
10955 mData->mGuestPropertiesModified = FALSE;
10956#endif /* VBOX_WITH_GUEST_PROPS defined */
10957
10958 *pDbg = mHWData->mDebugging;
10959 *pAutostart = mHWData->mAutostart;
10960
10961 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10962 }
10963 catch (std::bad_alloc &)
10964 {
10965 return E_OUTOFMEMORY;
10966 }
10967
10968 AssertComRC(rc);
10969 return rc;
10970}
10971
10972/**
10973 * Saves the storage controller configuration.
10974 *
10975 * @param data storage settings.
10976 */
10977HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10978{
10979 data.llStorageControllers.clear();
10980
10981 for (StorageControllerList::const_iterator
10982 it = mStorageControllers->begin();
10983 it != mStorageControllers->end();
10984 ++it)
10985 {
10986 HRESULT rc;
10987 ComObjPtr<StorageController> pCtl = *it;
10988
10989 settings::StorageController ctl;
10990 ctl.strName = pCtl->i_getName();
10991 ctl.controllerType = pCtl->i_getControllerType();
10992 ctl.storageBus = pCtl->i_getStorageBus();
10993 ctl.ulInstance = pCtl->i_getInstance();
10994 ctl.fBootable = pCtl->i_getBootable();
10995
10996 /* Save the port count. */
10997 ULONG portCount;
10998 rc = pCtl->COMGETTER(PortCount)(&portCount);
10999 ComAssertComRCRet(rc, rc);
11000 ctl.ulPortCount = portCount;
11001
11002 /* Save fUseHostIOCache */
11003 BOOL fUseHostIOCache;
11004 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
11005 ComAssertComRCRet(rc, rc);
11006 ctl.fUseHostIOCache = !!fUseHostIOCache;
11007
11008 /* save the devices now. */
11009 rc = i_saveStorageDevices(pCtl, ctl);
11010 ComAssertComRCRet(rc, rc);
11011
11012 data.llStorageControllers.push_back(ctl);
11013 }
11014
11015 return S_OK;
11016}
11017
11018/**
11019 * Saves the hard disk configuration.
11020 */
11021HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
11022 settings::StorageController &data)
11023{
11024 MediumAttachmentList atts;
11025
11026 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
11027 if (FAILED(rc)) return rc;
11028
11029 data.llAttachedDevices.clear();
11030 for (MediumAttachmentList::const_iterator
11031 it = atts.begin();
11032 it != atts.end();
11033 ++it)
11034 {
11035 settings::AttachedDevice dev;
11036 IMediumAttachment *iA = *it;
11037 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
11038 Medium *pMedium = pAttach->i_getMedium();
11039
11040 dev.deviceType = pAttach->i_getType();
11041 dev.lPort = pAttach->i_getPort();
11042 dev.lDevice = pAttach->i_getDevice();
11043 dev.fPassThrough = pAttach->i_getPassthrough();
11044 dev.fHotPluggable = pAttach->i_getHotPluggable();
11045 if (pMedium)
11046 {
11047 if (pMedium->i_isHostDrive())
11048 dev.strHostDriveSrc = pMedium->i_getLocationFull();
11049 else
11050 dev.uuid = pMedium->i_getId();
11051 dev.fTempEject = pAttach->i_getTempEject();
11052 dev.fNonRotational = pAttach->i_getNonRotational();
11053 dev.fDiscard = pAttach->i_getDiscard();
11054 }
11055
11056 dev.strBwGroup = pAttach->i_getBandwidthGroup();
11057
11058 data.llAttachedDevices.push_back(dev);
11059 }
11060
11061 return S_OK;
11062}
11063
11064/**
11065 * Saves machine state settings as defined by aFlags
11066 * (SaveSTS_* values).
11067 *
11068 * @param aFlags Combination of SaveSTS_* flags.
11069 *
11070 * @note Locks objects for writing.
11071 */
11072HRESULT Machine::i_saveStateSettings(int aFlags)
11073{
11074 if (aFlags == 0)
11075 return S_OK;
11076
11077 AutoCaller autoCaller(this);
11078 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11079
11080 /* This object's write lock is also necessary to serialize file access
11081 * (prevent concurrent reads and writes) */
11082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11083
11084 HRESULT rc = S_OK;
11085
11086 Assert(mData->pMachineConfigFile);
11087
11088 try
11089 {
11090 if (aFlags & SaveSTS_CurStateModified)
11091 mData->pMachineConfigFile->fCurrentStateModified = true;
11092
11093 if (aFlags & SaveSTS_StateFilePath)
11094 {
11095 if (!mSSData->strStateFilePath.isEmpty())
11096 /* try to make the file name relative to the settings file dir */
11097 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
11098 else
11099 mData->pMachineConfigFile->strStateFile.setNull();
11100 }
11101
11102 if (aFlags & SaveSTS_StateTimeStamp)
11103 {
11104 Assert( mData->mMachineState != MachineState_Aborted
11105 || mSSData->strStateFilePath.isEmpty());
11106
11107 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
11108
11109 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
11110 || mData->mMachineState == MachineState_AbortedSaved);
11111/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
11112 }
11113
11114 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
11115 }
11116 catch (...)
11117 {
11118 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
11119 }
11120
11121 return rc;
11122}
11123
11124/**
11125 * Ensures that the given medium is added to a media registry. If this machine
11126 * was created with 4.0 or later, then the machine registry is used. Otherwise
11127 * the global VirtualBox media registry is used.
11128 *
11129 * Caller must NOT hold machine lock, media tree or any medium locks!
11130 *
11131 * @param pMedium
11132 */
11133void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
11134{
11135 /* Paranoia checks: do not hold machine or media tree locks. */
11136 AssertReturnVoid(!isWriteLockOnCurrentThread());
11137 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
11138
11139 ComObjPtr<Medium> pBase;
11140 {
11141 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11142 pBase = pMedium->i_getBase();
11143 }
11144
11145 /* Paranoia checks: do not hold medium locks. */
11146 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
11147 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
11148
11149 // decide which medium registry to use now that the medium is attached:
11150 Guid uuid;
11151 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
11152 if (fCanHaveOwnMediaRegistry)
11153 // machine XML is VirtualBox 4.0 or higher:
11154 uuid = i_getId(); // machine UUID
11155 else
11156 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
11157
11158 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
11159 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11160 if (pMedium->i_addRegistry(uuid))
11161 mParent->i_markRegistryModified(uuid);
11162
11163 /* For more complex hard disk structures it can happen that the base
11164 * medium isn't yet associated with any medium registry. Do that now. */
11165 if (pMedium != pBase)
11166 {
11167 /* Tree lock needed by Medium::addRegistryAll. */
11168 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11169 if (fCanHaveOwnMediaRegistry && pBase->i_removeRegistryAll(mParent->i_getGlobalRegistryId()))
11170 {
11171 treeLock.release();
11172 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
11173 treeLock.acquire();
11174 }
11175 if (pBase->i_addRegistryAll(uuid))
11176 {
11177 treeLock.release();
11178 mParent->i_markRegistryModified(uuid);
11179 }
11180 }
11181}
11182
11183/**
11184 * Physically deletes a file belonging to a machine.
11185 *
11186 * @returns HRESULT
11187 * @retval VBOX_E_FILE_ERROR on failure.
11188 * @param strFile File to delete.
11189 * @param fIgnoreFailures Whether to ignore deletion failures. Defaults to \c false.
11190 * VERR_FILE_NOT_FOUND and VERR_PATH_NOT_FOUND always will be ignored.
11191 * @param strWhat File hint which will be used when setting an error. Optional.
11192 * @param prc Where to return IPRT's error code on failure. Optional and can be NULL.
11193 */
11194HRESULT Machine::i_deleteFile(const Utf8Str &strFile, bool fIgnoreFailures /* = false */,
11195 const Utf8Str &strWhat /* = "" */, int *prc /* = NULL */)
11196{
11197 AssertReturn(strFile.isNotEmpty(), E_INVALIDARG);
11198
11199 HRESULT hrc = S_OK;
11200
11201 LogFunc(("Deleting file '%s'\n", strFile.c_str()));
11202
11203 int vrc = RTFileDelete(strFile.c_str());
11204 if (RT_FAILURE(vrc))
11205 {
11206 if ( !fIgnoreFailures
11207 /* Don't (externally) bitch about stuff which doesn't exist. */
11208 && ( vrc != VERR_FILE_NOT_FOUND
11209 && vrc != VERR_PATH_NOT_FOUND
11210 )
11211 )
11212 {
11213 LogRel(("Deleting file '%s' failed: %Rrc\n", strFile.c_str(), vrc));
11214
11215 Utf8StrFmt strError("Error deleting %s '%s' (%Rrc)",
11216 strWhat.isEmpty() ? tr("file") : strWhat.c_str(), strFile.c_str(), vrc);
11217 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, strError.c_str(),
11218 strFile.c_str(), vrc);
11219 }
11220
11221 if (prc)
11222 *prc = vrc;
11223 }
11224
11225 return hrc;
11226}
11227
11228/**
11229 * Creates differencing hard disks for all normal hard disks attached to this
11230 * machine and a new set of attachments to refer to created disks.
11231 *
11232 * Used when taking a snapshot or when deleting the current state. Gets called
11233 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
11234 *
11235 * This method assumes that mMediumAttachments contains the original hard disk
11236 * attachments it needs to create diffs for. On success, these attachments will
11237 * be replaced with the created diffs.
11238 *
11239 * Attachments with non-normal hard disks are left as is.
11240 *
11241 * If @a aOnline is @c false then the original hard disks that require implicit
11242 * diffs will be locked for reading. Otherwise it is assumed that they are
11243 * already locked for writing (when the VM was started). Note that in the latter
11244 * case it is responsibility of the caller to lock the newly created diffs for
11245 * writing if this method succeeds.
11246 *
11247 * @param aProgress Progress object to run (must contain at least as
11248 * many operations left as the number of hard disks
11249 * attached).
11250 * @param aWeight Weight of this operation.
11251 * @param aOnline Whether the VM was online prior to this operation.
11252 *
11253 * @note The progress object is not marked as completed, neither on success nor
11254 * on failure. This is a responsibility of the caller.
11255 *
11256 * @note Locks this object and the media tree for writing.
11257 */
11258HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
11259 ULONG aWeight,
11260 bool aOnline)
11261{
11262 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11263
11264 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
11265 AssertReturn(!!pProgressControl, E_INVALIDARG);
11266
11267 AutoCaller autoCaller(this);
11268 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11269
11270 AutoMultiWriteLock2 alock(this->lockHandle(),
11271 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11272
11273 /* must be in a protective state because we release the lock below */
11274 AssertReturn( mData->mMachineState == MachineState_Snapshotting
11275 || mData->mMachineState == MachineState_OnlineSnapshotting
11276 || mData->mMachineState == MachineState_LiveSnapshotting
11277 || mData->mMachineState == MachineState_RestoringSnapshot
11278 || mData->mMachineState == MachineState_DeletingSnapshot
11279 , E_FAIL);
11280
11281 HRESULT rc = S_OK;
11282
11283 // use appropriate locked media map (online or offline)
11284 MediumLockListMap lockedMediaOffline;
11285 MediumLockListMap *lockedMediaMap;
11286 if (aOnline)
11287 lockedMediaMap = &mData->mSession.mLockedMedia;
11288 else
11289 lockedMediaMap = &lockedMediaOffline;
11290
11291 try
11292 {
11293 if (!aOnline)
11294 {
11295 /* lock all attached hard disks early to detect "in use"
11296 * situations before creating actual diffs */
11297 for (MediumAttachmentList::const_iterator
11298 it = mMediumAttachments->begin();
11299 it != mMediumAttachments->end();
11300 ++it)
11301 {
11302 MediumAttachment *pAtt = *it;
11303 if (pAtt->i_getType() == DeviceType_HardDisk)
11304 {
11305 Medium *pMedium = pAtt->i_getMedium();
11306 Assert(pMedium);
11307
11308 MediumLockList *pMediumLockList(new MediumLockList());
11309 alock.release();
11310 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11311 NULL /* pToLockWrite */,
11312 false /* fMediumLockWriteAll */,
11313 NULL,
11314 *pMediumLockList);
11315 alock.acquire();
11316 if (FAILED(rc))
11317 {
11318 delete pMediumLockList;
11319 throw rc;
11320 }
11321 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11322 if (FAILED(rc))
11323 {
11324 throw setError(rc,
11325 tr("Collecting locking information for all attached media failed"));
11326 }
11327 }
11328 }
11329
11330 /* Now lock all media. If this fails, nothing is locked. */
11331 alock.release();
11332 rc = lockedMediaMap->Lock();
11333 alock.acquire();
11334 if (FAILED(rc))
11335 {
11336 throw setError(rc,
11337 tr("Locking of attached media failed"));
11338 }
11339 }
11340
11341 /* remember the current list (note that we don't use backup() since
11342 * mMediumAttachments may be already backed up) */
11343 MediumAttachmentList atts = *mMediumAttachments.data();
11344
11345 /* start from scratch */
11346 mMediumAttachments->clear();
11347
11348 /* go through remembered attachments and create diffs for normal hard
11349 * disks and attach them */
11350 for (MediumAttachmentList::const_iterator
11351 it = atts.begin();
11352 it != atts.end();
11353 ++it)
11354 {
11355 MediumAttachment *pAtt = *it;
11356
11357 DeviceType_T devType = pAtt->i_getType();
11358 Medium *pMedium = pAtt->i_getMedium();
11359
11360 if ( devType != DeviceType_HardDisk
11361 || pMedium == NULL
11362 || pMedium->i_getType() != MediumType_Normal)
11363 {
11364 /* copy the attachment as is */
11365
11366 /** @todo the progress object created in SessionMachine::TakeSnaphot
11367 * only expects operations for hard disks. Later other
11368 * device types need to show up in the progress as well. */
11369 if (devType == DeviceType_HardDisk)
11370 {
11371 if (pMedium == NULL)
11372 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11373 aWeight); // weight
11374 else
11375 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11376 pMedium->i_getBase()->i_getName().c_str()).raw(),
11377 aWeight); // weight
11378 }
11379
11380 mMediumAttachments->push_back(pAtt);
11381 continue;
11382 }
11383
11384 /* need a diff */
11385 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11386 pMedium->i_getBase()->i_getName().c_str()).raw(),
11387 aWeight); // weight
11388
11389 Utf8Str strFullSnapshotFolder;
11390 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11391
11392 ComObjPtr<Medium> diff;
11393 diff.createObject();
11394 // store the diff in the same registry as the parent
11395 // (this cannot fail here because we can't create implicit diffs for
11396 // unregistered images)
11397 Guid uuidRegistryParent;
11398 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11399 Assert(fInRegistry); NOREF(fInRegistry);
11400 rc = diff->init(mParent,
11401 pMedium->i_getPreferredDiffFormat(),
11402 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11403 uuidRegistryParent,
11404 DeviceType_HardDisk);
11405 if (FAILED(rc)) throw rc;
11406
11407 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11408 * the push_back? Looks like we're going to release medium with the
11409 * wrong kind of lock (general issue with if we fail anywhere at all)
11410 * and an orphaned VDI in the snapshots folder. */
11411
11412 /* update the appropriate lock list */
11413 MediumLockList *pMediumLockList;
11414 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11415 AssertComRCThrowRC(rc);
11416 if (aOnline)
11417 {
11418 alock.release();
11419 /* The currently attached medium will be read-only, change
11420 * the lock type to read. */
11421 rc = pMediumLockList->Update(pMedium, false);
11422 alock.acquire();
11423 AssertComRCThrowRC(rc);
11424 }
11425
11426 /* release the locks before the potentially lengthy operation */
11427 alock.release();
11428 rc = pMedium->i_createDiffStorage(diff,
11429 pMedium->i_getPreferredDiffVariant(),
11430 pMediumLockList,
11431 NULL /* aProgress */,
11432 true /* aWait */,
11433 false /* aNotify */);
11434 alock.acquire();
11435 if (FAILED(rc)) throw rc;
11436
11437 /* actual lock list update is done in Machine::i_commitMedia */
11438
11439 rc = diff->i_addBackReference(mData->mUuid);
11440 AssertComRCThrowRC(rc);
11441
11442 /* add a new attachment */
11443 ComObjPtr<MediumAttachment> attachment;
11444 attachment.createObject();
11445 rc = attachment->init(this,
11446 diff,
11447 pAtt->i_getControllerName(),
11448 pAtt->i_getPort(),
11449 pAtt->i_getDevice(),
11450 DeviceType_HardDisk,
11451 true /* aImplicit */,
11452 false /* aPassthrough */,
11453 false /* aTempEject */,
11454 pAtt->i_getNonRotational(),
11455 pAtt->i_getDiscard(),
11456 pAtt->i_getHotPluggable(),
11457 pAtt->i_getBandwidthGroup());
11458 if (FAILED(rc)) throw rc;
11459
11460 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11461 AssertComRCThrowRC(rc);
11462 mMediumAttachments->push_back(attachment);
11463 }
11464 }
11465 catch (HRESULT aRC) { rc = aRC; }
11466
11467 /* unlock all hard disks we locked when there is no VM */
11468 if (!aOnline)
11469 {
11470 ErrorInfoKeeper eik;
11471
11472 HRESULT rc1 = lockedMediaMap->Clear();
11473 AssertComRC(rc1);
11474 }
11475
11476 return rc;
11477}
11478
11479/**
11480 * Deletes implicit differencing hard disks created either by
11481 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11482 * mMediumAttachments.
11483 *
11484 * Note that to delete hard disks created by #attachDevice() this method is
11485 * called from #i_rollbackMedia() when the changes are rolled back.
11486 *
11487 * @note Locks this object and the media tree for writing.
11488 */
11489HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11490{
11491 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11492
11493 AutoCaller autoCaller(this);
11494 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11495
11496 AutoMultiWriteLock2 alock(this->lockHandle(),
11497 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11498
11499 /* We absolutely must have backed up state. */
11500 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11501
11502 /* Check if there are any implicitly created diff images. */
11503 bool fImplicitDiffs = false;
11504 for (MediumAttachmentList::const_iterator
11505 it = mMediumAttachments->begin();
11506 it != mMediumAttachments->end();
11507 ++it)
11508 {
11509 const ComObjPtr<MediumAttachment> &pAtt = *it;
11510 if (pAtt->i_isImplicit())
11511 {
11512 fImplicitDiffs = true;
11513 break;
11514 }
11515 }
11516 /* If there is nothing to do, leave early. This saves lots of image locking
11517 * effort. It also avoids a MachineStateChanged event without real reason.
11518 * This is important e.g. when loading a VM config, because there should be
11519 * no events. Otherwise API clients can become thoroughly confused for
11520 * inaccessible VMs (the code for loading VM configs uses this method for
11521 * cleanup if the config makes no sense), as they take such events as an
11522 * indication that the VM is alive, and they would force the VM config to
11523 * be reread, leading to an endless loop. */
11524 if (!fImplicitDiffs)
11525 return S_OK;
11526
11527 HRESULT rc = S_OK;
11528 MachineState_T oldState = mData->mMachineState;
11529
11530 /* will release the lock before the potentially lengthy operation,
11531 * so protect with the special state (unless already protected) */
11532 if ( oldState != MachineState_Snapshotting
11533 && oldState != MachineState_OnlineSnapshotting
11534 && oldState != MachineState_LiveSnapshotting
11535 && oldState != MachineState_RestoringSnapshot
11536 && oldState != MachineState_DeletingSnapshot
11537 && oldState != MachineState_DeletingSnapshotOnline
11538 && oldState != MachineState_DeletingSnapshotPaused
11539 )
11540 i_setMachineState(MachineState_SettingUp);
11541
11542 // use appropriate locked media map (online or offline)
11543 MediumLockListMap lockedMediaOffline;
11544 MediumLockListMap *lockedMediaMap;
11545 if (aOnline)
11546 lockedMediaMap = &mData->mSession.mLockedMedia;
11547 else
11548 lockedMediaMap = &lockedMediaOffline;
11549
11550 try
11551 {
11552 if (!aOnline)
11553 {
11554 /* lock all attached hard disks early to detect "in use"
11555 * situations before deleting actual diffs */
11556 for (MediumAttachmentList::const_iterator
11557 it = mMediumAttachments->begin();
11558 it != mMediumAttachments->end();
11559 ++it)
11560 {
11561 MediumAttachment *pAtt = *it;
11562 if (pAtt->i_getType() == DeviceType_HardDisk)
11563 {
11564 Medium *pMedium = pAtt->i_getMedium();
11565 Assert(pMedium);
11566
11567 MediumLockList *pMediumLockList(new MediumLockList());
11568 alock.release();
11569 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11570 NULL /* pToLockWrite */,
11571 false /* fMediumLockWriteAll */,
11572 NULL,
11573 *pMediumLockList);
11574 alock.acquire();
11575
11576 if (FAILED(rc))
11577 {
11578 delete pMediumLockList;
11579 throw rc;
11580 }
11581
11582 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11583 if (FAILED(rc))
11584 throw rc;
11585 }
11586 }
11587
11588 if (FAILED(rc))
11589 throw rc;
11590 } // end of offline
11591
11592 /* Lock lists are now up to date and include implicitly created media */
11593
11594 /* Go through remembered attachments and delete all implicitly created
11595 * diffs and fix up the attachment information */
11596 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11597 MediumAttachmentList implicitAtts;
11598 for (MediumAttachmentList::const_iterator
11599 it = mMediumAttachments->begin();
11600 it != mMediumAttachments->end();
11601 ++it)
11602 {
11603 ComObjPtr<MediumAttachment> pAtt = *it;
11604 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11605 if (pMedium.isNull())
11606 continue;
11607
11608 // Implicit attachments go on the list for deletion and back references are removed.
11609 if (pAtt->i_isImplicit())
11610 {
11611 /* Deassociate and mark for deletion */
11612 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11613 rc = pMedium->i_removeBackReference(mData->mUuid);
11614 if (FAILED(rc))
11615 throw rc;
11616 implicitAtts.push_back(pAtt);
11617 continue;
11618 }
11619
11620 /* Was this medium attached before? */
11621 if (!i_findAttachment(oldAtts, pMedium))
11622 {
11623 /* no: de-associate */
11624 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11625 rc = pMedium->i_removeBackReference(mData->mUuid);
11626 if (FAILED(rc))
11627 throw rc;
11628 continue;
11629 }
11630 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11631 }
11632
11633 /* If there are implicit attachments to delete, throw away the lock
11634 * map contents (which will unlock all media) since the medium
11635 * attachments will be rolled back. Below we need to completely
11636 * recreate the lock map anyway since it is infinitely complex to
11637 * do this incrementally (would need reconstructing each attachment
11638 * change, which would be extremely hairy). */
11639 if (implicitAtts.size() != 0)
11640 {
11641 ErrorInfoKeeper eik;
11642
11643 HRESULT rc1 = lockedMediaMap->Clear();
11644 AssertComRC(rc1);
11645 }
11646
11647 /* rollback hard disk changes */
11648 mMediumAttachments.rollback();
11649
11650 MultiResult mrc(S_OK);
11651
11652 // Delete unused implicit diffs.
11653 if (implicitAtts.size() != 0)
11654 {
11655 alock.release();
11656
11657 for (MediumAttachmentList::const_iterator
11658 it = implicitAtts.begin();
11659 it != implicitAtts.end();
11660 ++it)
11661 {
11662 // Remove medium associated with this attachment.
11663 ComObjPtr<MediumAttachment> pAtt = *it;
11664 Assert(pAtt);
11665 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11666 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11667 Assert(pMedium);
11668
11669 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11670 // continue on delete failure, just collect error messages
11671 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11672 pMedium->i_getLocationFull().c_str() ));
11673 mrc = rc;
11674 }
11675 // Clear the list of deleted implicit attachments now, while not
11676 // holding the lock, as it will ultimately trigger Medium::uninit()
11677 // calls which assume that the media tree lock isn't held.
11678 implicitAtts.clear();
11679
11680 alock.acquire();
11681
11682 /* if there is a VM recreate media lock map as mentioned above,
11683 * otherwise it is a waste of time and we leave things unlocked */
11684 if (aOnline)
11685 {
11686 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11687 /* must never be NULL, but better safe than sorry */
11688 if (!pMachine.isNull())
11689 {
11690 alock.release();
11691 rc = mData->mSession.mMachine->i_lockMedia();
11692 alock.acquire();
11693 if (FAILED(rc))
11694 throw rc;
11695 }
11696 }
11697 }
11698 }
11699 catch (HRESULT aRC) {rc = aRC;}
11700
11701 if (mData->mMachineState == MachineState_SettingUp)
11702 i_setMachineState(oldState);
11703
11704 /* unlock all hard disks we locked when there is no VM */
11705 if (!aOnline)
11706 {
11707 ErrorInfoKeeper eik;
11708
11709 HRESULT rc1 = lockedMediaMap->Clear();
11710 AssertComRC(rc1);
11711 }
11712
11713 return rc;
11714}
11715
11716
11717/**
11718 * Looks through the given list of media attachments for one with the given parameters
11719 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11720 * can be searched as well if needed.
11721 *
11722 * @param ll
11723 * @param aControllerName
11724 * @param aControllerPort
11725 * @param aDevice
11726 * @return
11727 */
11728MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11729 const Utf8Str &aControllerName,
11730 LONG aControllerPort,
11731 LONG aDevice)
11732{
11733 for (MediumAttachmentList::const_iterator
11734 it = ll.begin();
11735 it != ll.end();
11736 ++it)
11737 {
11738 MediumAttachment *pAttach = *it;
11739 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11740 return pAttach;
11741 }
11742
11743 return NULL;
11744}
11745
11746/**
11747 * Looks through the given list of media attachments for one with the given parameters
11748 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11749 * can be searched as well if needed.
11750 *
11751 * @param ll
11752 * @param pMedium
11753 * @return
11754 */
11755MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11756 ComObjPtr<Medium> pMedium)
11757{
11758 for (MediumAttachmentList::const_iterator
11759 it = ll.begin();
11760 it != ll.end();
11761 ++it)
11762 {
11763 MediumAttachment *pAttach = *it;
11764 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11765 if (pMediumThis == pMedium)
11766 return pAttach;
11767 }
11768
11769 return NULL;
11770}
11771
11772/**
11773 * Looks through the given list of media attachments for one with the given parameters
11774 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11775 * can be searched as well if needed.
11776 *
11777 * @param ll
11778 * @param id
11779 * @return
11780 */
11781MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11782 Guid &id)
11783{
11784 for (MediumAttachmentList::const_iterator
11785 it = ll.begin();
11786 it != ll.end();
11787 ++it)
11788 {
11789 MediumAttachment *pAttach = *it;
11790 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11791 if (pMediumThis->i_getId() == id)
11792 return pAttach;
11793 }
11794
11795 return NULL;
11796}
11797
11798/**
11799 * Main implementation for Machine::DetachDevice. This also gets called
11800 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11801 *
11802 * @param pAttach Medium attachment to detach.
11803 * @param writeLock Machine write lock which the caller must have locked once.
11804 * This may be released temporarily in here.
11805 * @param pSnapshot If NULL, then the detachment is for the current machine.
11806 * Otherwise this is for a SnapshotMachine, and this must be
11807 * its snapshot.
11808 * @return
11809 */
11810HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11811 AutoWriteLock &writeLock,
11812 Snapshot *pSnapshot)
11813{
11814 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11815 DeviceType_T mediumType = pAttach->i_getType();
11816
11817 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11818
11819 if (pAttach->i_isImplicit())
11820 {
11821 /* attempt to implicitly delete the implicitly created diff */
11822
11823 /// @todo move the implicit flag from MediumAttachment to Medium
11824 /// and forbid any hard disk operation when it is implicit. Or maybe
11825 /// a special media state for it to make it even more simple.
11826
11827 Assert(mMediumAttachments.isBackedUp());
11828
11829 /* will release the lock before the potentially lengthy operation, so
11830 * protect with the special state */
11831 MachineState_T oldState = mData->mMachineState;
11832 i_setMachineState(MachineState_SettingUp);
11833
11834 writeLock.release();
11835
11836 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11837 true /*aWait*/,
11838 false /*aNotify*/);
11839
11840 writeLock.acquire();
11841
11842 i_setMachineState(oldState);
11843
11844 if (FAILED(rc)) return rc;
11845 }
11846
11847 i_setModified(IsModified_Storage);
11848 mMediumAttachments.backup();
11849 mMediumAttachments->remove(pAttach);
11850
11851 if (!oldmedium.isNull())
11852 {
11853 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11854 if (pSnapshot)
11855 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11856 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11857 else if (mediumType != DeviceType_HardDisk)
11858 oldmedium->i_removeBackReference(mData->mUuid);
11859 }
11860
11861 return S_OK;
11862}
11863
11864/**
11865 * Goes thru all media of the given list and
11866 *
11867 * 1) calls i_detachDevice() on each of them for this machine and
11868 * 2) adds all Medium objects found in the process to the given list,
11869 * depending on cleanupMode.
11870 *
11871 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11872 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11873 * media to the list.
11874 *
11875 * This gets called from Machine::Unregister, both for the actual Machine and
11876 * the SnapshotMachine objects that might be found in the snapshots.
11877 *
11878 * Requires caller and locking. The machine lock must be passed in because it
11879 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11880 *
11881 * @param writeLock Machine lock from top-level caller; this gets passed to
11882 * i_detachDevice.
11883 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11884 * object if called for a SnapshotMachine.
11885 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11886 * added to llMedia; if Full, then all media get added;
11887 * otherwise no media get added.
11888 * @param llMedia Caller's list to receive Medium objects which got detached so
11889 * caller can close() them, depending on cleanupMode.
11890 * @return
11891 */
11892HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11893 Snapshot *pSnapshot,
11894 CleanupMode_T cleanupMode,
11895 MediaList &llMedia)
11896{
11897 Assert(isWriteLockOnCurrentThread());
11898
11899 HRESULT rc;
11900
11901 // make a temporary list because i_detachDevice invalidates iterators into
11902 // mMediumAttachments
11903 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11904
11905 for (MediumAttachmentList::iterator
11906 it = llAttachments2.begin();
11907 it != llAttachments2.end();
11908 ++it)
11909 {
11910 ComObjPtr<MediumAttachment> &pAttach = *it;
11911 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11912
11913 if (!pMedium.isNull())
11914 {
11915 AutoCaller mac(pMedium);
11916 if (FAILED(mac.rc())) return mac.rc();
11917 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11918 DeviceType_T devType = pMedium->i_getDeviceType();
11919 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11920 && devType == DeviceType_HardDisk)
11921 || (cleanupMode == CleanupMode_Full)
11922 )
11923 {
11924 llMedia.push_back(pMedium);
11925 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11926 /* Not allowed to keep this lock as below we need the parent
11927 * medium lock, and the lock order is parent to child. */
11928 lock.release();
11929 /*
11930 * Search for medias which are not attached to any machine, but
11931 * in the chain to an attached disk. Mediums are only consided
11932 * if they are:
11933 * - have only one child
11934 * - no references to any machines
11935 * - are of normal medium type
11936 */
11937 while (!pParent.isNull())
11938 {
11939 AutoCaller mac1(pParent);
11940 if (FAILED(mac1.rc())) return mac1.rc();
11941 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11942 if (pParent->i_getChildren().size() == 1)
11943 {
11944 if ( pParent->i_getMachineBackRefCount() == 0
11945 && pParent->i_getType() == MediumType_Normal
11946 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11947 llMedia.push_back(pParent);
11948 }
11949 else
11950 break;
11951 pParent = pParent->i_getParent();
11952 }
11953 }
11954 }
11955
11956 // real machine: then we need to use the proper method
11957 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11958
11959 if (FAILED(rc))
11960 return rc;
11961 }
11962
11963 return S_OK;
11964}
11965
11966/**
11967 * Perform deferred hard disk detachments.
11968 *
11969 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11970 * changed (not backed up).
11971 *
11972 * If @a aOnline is @c true then this method will also unlock the old hard
11973 * disks for which the new implicit diffs were created and will lock these new
11974 * diffs for writing.
11975 *
11976 * @param aOnline Whether the VM was online prior to this operation.
11977 *
11978 * @note Locks this object for writing!
11979 */
11980void Machine::i_commitMedia(bool aOnline /*= false*/)
11981{
11982 AutoCaller autoCaller(this);
11983 AssertComRCReturnVoid(autoCaller.rc());
11984
11985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11986
11987 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11988
11989 HRESULT rc = S_OK;
11990
11991 /* no attach/detach operations -- nothing to do */
11992 if (!mMediumAttachments.isBackedUp())
11993 return;
11994
11995 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11996 bool fMediaNeedsLocking = false;
11997
11998 /* enumerate new attachments */
11999 for (MediumAttachmentList::const_iterator
12000 it = mMediumAttachments->begin();
12001 it != mMediumAttachments->end();
12002 ++it)
12003 {
12004 MediumAttachment *pAttach = *it;
12005
12006 pAttach->i_commit();
12007
12008 Medium *pMedium = pAttach->i_getMedium();
12009 bool fImplicit = pAttach->i_isImplicit();
12010
12011 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
12012 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
12013 fImplicit));
12014
12015 /** @todo convert all this Machine-based voodoo to MediumAttachment
12016 * based commit logic. */
12017 if (fImplicit)
12018 {
12019 /* convert implicit attachment to normal */
12020 pAttach->i_setImplicit(false);
12021
12022 if ( aOnline
12023 && pMedium
12024 && pAttach->i_getType() == DeviceType_HardDisk
12025 )
12026 {
12027 /* update the appropriate lock list */
12028 MediumLockList *pMediumLockList;
12029 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
12030 AssertComRC(rc);
12031 if (pMediumLockList)
12032 {
12033 /* unlock if there's a need to change the locking */
12034 if (!fMediaNeedsLocking)
12035 {
12036 rc = mData->mSession.mLockedMedia.Unlock();
12037 AssertComRC(rc);
12038 fMediaNeedsLocking = true;
12039 }
12040 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
12041 AssertComRC(rc);
12042 rc = pMediumLockList->Append(pMedium, true);
12043 AssertComRC(rc);
12044 }
12045 }
12046
12047 continue;
12048 }
12049
12050 if (pMedium)
12051 {
12052 /* was this medium attached before? */
12053 for (MediumAttachmentList::iterator
12054 oldIt = oldAtts.begin();
12055 oldIt != oldAtts.end();
12056 ++oldIt)
12057 {
12058 MediumAttachment *pOldAttach = *oldIt;
12059 if (pOldAttach->i_getMedium() == pMedium)
12060 {
12061 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
12062
12063 /* yes: remove from old to avoid de-association */
12064 oldAtts.erase(oldIt);
12065 break;
12066 }
12067 }
12068 }
12069 }
12070
12071 /* enumerate remaining old attachments and de-associate from the
12072 * current machine state */
12073 for (MediumAttachmentList::const_iterator
12074 it = oldAtts.begin();
12075 it != oldAtts.end();
12076 ++it)
12077 {
12078 MediumAttachment *pAttach = *it;
12079 Medium *pMedium = pAttach->i_getMedium();
12080
12081 /* Detach only hard disks, since DVD/floppy media is detached
12082 * instantly in MountMedium. */
12083 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
12084 {
12085 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
12086
12087 /* now de-associate from the current machine state */
12088 rc = pMedium->i_removeBackReference(mData->mUuid);
12089 AssertComRC(rc);
12090
12091 if (aOnline)
12092 {
12093 /* unlock since medium is not used anymore */
12094 MediumLockList *pMediumLockList;
12095 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
12096 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
12097 {
12098 /* this happens for online snapshots, there the attachment
12099 * is changing, but only to a diff image created under
12100 * the old one, so there is no separate lock list */
12101 Assert(!pMediumLockList);
12102 }
12103 else
12104 {
12105 AssertComRC(rc);
12106 if (pMediumLockList)
12107 {
12108 rc = mData->mSession.mLockedMedia.Remove(pAttach);
12109 AssertComRC(rc);
12110 }
12111 }
12112 }
12113 }
12114 }
12115
12116 /* take media locks again so that the locking state is consistent */
12117 if (fMediaNeedsLocking)
12118 {
12119 Assert(aOnline);
12120 rc = mData->mSession.mLockedMedia.Lock();
12121 AssertComRC(rc);
12122 }
12123
12124 /* commit the hard disk changes */
12125 mMediumAttachments.commit();
12126
12127 if (i_isSessionMachine())
12128 {
12129 /*
12130 * Update the parent machine to point to the new owner.
12131 * This is necessary because the stored parent will point to the
12132 * session machine otherwise and cause crashes or errors later
12133 * when the session machine gets invalid.
12134 */
12135 /** @todo Change the MediumAttachment class to behave like any other
12136 * class in this regard by creating peer MediumAttachment
12137 * objects for session machines and share the data with the peer
12138 * machine.
12139 */
12140 for (MediumAttachmentList::const_iterator
12141 it = mMediumAttachments->begin();
12142 it != mMediumAttachments->end();
12143 ++it)
12144 (*it)->i_updateParentMachine(mPeer);
12145
12146 /* attach new data to the primary machine and reshare it */
12147 mPeer->mMediumAttachments.attach(mMediumAttachments);
12148 }
12149
12150 return;
12151}
12152
12153/**
12154 * Perform deferred deletion of implicitly created diffs.
12155 *
12156 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
12157 * changed (not backed up).
12158 *
12159 * @note Locks this object for writing!
12160 */
12161void Machine::i_rollbackMedia()
12162{
12163 AutoCaller autoCaller(this);
12164 AssertComRCReturnVoid(autoCaller.rc());
12165
12166 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12167 LogFlowThisFunc(("Entering rollbackMedia\n"));
12168
12169 HRESULT rc = S_OK;
12170
12171 /* no attach/detach operations -- nothing to do */
12172 if (!mMediumAttachments.isBackedUp())
12173 return;
12174
12175 /* enumerate new attachments */
12176 for (MediumAttachmentList::const_iterator
12177 it = mMediumAttachments->begin();
12178 it != mMediumAttachments->end();
12179 ++it)
12180 {
12181 MediumAttachment *pAttach = *it;
12182 /* Fix up the backrefs for DVD/floppy media. */
12183 if (pAttach->i_getType() != DeviceType_HardDisk)
12184 {
12185 Medium *pMedium = pAttach->i_getMedium();
12186 if (pMedium)
12187 {
12188 rc = pMedium->i_removeBackReference(mData->mUuid);
12189 AssertComRC(rc);
12190 }
12191 }
12192
12193 (*it)->i_rollback();
12194
12195 pAttach = *it;
12196 /* Fix up the backrefs for DVD/floppy media. */
12197 if (pAttach->i_getType() != DeviceType_HardDisk)
12198 {
12199 Medium *pMedium = pAttach->i_getMedium();
12200 if (pMedium)
12201 {
12202 rc = pMedium->i_addBackReference(mData->mUuid);
12203 AssertComRC(rc);
12204 }
12205 }
12206 }
12207
12208 /** @todo convert all this Machine-based voodoo to MediumAttachment
12209 * based rollback logic. */
12210 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
12211
12212 return;
12213}
12214
12215/**
12216 * Returns true if the settings file is located in the directory named exactly
12217 * as the machine; this means, among other things, that the machine directory
12218 * should be auto-renamed.
12219 *
12220 * @param aSettingsDir if not NULL, the full machine settings file directory
12221 * name will be assigned there.
12222 *
12223 * @note Doesn't lock anything.
12224 * @note Not thread safe (must be called from this object's lock).
12225 */
12226bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
12227{
12228 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12229 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
12230 if (aSettingsDir)
12231 *aSettingsDir = strMachineDirName;
12232 strMachineDirName.stripPath(); // vmname
12233 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12234 strConfigFileOnly.stripPath() // vmname.vbox
12235 .stripSuffix(); // vmname
12236 /** @todo hack, make somehow use of ComposeMachineFilename */
12237 if (mUserData->s.fDirectoryIncludesUUID)
12238 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
12239
12240 AssertReturn(!strMachineDirName.isEmpty(), false);
12241 AssertReturn(!strConfigFileOnly.isEmpty(), false);
12242
12243 return strMachineDirName == strConfigFileOnly;
12244}
12245
12246/**
12247 * Discards all changes to machine settings.
12248 *
12249 * @param aNotify Whether to notify the direct session about changes or not.
12250 *
12251 * @note Locks objects for writing!
12252 */
12253void Machine::i_rollback(bool aNotify)
12254{
12255 AutoCaller autoCaller(this);
12256 AssertComRCReturn(autoCaller.rc(), (void)0);
12257
12258 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12259
12260 if (!mStorageControllers.isNull())
12261 {
12262 if (mStorageControllers.isBackedUp())
12263 {
12264 /* unitialize all new devices (absent in the backed up list). */
12265 StorageControllerList *backedList = mStorageControllers.backedUpData();
12266 for (StorageControllerList::const_iterator
12267 it = mStorageControllers->begin();
12268 it != mStorageControllers->end();
12269 ++it)
12270 {
12271 if ( std::find(backedList->begin(), backedList->end(), *it)
12272 == backedList->end()
12273 )
12274 {
12275 (*it)->uninit();
12276 }
12277 }
12278
12279 /* restore the list */
12280 mStorageControllers.rollback();
12281 }
12282
12283 /* rollback any changes to devices after restoring the list */
12284 if (mData->flModifications & IsModified_Storage)
12285 {
12286 for (StorageControllerList::const_iterator
12287 it = mStorageControllers->begin();
12288 it != mStorageControllers->end();
12289 ++it)
12290 {
12291 (*it)->i_rollback();
12292 }
12293 }
12294 }
12295
12296 if (!mUSBControllers.isNull())
12297 {
12298 if (mUSBControllers.isBackedUp())
12299 {
12300 /* unitialize all new devices (absent in the backed up list). */
12301 USBControllerList *backedList = mUSBControllers.backedUpData();
12302 for (USBControllerList::const_iterator
12303 it = mUSBControllers->begin();
12304 it != mUSBControllers->end();
12305 ++it)
12306 {
12307 if ( std::find(backedList->begin(), backedList->end(), *it)
12308 == backedList->end()
12309 )
12310 {
12311 (*it)->uninit();
12312 }
12313 }
12314
12315 /* restore the list */
12316 mUSBControllers.rollback();
12317 }
12318
12319 /* rollback any changes to devices after restoring the list */
12320 if (mData->flModifications & IsModified_USB)
12321 {
12322 for (USBControllerList::const_iterator
12323 it = mUSBControllers->begin();
12324 it != mUSBControllers->end();
12325 ++it)
12326 {
12327 (*it)->i_rollback();
12328 }
12329 }
12330 }
12331
12332 mUserData.rollback();
12333
12334 mHWData.rollback();
12335
12336 if (mData->flModifications & IsModified_Storage)
12337 i_rollbackMedia();
12338
12339 if (mBIOSSettings)
12340 mBIOSSettings->i_rollback();
12341
12342 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
12343 mRecordingSettings->i_rollback();
12344
12345 if (mTrustedPlatformModule)
12346 mTrustedPlatformModule->i_rollback();
12347
12348 if (mNvramStore)
12349 mNvramStore->i_rollback();
12350
12351 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
12352 mGraphicsAdapter->i_rollback();
12353
12354 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12355 mVRDEServer->i_rollback();
12356
12357 if (mAudioSettings && (mData->flModifications & IsModified_AudioSettings))
12358 mAudioSettings->i_rollback();
12359
12360 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12361 mUSBDeviceFilters->i_rollback();
12362
12363 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12364 mBandwidthControl->i_rollback();
12365
12366 if (!mHWData.isNull())
12367 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12368 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12369 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12370 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12371
12372 if (mData->flModifications & IsModified_NetworkAdapters)
12373 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12374 if ( mNetworkAdapters[slot]
12375 && mNetworkAdapters[slot]->i_isModified())
12376 {
12377 mNetworkAdapters[slot]->i_rollback();
12378 networkAdapters[slot] = mNetworkAdapters[slot];
12379 }
12380
12381 if (mData->flModifications & IsModified_SerialPorts)
12382 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12383 if ( mSerialPorts[slot]
12384 && mSerialPorts[slot]->i_isModified())
12385 {
12386 mSerialPorts[slot]->i_rollback();
12387 serialPorts[slot] = mSerialPorts[slot];
12388 }
12389
12390 if (mData->flModifications & IsModified_ParallelPorts)
12391 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12392 if ( mParallelPorts[slot]
12393 && mParallelPorts[slot]->i_isModified())
12394 {
12395 mParallelPorts[slot]->i_rollback();
12396 parallelPorts[slot] = mParallelPorts[slot];
12397 }
12398
12399 if (aNotify)
12400 {
12401 /* inform the direct session about changes */
12402
12403 ComObjPtr<Machine> that = this;
12404 uint32_t flModifications = mData->flModifications;
12405 alock.release();
12406
12407 if (flModifications & IsModified_SharedFolders)
12408 that->i_onSharedFolderChange();
12409
12410 if (flModifications & IsModified_VRDEServer)
12411 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12412 if (flModifications & IsModified_USB)
12413 that->i_onUSBControllerChange();
12414
12415 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12416 if (networkAdapters[slot])
12417 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12418 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12419 if (serialPorts[slot])
12420 that->i_onSerialPortChange(serialPorts[slot]);
12421 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12422 if (parallelPorts[slot])
12423 that->i_onParallelPortChange(parallelPorts[slot]);
12424
12425 if (flModifications & IsModified_Storage)
12426 {
12427 for (StorageControllerList::const_iterator
12428 it = mStorageControllers->begin();
12429 it != mStorageControllers->end();
12430 ++it)
12431 {
12432 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
12433 }
12434 }
12435
12436
12437#if 0
12438 if (flModifications & IsModified_BandwidthControl)
12439 that->onBandwidthControlChange();
12440#endif
12441 }
12442}
12443
12444/**
12445 * Commits all the changes to machine settings.
12446 *
12447 * Note that this operation is supposed to never fail.
12448 *
12449 * @note Locks this object and children for writing.
12450 */
12451void Machine::i_commit()
12452{
12453 AutoCaller autoCaller(this);
12454 AssertComRCReturnVoid(autoCaller.rc());
12455
12456 AutoCaller peerCaller(mPeer);
12457 AssertComRCReturnVoid(peerCaller.rc());
12458
12459 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12460
12461 /*
12462 * use safe commit to ensure Snapshot machines (that share mUserData)
12463 * will still refer to a valid memory location
12464 */
12465 mUserData.commitCopy();
12466
12467 mHWData.commit();
12468
12469 if (mMediumAttachments.isBackedUp())
12470 i_commitMedia(Global::IsOnline(mData->mMachineState));
12471
12472 mBIOSSettings->i_commit();
12473 mRecordingSettings->i_commit();
12474 mTrustedPlatformModule->i_commit();
12475 mNvramStore->i_commit();
12476 mGraphicsAdapter->i_commit();
12477 mVRDEServer->i_commit();
12478 mAudioSettings->i_commit();
12479 mUSBDeviceFilters->i_commit();
12480 mBandwidthControl->i_commit();
12481
12482 /* Since mNetworkAdapters is a list which might have been changed (resized)
12483 * without using the Backupable<> template we need to handle the copying
12484 * of the list entries manually, including the creation of peers for the
12485 * new objects. */
12486 bool commitNetworkAdapters = false;
12487 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12488 if (mPeer)
12489 {
12490 /* commit everything, even the ones which will go away */
12491 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12492 mNetworkAdapters[slot]->i_commit();
12493 /* copy over the new entries, creating a peer and uninit the original */
12494 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12495 for (size_t slot = 0; slot < newSize; slot++)
12496 {
12497 /* look if this adapter has a peer device */
12498 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12499 if (!peer)
12500 {
12501 /* no peer means the adapter is a newly created one;
12502 * create a peer owning data this data share it with */
12503 peer.createObject();
12504 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12505 }
12506 mPeer->mNetworkAdapters[slot] = peer;
12507 }
12508 /* uninit any no longer needed network adapters */
12509 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12510 mNetworkAdapters[slot]->uninit();
12511 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12512 {
12513 if (mPeer->mNetworkAdapters[slot])
12514 mPeer->mNetworkAdapters[slot]->uninit();
12515 }
12516 /* Keep the original network adapter count until this point, so that
12517 * discarding a chipset type change will not lose settings. */
12518 mNetworkAdapters.resize(newSize);
12519 mPeer->mNetworkAdapters.resize(newSize);
12520 }
12521 else
12522 {
12523 /* we have no peer (our parent is the newly created machine);
12524 * just commit changes to the network adapters */
12525 commitNetworkAdapters = true;
12526 }
12527 if (commitNetworkAdapters)
12528 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12529 mNetworkAdapters[slot]->i_commit();
12530
12531 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12532 mSerialPorts[slot]->i_commit();
12533 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12534 mParallelPorts[slot]->i_commit();
12535
12536 bool commitStorageControllers = false;
12537
12538 if (mStorageControllers.isBackedUp())
12539 {
12540 mStorageControllers.commit();
12541
12542 if (mPeer)
12543 {
12544 /* Commit all changes to new controllers (this will reshare data with
12545 * peers for those who have peers) */
12546 StorageControllerList *newList = new StorageControllerList();
12547 for (StorageControllerList::const_iterator
12548 it = mStorageControllers->begin();
12549 it != mStorageControllers->end();
12550 ++it)
12551 {
12552 (*it)->i_commit();
12553
12554 /* look if this controller has a peer device */
12555 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12556 if (!peer)
12557 {
12558 /* no peer means the device is a newly created one;
12559 * create a peer owning data this device share it with */
12560 peer.createObject();
12561 peer->init(mPeer, *it, true /* aReshare */);
12562 }
12563 else
12564 {
12565 /* remove peer from the old list */
12566 mPeer->mStorageControllers->remove(peer);
12567 }
12568 /* and add it to the new list */
12569 newList->push_back(peer);
12570 }
12571
12572 /* uninit old peer's controllers that are left */
12573 for (StorageControllerList::const_iterator
12574 it = mPeer->mStorageControllers->begin();
12575 it != mPeer->mStorageControllers->end();
12576 ++it)
12577 {
12578 (*it)->uninit();
12579 }
12580
12581 /* attach new list of controllers to our peer */
12582 mPeer->mStorageControllers.attach(newList);
12583 }
12584 else
12585 {
12586 /* we have no peer (our parent is the newly created machine);
12587 * just commit changes to devices */
12588 commitStorageControllers = true;
12589 }
12590 }
12591 else
12592 {
12593 /* the list of controllers itself is not changed,
12594 * just commit changes to controllers themselves */
12595 commitStorageControllers = true;
12596 }
12597
12598 if (commitStorageControllers)
12599 {
12600 for (StorageControllerList::const_iterator
12601 it = mStorageControllers->begin();
12602 it != mStorageControllers->end();
12603 ++it)
12604 {
12605 (*it)->i_commit();
12606 }
12607 }
12608
12609 bool commitUSBControllers = false;
12610
12611 if (mUSBControllers.isBackedUp())
12612 {
12613 mUSBControllers.commit();
12614
12615 if (mPeer)
12616 {
12617 /* Commit all changes to new controllers (this will reshare data with
12618 * peers for those who have peers) */
12619 USBControllerList *newList = new USBControllerList();
12620 for (USBControllerList::const_iterator
12621 it = mUSBControllers->begin();
12622 it != mUSBControllers->end();
12623 ++it)
12624 {
12625 (*it)->i_commit();
12626
12627 /* look if this controller has a peer device */
12628 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12629 if (!peer)
12630 {
12631 /* no peer means the device is a newly created one;
12632 * create a peer owning data this device share it with */
12633 peer.createObject();
12634 peer->init(mPeer, *it, true /* aReshare */);
12635 }
12636 else
12637 {
12638 /* remove peer from the old list */
12639 mPeer->mUSBControllers->remove(peer);
12640 }
12641 /* and add it to the new list */
12642 newList->push_back(peer);
12643 }
12644
12645 /* uninit old peer's controllers that are left */
12646 for (USBControllerList::const_iterator
12647 it = mPeer->mUSBControllers->begin();
12648 it != mPeer->mUSBControllers->end();
12649 ++it)
12650 {
12651 (*it)->uninit();
12652 }
12653
12654 /* attach new list of controllers to our peer */
12655 mPeer->mUSBControllers.attach(newList);
12656 }
12657 else
12658 {
12659 /* we have no peer (our parent is the newly created machine);
12660 * just commit changes to devices */
12661 commitUSBControllers = true;
12662 }
12663 }
12664 else
12665 {
12666 /* the list of controllers itself is not changed,
12667 * just commit changes to controllers themselves */
12668 commitUSBControllers = true;
12669 }
12670
12671 if (commitUSBControllers)
12672 {
12673 for (USBControllerList::const_iterator
12674 it = mUSBControllers->begin();
12675 it != mUSBControllers->end();
12676 ++it)
12677 {
12678 (*it)->i_commit();
12679 }
12680 }
12681
12682 if (i_isSessionMachine())
12683 {
12684 /* attach new data to the primary machine and reshare it */
12685 mPeer->mUserData.attach(mUserData);
12686 mPeer->mHWData.attach(mHWData);
12687 /* mmMediumAttachments is reshared by fixupMedia */
12688 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12689 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12690 }
12691}
12692
12693/**
12694 * Copies all the hardware data from the given machine.
12695 *
12696 * Currently, only called when the VM is being restored from a snapshot. In
12697 * particular, this implies that the VM is not running during this method's
12698 * call.
12699 *
12700 * @note This method must be called from under this object's lock.
12701 *
12702 * @note This method doesn't call #i_commit(), so all data remains backed up and
12703 * unsaved.
12704 */
12705void Machine::i_copyFrom(Machine *aThat)
12706{
12707 AssertReturnVoid(!i_isSnapshotMachine());
12708 AssertReturnVoid(aThat->i_isSnapshotMachine());
12709
12710 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12711
12712 mHWData.assignCopy(aThat->mHWData);
12713
12714 // create copies of all shared folders (mHWData after attaching a copy
12715 // contains just references to original objects)
12716 for (HWData::SharedFolderList::iterator
12717 it = mHWData->mSharedFolders.begin();
12718 it != mHWData->mSharedFolders.end();
12719 ++it)
12720 {
12721 ComObjPtr<SharedFolder> folder;
12722 folder.createObject();
12723 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12724 AssertComRC(rc);
12725 *it = folder;
12726 }
12727
12728 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12729 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12730 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12731 mNvramStore->i_copyFrom(aThat->mNvramStore);
12732 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12733 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12734 mAudioSettings->i_copyFrom(aThat->mAudioSettings);
12735 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12736 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12737
12738 /* create private copies of all controllers */
12739 mStorageControllers.backup();
12740 mStorageControllers->clear();
12741 for (StorageControllerList::const_iterator
12742 it = aThat->mStorageControllers->begin();
12743 it != aThat->mStorageControllers->end();
12744 ++it)
12745 {
12746 ComObjPtr<StorageController> ctrl;
12747 ctrl.createObject();
12748 ctrl->initCopy(this, *it);
12749 mStorageControllers->push_back(ctrl);
12750 }
12751
12752 /* create private copies of all USB controllers */
12753 mUSBControllers.backup();
12754 mUSBControllers->clear();
12755 for (USBControllerList::const_iterator
12756 it = aThat->mUSBControllers->begin();
12757 it != aThat->mUSBControllers->end();
12758 ++it)
12759 {
12760 ComObjPtr<USBController> ctrl;
12761 ctrl.createObject();
12762 ctrl->initCopy(this, *it);
12763 mUSBControllers->push_back(ctrl);
12764 }
12765
12766 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12767 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12768 {
12769 if (mNetworkAdapters[slot].isNotNull())
12770 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12771 else
12772 {
12773 unconst(mNetworkAdapters[slot]).createObject();
12774 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12775 }
12776 }
12777 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12778 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12779 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12780 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12781}
12782
12783/**
12784 * Returns whether the given storage controller is hotplug capable.
12785 *
12786 * @returns true if the controller supports hotplugging
12787 * false otherwise.
12788 * @param enmCtrlType The controller type to check for.
12789 */
12790bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12791{
12792 ComPtr<ISystemProperties> systemProperties;
12793 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12794 if (FAILED(rc))
12795 return false;
12796
12797 BOOL aHotplugCapable = FALSE;
12798 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12799
12800 return RT_BOOL(aHotplugCapable);
12801}
12802
12803#ifdef VBOX_WITH_RESOURCE_USAGE_API
12804
12805void Machine::i_getDiskList(MediaList &list)
12806{
12807 for (MediumAttachmentList::const_iterator
12808 it = mMediumAttachments->begin();
12809 it != mMediumAttachments->end();
12810 ++it)
12811 {
12812 MediumAttachment *pAttach = *it;
12813 /* just in case */
12814 AssertContinue(pAttach);
12815
12816 AutoCaller localAutoCallerA(pAttach);
12817 if (FAILED(localAutoCallerA.rc())) continue;
12818
12819 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12820
12821 if (pAttach->i_getType() == DeviceType_HardDisk)
12822 list.push_back(pAttach->i_getMedium());
12823 }
12824}
12825
12826void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12827{
12828 AssertReturnVoid(isWriteLockOnCurrentThread());
12829 AssertPtrReturnVoid(aCollector);
12830
12831 pm::CollectorHAL *hal = aCollector->getHAL();
12832 /* Create sub metrics */
12833 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12834 "Percentage of processor time spent in user mode by the VM process.");
12835 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12836 "Percentage of processor time spent in kernel mode by the VM process.");
12837 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12838 "Size of resident portion of VM process in memory.");
12839 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12840 "Actual size of all VM disks combined.");
12841 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12842 "Network receive rate.");
12843 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12844 "Network transmit rate.");
12845 /* Create and register base metrics */
12846 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12847 cpuLoadUser, cpuLoadKernel);
12848 aCollector->registerBaseMetric(cpuLoad);
12849 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12850 ramUsageUsed);
12851 aCollector->registerBaseMetric(ramUsage);
12852 MediaList disks;
12853 i_getDiskList(disks);
12854 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12855 diskUsageUsed);
12856 aCollector->registerBaseMetric(diskUsage);
12857
12858 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12859 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12860 new pm::AggregateAvg()));
12861 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12862 new pm::AggregateMin()));
12863 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12864 new pm::AggregateMax()));
12865 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12866 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12867 new pm::AggregateAvg()));
12868 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12869 new pm::AggregateMin()));
12870 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12871 new pm::AggregateMax()));
12872
12873 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12874 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12875 new pm::AggregateAvg()));
12876 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12877 new pm::AggregateMin()));
12878 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12879 new pm::AggregateMax()));
12880
12881 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12882 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12883 new pm::AggregateAvg()));
12884 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12885 new pm::AggregateMin()));
12886 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12887 new pm::AggregateMax()));
12888
12889
12890 /* Guest metrics collector */
12891 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12892 aCollector->registerGuest(mCollectorGuest);
12893 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12894
12895 /* Create sub metrics */
12896 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12897 "Percentage of processor time spent in user mode as seen by the guest.");
12898 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12899 "Percentage of processor time spent in kernel mode as seen by the guest.");
12900 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12901 "Percentage of processor time spent idling as seen by the guest.");
12902
12903 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12904 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12905 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12906 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12907 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12908 pm::SubMetric *guestMemCache = new pm::SubMetric(
12909 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12910
12911 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12912 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12913
12914 /* Create and register base metrics */
12915 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12916 machineNetRx, machineNetTx);
12917 aCollector->registerBaseMetric(machineNetRate);
12918
12919 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12920 guestLoadUser, guestLoadKernel, guestLoadIdle);
12921 aCollector->registerBaseMetric(guestCpuLoad);
12922
12923 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12924 guestMemTotal, guestMemFree,
12925 guestMemBalloon, guestMemShared,
12926 guestMemCache, guestPagedTotal);
12927 aCollector->registerBaseMetric(guestCpuMem);
12928
12929 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12930 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12931 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12932 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12933
12934 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12935 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12936 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12937 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12938
12939 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12940 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12941 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12942 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12943
12944 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12945 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12946 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12947 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12948
12949 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12950 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12951 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12952 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12953
12954 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12955 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12956 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12957 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12958
12959 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12960 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12961 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12962 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12963
12964 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12965 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12966 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12967 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12968
12969 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12970 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12971 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12972 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12973
12974 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12975 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12976 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12977 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12978
12979 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12980 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12981 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12982 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12983}
12984
12985void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12986{
12987 AssertReturnVoid(isWriteLockOnCurrentThread());
12988
12989 if (aCollector)
12990 {
12991 aCollector->unregisterMetricsFor(aMachine);
12992 aCollector->unregisterBaseMetricsFor(aMachine);
12993 }
12994}
12995
12996#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12997
12998
12999////////////////////////////////////////////////////////////////////////////////
13000
13001DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
13002
13003HRESULT SessionMachine::FinalConstruct()
13004{
13005 LogFlowThisFunc(("\n"));
13006
13007 mClientToken = NULL;
13008
13009 return BaseFinalConstruct();
13010}
13011
13012void SessionMachine::FinalRelease()
13013{
13014 LogFlowThisFunc(("\n"));
13015
13016 Assert(!mClientToken);
13017 /* paranoia, should not hang around any more */
13018 if (mClientToken)
13019 {
13020 delete mClientToken;
13021 mClientToken = NULL;
13022 }
13023
13024 uninit(Uninit::Unexpected);
13025
13026 BaseFinalRelease();
13027}
13028
13029/**
13030 * @note Must be called only by Machine::LockMachine() from its own write lock.
13031 */
13032HRESULT SessionMachine::init(Machine *aMachine)
13033{
13034 LogFlowThisFuncEnter();
13035 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
13036
13037 AssertReturn(aMachine, E_INVALIDARG);
13038
13039 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
13040
13041 /* Enclose the state transition NotReady->InInit->Ready */
13042 AutoInitSpan autoInitSpan(this);
13043 AssertReturn(autoInitSpan.isOk(), E_FAIL);
13044
13045 HRESULT rc = S_OK;
13046
13047 RT_ZERO(mAuthLibCtx);
13048
13049 /* create the machine client token */
13050 try
13051 {
13052 mClientToken = new ClientToken(aMachine, this);
13053 if (!mClientToken->isReady())
13054 {
13055 delete mClientToken;
13056 mClientToken = NULL;
13057 rc = E_FAIL;
13058 }
13059 }
13060 catch (std::bad_alloc &)
13061 {
13062 rc = E_OUTOFMEMORY;
13063 }
13064 if (FAILED(rc))
13065 return rc;
13066
13067 /* memorize the peer Machine */
13068 unconst(mPeer) = aMachine;
13069 /* share the parent pointer */
13070 unconst(mParent) = aMachine->mParent;
13071
13072 /* take the pointers to data to share */
13073 mData.share(aMachine->mData);
13074 mSSData.share(aMachine->mSSData);
13075
13076 mUserData.share(aMachine->mUserData);
13077 mHWData.share(aMachine->mHWData);
13078 mMediumAttachments.share(aMachine->mMediumAttachments);
13079
13080 mStorageControllers.allocate();
13081 for (StorageControllerList::const_iterator
13082 it = aMachine->mStorageControllers->begin();
13083 it != aMachine->mStorageControllers->end();
13084 ++it)
13085 {
13086 ComObjPtr<StorageController> ctl;
13087 ctl.createObject();
13088 ctl->init(this, *it);
13089 mStorageControllers->push_back(ctl);
13090 }
13091
13092 mUSBControllers.allocate();
13093 for (USBControllerList::const_iterator
13094 it = aMachine->mUSBControllers->begin();
13095 it != aMachine->mUSBControllers->end();
13096 ++it)
13097 {
13098 ComObjPtr<USBController> ctl;
13099 ctl.createObject();
13100 ctl->init(this, *it);
13101 mUSBControllers->push_back(ctl);
13102 }
13103
13104 unconst(mBIOSSettings).createObject();
13105 mBIOSSettings->init(this, aMachine->mBIOSSettings);
13106
13107 unconst(mRecordingSettings).createObject();
13108 mRecordingSettings->init(this, aMachine->mRecordingSettings);
13109
13110 unconst(mTrustedPlatformModule).createObject();
13111 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
13112
13113 unconst(mNvramStore).createObject();
13114 mNvramStore->init(this, aMachine->mNvramStore);
13115
13116 /* create another GraphicsAdapter object that will be mutable */
13117 unconst(mGraphicsAdapter).createObject();
13118 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
13119 /* create another VRDEServer object that will be mutable */
13120 unconst(mVRDEServer).createObject();
13121 mVRDEServer->init(this, aMachine->mVRDEServer);
13122 /* create another audio settings object that will be mutable */
13123 unconst(mAudioSettings).createObject();
13124 mAudioSettings->init(this, aMachine->mAudioSettings);
13125 /* create a list of serial ports that will be mutable */
13126 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
13127 {
13128 unconst(mSerialPorts[slot]).createObject();
13129 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
13130 }
13131 /* create a list of parallel ports that will be mutable */
13132 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
13133 {
13134 unconst(mParallelPorts[slot]).createObject();
13135 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
13136 }
13137
13138 /* create another USB device filters object that will be mutable */
13139 unconst(mUSBDeviceFilters).createObject();
13140 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
13141
13142 /* create a list of network adapters that will be mutable */
13143 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
13144 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13145 {
13146 unconst(mNetworkAdapters[slot]).createObject();
13147 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
13148 }
13149
13150 /* create another bandwidth control object that will be mutable */
13151 unconst(mBandwidthControl).createObject();
13152 mBandwidthControl->init(this, aMachine->mBandwidthControl);
13153
13154 /* default is to delete saved state on Saved -> PoweredOff transition */
13155 mRemoveSavedState = true;
13156
13157 /* Confirm a successful initialization when it's the case */
13158 autoInitSpan.setSucceeded();
13159
13160 miNATNetworksStarted = 0;
13161
13162 LogFlowThisFuncLeave();
13163 return rc;
13164}
13165
13166/**
13167 * Uninitializes this session object. If the reason is other than
13168 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
13169 * or the client watcher code.
13170 *
13171 * @param aReason uninitialization reason
13172 *
13173 * @note Locks mParent + this object for writing.
13174 */
13175void SessionMachine::uninit(Uninit::Reason aReason)
13176{
13177 LogFlowThisFuncEnter();
13178 LogFlowThisFunc(("reason=%d\n", aReason));
13179
13180 /*
13181 * Strongly reference ourselves to prevent this object deletion after
13182 * mData->mSession.mMachine.setNull() below (which can release the last
13183 * reference and call the destructor). Important: this must be done before
13184 * accessing any members (and before AutoUninitSpan that does it as well).
13185 * This self reference will be released as the very last step on return.
13186 */
13187 ComObjPtr<SessionMachine> selfRef;
13188 if (aReason != Uninit::Unexpected)
13189 selfRef = this;
13190
13191 /* Enclose the state transition Ready->InUninit->NotReady */
13192 AutoUninitSpan autoUninitSpan(this);
13193 if (autoUninitSpan.uninitDone())
13194 {
13195 LogFlowThisFunc(("Already uninitialized\n"));
13196 LogFlowThisFuncLeave();
13197 return;
13198 }
13199
13200 if (autoUninitSpan.initFailed())
13201 {
13202 /* We've been called by init() because it's failed. It's not really
13203 * necessary (nor it's safe) to perform the regular uninit sequence
13204 * below, the following is enough.
13205 */
13206 LogFlowThisFunc(("Initialization failed.\n"));
13207 /* destroy the machine client token */
13208 if (mClientToken)
13209 {
13210 delete mClientToken;
13211 mClientToken = NULL;
13212 }
13213 uninitDataAndChildObjects();
13214 mData.free();
13215 unconst(mParent) = NULL;
13216 unconst(mPeer) = NULL;
13217 LogFlowThisFuncLeave();
13218 return;
13219 }
13220
13221 MachineState_T lastState;
13222 {
13223 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
13224 lastState = mData->mMachineState;
13225 }
13226 NOREF(lastState);
13227
13228#ifdef VBOX_WITH_USB
13229 // release all captured USB devices, but do this before requesting the locks below
13230 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
13231 {
13232 /* Console::captureUSBDevices() is called in the VM process only after
13233 * setting the machine state to Starting or Restoring.
13234 * Console::detachAllUSBDevices() will be called upon successful
13235 * termination. So, we need to release USB devices only if there was
13236 * an abnormal termination of a running VM.
13237 *
13238 * This is identical to SessionMachine::DetachAllUSBDevices except
13239 * for the aAbnormal argument. */
13240 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13241 AssertComRC(rc);
13242 NOREF(rc);
13243
13244 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13245 if (service)
13246 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
13247 }
13248#endif /* VBOX_WITH_USB */
13249
13250 // we need to lock this object in uninit() because the lock is shared
13251 // with mPeer (as well as data we modify below). mParent lock is needed
13252 // by several calls to it.
13253 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
13254
13255#ifdef VBOX_WITH_RESOURCE_USAGE_API
13256 /*
13257 * It is safe to call Machine::i_unregisterMetrics() here because
13258 * PerformanceCollector::samplerCallback no longer accesses guest methods
13259 * holding the lock.
13260 */
13261 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
13262 /* The guest must be unregistered after its metrics (@bugref{5949}). */
13263 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
13264 if (mCollectorGuest)
13265 {
13266 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
13267 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
13268 mCollectorGuest = NULL;
13269 }
13270#endif
13271
13272 if (aReason == Uninit::Abnormal)
13273 {
13274 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
13275
13276 /*
13277 * Move the VM to the 'Aborted' machine state unless we are restoring a
13278 * VM that was in the 'Saved' machine state. In that case, if the VM
13279 * fails before reaching either the 'Restoring' machine state or the
13280 * 'Running' machine state then we set the machine state to
13281 * 'AbortedSaved' in order to preserve the saved state file so that the
13282 * VM can be restored in the future.
13283 */
13284 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
13285 i_setMachineState(MachineState_AbortedSaved);
13286 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
13287 i_setMachineState(MachineState_Aborted);
13288 }
13289
13290 // any machine settings modified?
13291 if (mData->flModifications)
13292 {
13293 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
13294 i_rollback(false /* aNotify */);
13295 }
13296
13297 mData->mSession.mPID = NIL_RTPROCESS;
13298
13299 if (aReason == Uninit::Unexpected)
13300 {
13301 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
13302 * client watcher thread to update the set of machines that have open
13303 * sessions. */
13304 mParent->i_updateClientWatcher();
13305 }
13306
13307 /* uninitialize all remote controls */
13308 if (mData->mSession.mRemoteControls.size())
13309 {
13310 LogFlowThisFunc(("Closing remote sessions (%d):\n",
13311 mData->mSession.mRemoteControls.size()));
13312
13313 /* Always restart a the beginning, since the iterator is invalidated
13314 * by using erase(). */
13315 for (Data::Session::RemoteControlList::iterator
13316 it = mData->mSession.mRemoteControls.begin();
13317 it != mData->mSession.mRemoteControls.end();
13318 it = mData->mSession.mRemoteControls.begin())
13319 {
13320 ComPtr<IInternalSessionControl> pControl = *it;
13321 mData->mSession.mRemoteControls.erase(it);
13322 multilock.release();
13323 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
13324 HRESULT rc = pControl->Uninitialize();
13325 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
13326 if (FAILED(rc))
13327 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
13328 multilock.acquire();
13329 }
13330 mData->mSession.mRemoteControls.clear();
13331 }
13332
13333 /* Remove all references to the NAT network service. The service will stop
13334 * if all references (also from other VMs) are removed. */
13335 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
13336 {
13337 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13338 {
13339 BOOL enabled;
13340 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13341 if ( FAILED(hrc)
13342 || !enabled)
13343 continue;
13344
13345 NetworkAttachmentType_T type;
13346 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13347 if ( SUCCEEDED(hrc)
13348 && type == NetworkAttachmentType_NATNetwork)
13349 {
13350 Bstr name;
13351 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13352 if (SUCCEEDED(hrc))
13353 {
13354 multilock.release();
13355 Utf8Str strName(name);
13356 LogRel(("VM '%s' stops using NAT network '%s'\n",
13357 mUserData->s.strName.c_str(), strName.c_str()));
13358 mParent->i_natNetworkRefDec(strName);
13359 multilock.acquire();
13360 }
13361 }
13362 }
13363 }
13364
13365 /*
13366 * An expected uninitialization can come only from #i_checkForDeath().
13367 * Otherwise it means that something's gone really wrong (for example,
13368 * the Session implementation has released the VirtualBox reference
13369 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
13370 * etc). However, it's also possible, that the client releases the IPC
13371 * semaphore correctly (i.e. before it releases the VirtualBox reference),
13372 * but the VirtualBox release event comes first to the server process.
13373 * This case is practically possible, so we should not assert on an
13374 * unexpected uninit, just log a warning.
13375 */
13376
13377 if (aReason == Uninit::Unexpected)
13378 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
13379
13380 if (aReason != Uninit::Normal)
13381 {
13382 mData->mSession.mDirectControl.setNull();
13383 }
13384 else
13385 {
13386 /* this must be null here (see #OnSessionEnd()) */
13387 Assert(mData->mSession.mDirectControl.isNull());
13388 Assert(mData->mSession.mState == SessionState_Unlocking);
13389 Assert(!mData->mSession.mProgress.isNull());
13390 }
13391 if (mData->mSession.mProgress)
13392 {
13393 if (aReason == Uninit::Normal)
13394 mData->mSession.mProgress->i_notifyComplete(S_OK);
13395 else
13396 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
13397 COM_IIDOF(ISession),
13398 getComponentName(),
13399 tr("The VM session was aborted"));
13400 mData->mSession.mProgress.setNull();
13401 }
13402
13403 if (mConsoleTaskData.mProgress)
13404 {
13405 Assert(aReason == Uninit::Abnormal);
13406 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
13407 COM_IIDOF(ISession),
13408 getComponentName(),
13409 tr("The VM session was aborted"));
13410 mConsoleTaskData.mProgress.setNull();
13411 }
13412
13413 /* remove the association between the peer machine and this session machine */
13414 Assert( (SessionMachine*)mData->mSession.mMachine == this
13415 || aReason == Uninit::Unexpected);
13416
13417 /* reset the rest of session data */
13418 mData->mSession.mLockType = LockType_Null;
13419 mData->mSession.mMachine.setNull();
13420 mData->mSession.mState = SessionState_Unlocked;
13421 mData->mSession.mName.setNull();
13422
13423 /* destroy the machine client token before leaving the exclusive lock */
13424 if (mClientToken)
13425 {
13426 delete mClientToken;
13427 mClientToken = NULL;
13428 }
13429
13430 /* fire an event */
13431 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
13432
13433 uninitDataAndChildObjects();
13434
13435 /* free the essential data structure last */
13436 mData.free();
13437
13438 /* release the exclusive lock before setting the below two to NULL */
13439 multilock.release();
13440
13441 unconst(mParent) = NULL;
13442 unconst(mPeer) = NULL;
13443
13444 AuthLibUnload(&mAuthLibCtx);
13445
13446 LogFlowThisFuncLeave();
13447}
13448
13449// util::Lockable interface
13450////////////////////////////////////////////////////////////////////////////////
13451
13452/**
13453 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13454 * with the primary Machine instance (mPeer).
13455 */
13456RWLockHandle *SessionMachine::lockHandle() const
13457{
13458 AssertReturn(mPeer != NULL, NULL);
13459 return mPeer->lockHandle();
13460}
13461
13462// IInternalMachineControl methods
13463////////////////////////////////////////////////////////////////////////////////
13464
13465/**
13466 * Passes collected guest statistics to performance collector object
13467 */
13468HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13469 ULONG aCpuKernel, ULONG aCpuIdle,
13470 ULONG aMemTotal, ULONG aMemFree,
13471 ULONG aMemBalloon, ULONG aMemShared,
13472 ULONG aMemCache, ULONG aPageTotal,
13473 ULONG aAllocVMM, ULONG aFreeVMM,
13474 ULONG aBalloonedVMM, ULONG aSharedVMM,
13475 ULONG aVmNetRx, ULONG aVmNetTx)
13476{
13477#ifdef VBOX_WITH_RESOURCE_USAGE_API
13478 if (mCollectorGuest)
13479 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13480 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13481 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13482 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13483
13484 return S_OK;
13485#else
13486 NOREF(aValidStats);
13487 NOREF(aCpuUser);
13488 NOREF(aCpuKernel);
13489 NOREF(aCpuIdle);
13490 NOREF(aMemTotal);
13491 NOREF(aMemFree);
13492 NOREF(aMemBalloon);
13493 NOREF(aMemShared);
13494 NOREF(aMemCache);
13495 NOREF(aPageTotal);
13496 NOREF(aAllocVMM);
13497 NOREF(aFreeVMM);
13498 NOREF(aBalloonedVMM);
13499 NOREF(aSharedVMM);
13500 NOREF(aVmNetRx);
13501 NOREF(aVmNetTx);
13502 return E_NOTIMPL;
13503#endif
13504}
13505
13506////////////////////////////////////////////////////////////////////////////////
13507//
13508// SessionMachine task records
13509//
13510////////////////////////////////////////////////////////////////////////////////
13511
13512/**
13513 * Task record for saving the machine state.
13514 */
13515class SessionMachine::SaveStateTask
13516 : public Machine::Task
13517{
13518public:
13519 SaveStateTask(SessionMachine *m,
13520 Progress *p,
13521 const Utf8Str &t,
13522 Reason_T enmReason,
13523 const Utf8Str &strStateFilePath)
13524 : Task(m, p, t),
13525 m_enmReason(enmReason),
13526 m_strStateFilePath(strStateFilePath)
13527 {}
13528
13529private:
13530 void handler()
13531 {
13532 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13533 }
13534
13535 Reason_T m_enmReason;
13536 Utf8Str m_strStateFilePath;
13537
13538 friend class SessionMachine;
13539};
13540
13541/**
13542 * Task thread implementation for SessionMachine::SaveState(), called from
13543 * SessionMachine::taskHandler().
13544 *
13545 * @note Locks this object for writing.
13546 *
13547 * @param task
13548 * @return
13549 */
13550void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13551{
13552 LogFlowThisFuncEnter();
13553
13554 AutoCaller autoCaller(this);
13555 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13556 if (FAILED(autoCaller.rc()))
13557 {
13558 /* we might have been uninitialized because the session was accidentally
13559 * closed by the client, so don't assert */
13560 HRESULT rc = setError(E_FAIL,
13561 tr("The session has been accidentally closed"));
13562 task.m_pProgress->i_notifyComplete(rc);
13563 LogFlowThisFuncLeave();
13564 return;
13565 }
13566
13567 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13568
13569 HRESULT rc = S_OK;
13570
13571 try
13572 {
13573 ComPtr<IInternalSessionControl> directControl;
13574 if (mData->mSession.mLockType == LockType_VM)
13575 directControl = mData->mSession.mDirectControl;
13576 if (directControl.isNull())
13577 throw setError(VBOX_E_INVALID_VM_STATE,
13578 tr("Trying to save state without a running VM"));
13579 alock.release();
13580 BOOL fSuspendedBySave;
13581 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13582 Assert(!fSuspendedBySave);
13583 alock.acquire();
13584
13585 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13586 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13587 throw E_FAIL);
13588
13589 if (SUCCEEDED(rc))
13590 {
13591 mSSData->strStateFilePath = task.m_strStateFilePath;
13592
13593 /* save all VM settings */
13594 rc = i_saveSettings(NULL, alock);
13595 // no need to check whether VirtualBox.xml needs saving also since
13596 // we can't have a name change pending at this point
13597 }
13598 else
13599 {
13600 // On failure, set the state to the state we had at the beginning.
13601 i_setMachineState(task.m_machineStateBackup);
13602 i_updateMachineStateOnClient();
13603
13604 // Delete the saved state file (might have been already created).
13605 // No need to check whether this is shared with a snapshot here
13606 // because we certainly created a fresh saved state file here.
13607 i_deleteFile(task.m_strStateFilePath, true /* fIgnoreFailures */);
13608 }
13609 }
13610 catch (HRESULT aRC) { rc = aRC; }
13611
13612 task.m_pProgress->i_notifyComplete(rc);
13613
13614 LogFlowThisFuncLeave();
13615}
13616
13617/**
13618 * @note Locks this object for writing.
13619 */
13620HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13621{
13622 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13623}
13624
13625HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13626{
13627 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13628
13629 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13630 if (FAILED(rc)) return rc;
13631
13632 if ( mData->mMachineState != MachineState_Running
13633 && mData->mMachineState != MachineState_Paused
13634 )
13635 return setError(VBOX_E_INVALID_VM_STATE,
13636 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13637 Global::stringifyMachineState(mData->mMachineState));
13638
13639 ComObjPtr<Progress> pProgress;
13640 pProgress.createObject();
13641 rc = pProgress->init(i_getVirtualBox(),
13642 static_cast<IMachine *>(this) /* aInitiator */,
13643 tr("Saving the execution state of the virtual machine"),
13644 FALSE /* aCancelable */);
13645 if (FAILED(rc))
13646 return rc;
13647
13648 Utf8Str strStateFilePath;
13649 i_composeSavedStateFilename(strStateFilePath);
13650
13651 /* create and start the task on a separate thread (note that it will not
13652 * start working until we release alock) */
13653 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13654 rc = pTask->createThread();
13655 if (FAILED(rc))
13656 return rc;
13657
13658 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13659 i_setMachineState(MachineState_Saving);
13660 i_updateMachineStateOnClient();
13661
13662 pProgress.queryInterfaceTo(aProgress.asOutParam());
13663
13664 return S_OK;
13665}
13666
13667/**
13668 * @note Locks this object for writing.
13669 */
13670HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13671{
13672 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13673
13674 HRESULT rc = i_checkStateDependency(MutableStateDep);
13675 if (FAILED(rc)) return rc;
13676
13677 if ( mData->mMachineState != MachineState_PoweredOff
13678 && mData->mMachineState != MachineState_Teleported
13679 && mData->mMachineState != MachineState_Aborted
13680 )
13681 return setError(VBOX_E_INVALID_VM_STATE,
13682 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13683 Global::stringifyMachineState(mData->mMachineState));
13684
13685 com::Utf8Str stateFilePathFull;
13686 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13687 if (RT_FAILURE(vrc))
13688 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13689 tr("Invalid saved state file path '%s' (%Rrc)"),
13690 aSavedStateFile.c_str(),
13691 vrc);
13692
13693 mSSData->strStateFilePath = stateFilePathFull;
13694
13695 /* The below i_setMachineState() will detect the state transition and will
13696 * update the settings file */
13697
13698 return i_setMachineState(MachineState_Saved);
13699}
13700
13701/**
13702 * @note Locks this object for writing.
13703 */
13704HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13705{
13706 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13707
13708 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13709 if (FAILED(rc)) return rc;
13710
13711 if ( mData->mMachineState != MachineState_Saved
13712 && mData->mMachineState != MachineState_AbortedSaved)
13713 return setError(VBOX_E_INVALID_VM_STATE,
13714 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13715 Global::stringifyMachineState(mData->mMachineState));
13716
13717 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13718
13719 /*
13720 * Saved -> PoweredOff transition will be detected in the SessionMachine
13721 * and properly handled.
13722 */
13723 rc = i_setMachineState(MachineState_PoweredOff);
13724 return rc;
13725}
13726
13727
13728/**
13729 * @note Locks the same as #i_setMachineState() does.
13730 */
13731HRESULT SessionMachine::updateState(MachineState_T aState)
13732{
13733 return i_setMachineState(aState);
13734}
13735
13736/**
13737 * @note Locks this object for writing.
13738 */
13739HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13740{
13741 IProgress *pProgress(aProgress);
13742
13743 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13744
13745 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13746
13747 if (mData->mSession.mState != SessionState_Locked)
13748 return VBOX_E_INVALID_OBJECT_STATE;
13749
13750 if (!mData->mSession.mProgress.isNull())
13751 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13752
13753 /* If we didn't reference the NAT network service yet, add a reference to
13754 * force a start */
13755 if (miNATNetworksStarted < 1)
13756 {
13757 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13758 {
13759 BOOL enabled;
13760 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13761 if ( FAILED(hrc)
13762 || !enabled)
13763 continue;
13764
13765 NetworkAttachmentType_T type;
13766 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13767 if ( SUCCEEDED(hrc)
13768 && type == NetworkAttachmentType_NATNetwork)
13769 {
13770 Bstr name;
13771 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13772 if (SUCCEEDED(hrc))
13773 {
13774 Utf8Str strName(name);
13775 LogRel(("VM '%s' starts using NAT network '%s'\n",
13776 mUserData->s.strName.c_str(), strName.c_str()));
13777 mPeer->lockHandle()->unlockWrite();
13778 mParent->i_natNetworkRefInc(strName);
13779#ifdef RT_LOCK_STRICT
13780 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13781#else
13782 mPeer->lockHandle()->lockWrite();
13783#endif
13784 }
13785 }
13786 }
13787 miNATNetworksStarted++;
13788 }
13789
13790 LogFlowThisFunc(("returns S_OK.\n"));
13791 return S_OK;
13792}
13793
13794/**
13795 * @note Locks this object for writing.
13796 */
13797HRESULT SessionMachine::endPowerUp(LONG aResult)
13798{
13799 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13800
13801 if (mData->mSession.mState != SessionState_Locked)
13802 return VBOX_E_INVALID_OBJECT_STATE;
13803
13804 /* Finalize the LaunchVMProcess progress object. */
13805 if (mData->mSession.mProgress)
13806 {
13807 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13808 mData->mSession.mProgress.setNull();
13809 }
13810
13811 if (SUCCEEDED((HRESULT)aResult))
13812 {
13813#ifdef VBOX_WITH_RESOURCE_USAGE_API
13814 /* The VM has been powered up successfully, so it makes sense
13815 * now to offer the performance metrics for a running machine
13816 * object. Doing it earlier wouldn't be safe. */
13817 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13818 mData->mSession.mPID);
13819#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13820 }
13821
13822 return S_OK;
13823}
13824
13825/**
13826 * @note Locks this object for writing.
13827 */
13828HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13829{
13830 LogFlowThisFuncEnter();
13831
13832 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13833
13834 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13835 E_FAIL);
13836
13837 /* create a progress object to track operation completion */
13838 ComObjPtr<Progress> pProgress;
13839 pProgress.createObject();
13840 pProgress->init(i_getVirtualBox(),
13841 static_cast<IMachine *>(this) /* aInitiator */,
13842 tr("Stopping the virtual machine"),
13843 FALSE /* aCancelable */);
13844
13845 /* fill in the console task data */
13846 mConsoleTaskData.mLastState = mData->mMachineState;
13847 mConsoleTaskData.mProgress = pProgress;
13848
13849 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13850 i_setMachineState(MachineState_Stopping);
13851
13852 pProgress.queryInterfaceTo(aProgress.asOutParam());
13853
13854 return S_OK;
13855}
13856
13857/**
13858 * @note Locks this object for writing.
13859 */
13860HRESULT SessionMachine::endPoweringDown(LONG aResult,
13861 const com::Utf8Str &aErrMsg)
13862{
13863 HRESULT const hrcResult = (HRESULT)aResult;
13864 LogFlowThisFuncEnter();
13865
13866 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13867
13868 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13869 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13870 && mConsoleTaskData.mLastState != MachineState_Null,
13871 E_FAIL);
13872
13873 /*
13874 * On failure, set the state to the state we had when BeginPoweringDown()
13875 * was called (this is expected by Console::PowerDown() and the associated
13876 * task). On success the VM process already changed the state to
13877 * MachineState_PoweredOff, so no need to do anything.
13878 */
13879 if (FAILED(hrcResult))
13880 i_setMachineState(mConsoleTaskData.mLastState);
13881
13882 /* notify the progress object about operation completion */
13883 Assert(mConsoleTaskData.mProgress);
13884 if (SUCCEEDED(hrcResult))
13885 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13886 else
13887 {
13888 if (aErrMsg.length())
13889 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13890 COM_IIDOF(ISession),
13891 getComponentName(),
13892 aErrMsg.c_str());
13893 else
13894 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13895 }
13896
13897 /* clear out the temporary saved state data */
13898 mConsoleTaskData.mLastState = MachineState_Null;
13899 mConsoleTaskData.mProgress.setNull();
13900
13901 LogFlowThisFuncLeave();
13902 return S_OK;
13903}
13904
13905
13906/**
13907 * Goes through the USB filters of the given machine to see if the given
13908 * device matches any filter or not.
13909 *
13910 * @note Locks the same as USBController::hasMatchingFilter() does.
13911 */
13912HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13913 BOOL *aMatched,
13914 ULONG *aMaskedInterfaces)
13915{
13916 LogFlowThisFunc(("\n"));
13917
13918#ifdef VBOX_WITH_USB
13919 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13920#else
13921 NOREF(aDevice);
13922 NOREF(aMaskedInterfaces);
13923 *aMatched = FALSE;
13924#endif
13925
13926 return S_OK;
13927}
13928
13929/**
13930 * @note Locks the same as Host::captureUSBDevice() does.
13931 */
13932HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13933{
13934 LogFlowThisFunc(("\n"));
13935
13936#ifdef VBOX_WITH_USB
13937 /* if captureDeviceForVM() fails, it must have set extended error info */
13938 clearError();
13939 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13940 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13941 return rc;
13942
13943 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13944 AssertReturn(service, E_FAIL);
13945 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13946#else
13947 RT_NOREF(aId, aCaptureFilename);
13948 return E_NOTIMPL;
13949#endif
13950}
13951
13952/**
13953 * @note Locks the same as Host::detachUSBDevice() does.
13954 */
13955HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13956 BOOL aDone)
13957{
13958 LogFlowThisFunc(("\n"));
13959
13960#ifdef VBOX_WITH_USB
13961 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13962 AssertReturn(service, E_FAIL);
13963 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13964#else
13965 NOREF(aId);
13966 NOREF(aDone);
13967 return E_NOTIMPL;
13968#endif
13969}
13970
13971/**
13972 * Inserts all machine filters to the USB proxy service and then calls
13973 * Host::autoCaptureUSBDevices().
13974 *
13975 * Called by Console from the VM process upon VM startup.
13976 *
13977 * @note Locks what called methods lock.
13978 */
13979HRESULT SessionMachine::autoCaptureUSBDevices()
13980{
13981 LogFlowThisFunc(("\n"));
13982
13983#ifdef VBOX_WITH_USB
13984 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13985 AssertComRC(rc);
13986 NOREF(rc);
13987
13988 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13989 AssertReturn(service, E_FAIL);
13990 return service->autoCaptureDevicesForVM(this);
13991#else
13992 return S_OK;
13993#endif
13994}
13995
13996/**
13997 * Removes all machine filters from the USB proxy service and then calls
13998 * Host::detachAllUSBDevices().
13999 *
14000 * Called by Console from the VM process upon normal VM termination or by
14001 * SessionMachine::uninit() upon abnormal VM termination (from under the
14002 * Machine/SessionMachine lock).
14003 *
14004 * @note Locks what called methods lock.
14005 */
14006HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
14007{
14008 LogFlowThisFunc(("\n"));
14009
14010#ifdef VBOX_WITH_USB
14011 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
14012 AssertComRC(rc);
14013 NOREF(rc);
14014
14015 USBProxyService *service = mParent->i_host()->i_usbProxyService();
14016 AssertReturn(service, E_FAIL);
14017 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
14018#else
14019 NOREF(aDone);
14020 return S_OK;
14021#endif
14022}
14023
14024/**
14025 * @note Locks this object for writing.
14026 */
14027HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
14028 ComPtr<IProgress> &aProgress)
14029{
14030 LogFlowThisFuncEnter();
14031
14032 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
14033 /*
14034 * We don't assert below because it might happen that a non-direct session
14035 * informs us it is closed right after we've been uninitialized -- it's ok.
14036 */
14037
14038 /* get IInternalSessionControl interface */
14039 ComPtr<IInternalSessionControl> control(aSession);
14040
14041 ComAssertRet(!control.isNull(), E_INVALIDARG);
14042
14043 /* Creating a Progress object requires the VirtualBox lock, and
14044 * thus locking it here is required by the lock order rules. */
14045 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
14046
14047 if (control == mData->mSession.mDirectControl)
14048 {
14049 /* The direct session is being normally closed by the client process
14050 * ----------------------------------------------------------------- */
14051
14052 /* go to the closing state (essential for all open*Session() calls and
14053 * for #i_checkForDeath()) */
14054 Assert(mData->mSession.mState == SessionState_Locked);
14055 mData->mSession.mState = SessionState_Unlocking;
14056
14057 /* set direct control to NULL to release the remote instance */
14058 mData->mSession.mDirectControl.setNull();
14059 LogFlowThisFunc(("Direct control is set to NULL\n"));
14060
14061 if (mData->mSession.mProgress)
14062 {
14063 /* finalize the progress, someone might wait if a frontend
14064 * closes the session before powering on the VM. */
14065 mData->mSession.mProgress->notifyComplete(E_FAIL,
14066 COM_IIDOF(ISession),
14067 getComponentName(),
14068 tr("The VM session was closed before any attempt to power it on"));
14069 mData->mSession.mProgress.setNull();
14070 }
14071
14072 /* Create the progress object the client will use to wait until
14073 * #i_checkForDeath() is called to uninitialize this session object after
14074 * it releases the IPC semaphore.
14075 * Note! Because we're "reusing" mProgress here, this must be a proxy
14076 * object just like for LaunchVMProcess. */
14077 Assert(mData->mSession.mProgress.isNull());
14078 ComObjPtr<ProgressProxy> progress;
14079 progress.createObject();
14080 ComPtr<IUnknown> pPeer(mPeer);
14081 progress->init(mParent, pPeer,
14082 Bstr(tr("Closing session")).raw(),
14083 FALSE /* aCancelable */);
14084 progress.queryInterfaceTo(aProgress.asOutParam());
14085 mData->mSession.mProgress = progress;
14086 }
14087 else
14088 {
14089 /* the remote session is being normally closed */
14090 bool found = false;
14091 for (Data::Session::RemoteControlList::iterator
14092 it = mData->mSession.mRemoteControls.begin();
14093 it != mData->mSession.mRemoteControls.end();
14094 ++it)
14095 {
14096 if (control == *it)
14097 {
14098 found = true;
14099 // This MUST be erase(it), not remove(*it) as the latter
14100 // triggers a very nasty use after free due to the place where
14101 // the value "lives".
14102 mData->mSession.mRemoteControls.erase(it);
14103 break;
14104 }
14105 }
14106 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
14107 E_INVALIDARG);
14108 }
14109
14110 /* signal the client watcher thread, because the client is going away */
14111 mParent->i_updateClientWatcher();
14112
14113 LogFlowThisFuncLeave();
14114 return S_OK;
14115}
14116
14117HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14118 std::vector<com::Utf8Str> &aValues,
14119 std::vector<LONG64> &aTimestamps,
14120 std::vector<com::Utf8Str> &aFlags)
14121{
14122 LogFlowThisFunc(("\n"));
14123
14124#ifdef VBOX_WITH_GUEST_PROPS
14125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14126
14127 size_t cEntries = mHWData->mGuestProperties.size();
14128 aNames.resize(cEntries);
14129 aValues.resize(cEntries);
14130 aTimestamps.resize(cEntries);
14131 aFlags.resize(cEntries);
14132
14133 size_t i = 0;
14134 for (HWData::GuestPropertyMap::const_iterator
14135 it = mHWData->mGuestProperties.begin();
14136 it != mHWData->mGuestProperties.end();
14137 ++it, ++i)
14138 {
14139 aNames[i] = it->first;
14140 int vrc = GuestPropValidateName(aNames[i].c_str(), aNames[i].length() + 1 /* '\0' */);
14141 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
14142
14143 aValues[i] = it->second.strValue;
14144 vrc = GuestPropValidateValue(aValues[i].c_str(), aValues[i].length() + 1 /* '\0' */);
14145 AssertRCReturn(vrc, setErrorBoth(E_INVALIDARG /* bad choice */, vrc));
14146
14147 aTimestamps[i] = it->second.mTimestamp;
14148
14149 /* If it is NULL, keep it NULL. */
14150 if (it->second.mFlags)
14151 {
14152 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
14153 GuestPropWriteFlags(it->second.mFlags, szFlags);
14154 aFlags[i] = szFlags;
14155 }
14156 else
14157 aFlags[i] = "";
14158 }
14159 return S_OK;
14160#else
14161 ReturnComNotImplemented();
14162#endif
14163}
14164
14165HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
14166 const com::Utf8Str &aValue,
14167 LONG64 aTimestamp,
14168 const com::Utf8Str &aFlags,
14169 BOOL fWasDeleted)
14170{
14171 LogFlowThisFunc(("\n"));
14172
14173#ifdef VBOX_WITH_GUEST_PROPS
14174 try
14175 {
14176 /*
14177 * Convert input up front.
14178 */
14179 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
14180 if (aFlags.length())
14181 {
14182 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
14183 AssertRCReturn(vrc, E_INVALIDARG);
14184 }
14185
14186 /*
14187 * Now grab the object lock, validate the state and do the update.
14188 */
14189
14190 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14191
14192 if (!Global::IsOnline(mData->mMachineState))
14193 AssertMsgFailedReturn(("%s\n", ::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE);
14194
14195 i_setModified(IsModified_MachineData);
14196 mHWData.backup();
14197
14198 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
14199 if (it != mHWData->mGuestProperties.end())
14200 {
14201 if (!fWasDeleted)
14202 {
14203 it->second.strValue = aValue;
14204 it->second.mTimestamp = aTimestamp;
14205 it->second.mFlags = fFlags;
14206 }
14207 else
14208 mHWData->mGuestProperties.erase(it);
14209
14210 mData->mGuestPropertiesModified = TRUE;
14211 }
14212 else if (!fWasDeleted)
14213 {
14214 HWData::GuestProperty prop;
14215 prop.strValue = aValue;
14216 prop.mTimestamp = aTimestamp;
14217 prop.mFlags = fFlags;
14218
14219 mHWData->mGuestProperties[aName] = prop;
14220 mData->mGuestPropertiesModified = TRUE;
14221 }
14222
14223 alock.release();
14224
14225 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags, fWasDeleted);
14226 }
14227 catch (...)
14228 {
14229 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
14230 }
14231 return S_OK;
14232#else
14233 ReturnComNotImplemented();
14234#endif
14235}
14236
14237
14238HRESULT SessionMachine::lockMedia()
14239{
14240 AutoMultiWriteLock2 alock(this->lockHandle(),
14241 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14242
14243 AssertReturn( mData->mMachineState == MachineState_Starting
14244 || mData->mMachineState == MachineState_Restoring
14245 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
14246
14247 clearError();
14248 alock.release();
14249 return i_lockMedia();
14250}
14251
14252HRESULT SessionMachine::unlockMedia()
14253{
14254 HRESULT hrc = i_unlockMedia();
14255 return hrc;
14256}
14257
14258HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14259 ComPtr<IMediumAttachment> &aNewAttachment)
14260{
14261 // request the host lock first, since might be calling Host methods for getting host drives;
14262 // next, protect the media tree all the while we're in here, as well as our member variables
14263 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
14264 this->lockHandle(),
14265 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14266
14267 IMediumAttachment *iAttach = aAttachment;
14268 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
14269
14270 Utf8Str ctrlName;
14271 LONG lPort;
14272 LONG lDevice;
14273 bool fTempEject;
14274 {
14275 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14276
14277 /* Need to query the details first, as the IMediumAttachment reference
14278 * might be to the original settings, which we are going to change. */
14279 ctrlName = pAttach->i_getControllerName();
14280 lPort = pAttach->i_getPort();
14281 lDevice = pAttach->i_getDevice();
14282 fTempEject = pAttach->i_getTempEject();
14283 }
14284
14285 if (!fTempEject)
14286 {
14287 /* Remember previously mounted medium. The medium before taking the
14288 * backup is not necessarily the same thing. */
14289 ComObjPtr<Medium> oldmedium;
14290 oldmedium = pAttach->i_getMedium();
14291
14292 i_setModified(IsModified_Storage);
14293 mMediumAttachments.backup();
14294
14295 // The backup operation makes the pAttach reference point to the
14296 // old settings. Re-get the correct reference.
14297 pAttach = i_findAttachment(*mMediumAttachments.data(),
14298 ctrlName,
14299 lPort,
14300 lDevice);
14301
14302 {
14303 AutoCaller autoAttachCaller(this);
14304 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
14305
14306 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14307 if (!oldmedium.isNull())
14308 oldmedium->i_removeBackReference(mData->mUuid);
14309
14310 pAttach->i_updateMedium(NULL);
14311 pAttach->i_updateEjected();
14312 }
14313
14314 i_setModified(IsModified_Storage);
14315 }
14316 else
14317 {
14318 {
14319 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14320 pAttach->i_updateEjected();
14321 }
14322 }
14323
14324 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
14325
14326 return S_OK;
14327}
14328
14329HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
14330 com::Utf8Str &aResult)
14331{
14332 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14333
14334 HRESULT hr = S_OK;
14335
14336 if (!mAuthLibCtx.hAuthLibrary)
14337 {
14338 /* Load the external authentication library. */
14339 Bstr authLibrary;
14340 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
14341
14342 Utf8Str filename = authLibrary;
14343
14344 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
14345 if (RT_FAILURE(vrc))
14346 hr = setErrorBoth(E_FAIL, vrc,
14347 tr("Could not load the external authentication library '%s' (%Rrc)"),
14348 filename.c_str(), vrc);
14349 }
14350
14351 /* The auth library might need the machine lock. */
14352 alock.release();
14353
14354 if (FAILED(hr))
14355 return hr;
14356
14357 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
14358 {
14359 enum VRDEAuthParams
14360 {
14361 parmUuid = 1,
14362 parmGuestJudgement,
14363 parmUser,
14364 parmPassword,
14365 parmDomain,
14366 parmClientId
14367 };
14368
14369 AuthResult result = AuthResultAccessDenied;
14370
14371 Guid uuid(aAuthParams[parmUuid]);
14372 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
14373 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
14374
14375 result = AuthLibAuthenticate(&mAuthLibCtx,
14376 uuid.raw(), guestJudgement,
14377 aAuthParams[parmUser].c_str(),
14378 aAuthParams[parmPassword].c_str(),
14379 aAuthParams[parmDomain].c_str(),
14380 u32ClientId);
14381
14382 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
14383 size_t cbPassword = aAuthParams[parmPassword].length();
14384 if (cbPassword)
14385 {
14386 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
14387 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
14388 }
14389
14390 if (result == AuthResultAccessGranted)
14391 aResult = "granted";
14392 else
14393 aResult = "denied";
14394
14395 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
14396 aAuthParams[parmUser].c_str(), aResult.c_str()));
14397 }
14398 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
14399 {
14400 enum VRDEAuthDisconnectParams
14401 {
14402 parmUuid = 1,
14403 parmClientId
14404 };
14405
14406 Guid uuid(aAuthParams[parmUuid]);
14407 uint32_t u32ClientId = 0;
14408 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
14409 }
14410 else
14411 {
14412 hr = E_INVALIDARG;
14413 }
14414
14415 return hr;
14416}
14417
14418// public methods only for internal purposes
14419/////////////////////////////////////////////////////////////////////////////
14420
14421#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14422/**
14423 * Called from the client watcher thread to check for expected or unexpected
14424 * death of the client process that has a direct session to this machine.
14425 *
14426 * On Win32 and on OS/2, this method is called only when we've got the
14427 * mutex (i.e. the client has either died or terminated normally) so it always
14428 * returns @c true (the client is terminated, the session machine is
14429 * uninitialized).
14430 *
14431 * On other platforms, the method returns @c true if the client process has
14432 * terminated normally or abnormally and the session machine was uninitialized,
14433 * and @c false if the client process is still alive.
14434 *
14435 * @note Locks this object for writing.
14436 */
14437bool SessionMachine::i_checkForDeath()
14438{
14439 Uninit::Reason reason;
14440 bool terminated = false;
14441
14442 /* Enclose autoCaller with a block because calling uninit() from under it
14443 * will deadlock. */
14444 {
14445 AutoCaller autoCaller(this);
14446 if (!autoCaller.isOk())
14447 {
14448 /* return true if not ready, to cause the client watcher to exclude
14449 * the corresponding session from watching */
14450 LogFlowThisFunc(("Already uninitialized!\n"));
14451 return true;
14452 }
14453
14454 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14455
14456 /* Determine the reason of death: if the session state is Closing here,
14457 * everything is fine. Otherwise it means that the client did not call
14458 * OnSessionEnd() before it released the IPC semaphore. This may happen
14459 * either because the client process has abnormally terminated, or
14460 * because it simply forgot to call ISession::Close() before exiting. We
14461 * threat the latter also as an abnormal termination (see
14462 * Session::uninit() for details). */
14463 reason = mData->mSession.mState == SessionState_Unlocking ?
14464 Uninit::Normal :
14465 Uninit::Abnormal;
14466
14467 if (mClientToken)
14468 terminated = mClientToken->release();
14469 } /* AutoCaller block */
14470
14471 if (terminated)
14472 uninit(reason);
14473
14474 return terminated;
14475}
14476
14477void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14478{
14479 LogFlowThisFunc(("\n"));
14480
14481 strTokenId.setNull();
14482
14483 AutoCaller autoCaller(this);
14484 AssertComRCReturnVoid(autoCaller.rc());
14485
14486 Assert(mClientToken);
14487 if (mClientToken)
14488 mClientToken->getId(strTokenId);
14489}
14490#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14491IToken *SessionMachine::i_getToken()
14492{
14493 LogFlowThisFunc(("\n"));
14494
14495 AutoCaller autoCaller(this);
14496 AssertComRCReturn(autoCaller.rc(), NULL);
14497
14498 Assert(mClientToken);
14499 if (mClientToken)
14500 return mClientToken->getToken();
14501 else
14502 return NULL;
14503}
14504#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14505
14506Machine::ClientToken *SessionMachine::i_getClientToken()
14507{
14508 LogFlowThisFunc(("\n"));
14509
14510 AutoCaller autoCaller(this);
14511 AssertComRCReturn(autoCaller.rc(), NULL);
14512
14513 return mClientToken;
14514}
14515
14516
14517/**
14518 * @note Locks this object for reading.
14519 */
14520HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14521{
14522 LogFlowThisFunc(("\n"));
14523
14524 AutoCaller autoCaller(this);
14525 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14526
14527 ComPtr<IInternalSessionControl> directControl;
14528 {
14529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14530 if (mData->mSession.mLockType == LockType_VM)
14531 directControl = mData->mSession.mDirectControl;
14532 }
14533
14534 /* ignore notifications sent after #OnSessionEnd() is called */
14535 if (!directControl)
14536 return S_OK;
14537
14538 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14539}
14540
14541/**
14542 * @note Locks this object for reading.
14543 */
14544HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
14545 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
14546 const Utf8Str &aGuestIp, LONG aGuestPort)
14547{
14548 LogFlowThisFunc(("\n"));
14549
14550 AutoCaller autoCaller(this);
14551 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14552
14553 ComPtr<IInternalSessionControl> directControl;
14554 {
14555 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14556 if (mData->mSession.mLockType == LockType_VM)
14557 directControl = mData->mSession.mDirectControl;
14558 }
14559
14560 /* ignore notifications sent after #OnSessionEnd() is called */
14561 if (!directControl)
14562 return S_OK;
14563 /*
14564 * instead acting like callback we ask IVirtualBox deliver corresponding event
14565 */
14566
14567 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14568 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14569 return S_OK;
14570}
14571
14572/**
14573 * @note Locks this object for reading.
14574 */
14575HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14576{
14577 LogFlowThisFunc(("\n"));
14578
14579 AutoCaller autoCaller(this);
14580 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14581
14582 ComPtr<IInternalSessionControl> directControl;
14583 {
14584 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14585 if (mData->mSession.mLockType == LockType_VM)
14586 directControl = mData->mSession.mDirectControl;
14587 }
14588
14589 /* ignore notifications sent after #OnSessionEnd() is called */
14590 if (!directControl)
14591 return S_OK;
14592
14593 return directControl->OnAudioAdapterChange(audioAdapter);
14594}
14595
14596/**
14597 * @note Locks this object for reading.
14598 */
14599HRESULT SessionMachine::i_onHostAudioDeviceChange(IHostAudioDevice *aDevice, BOOL aNew, AudioDeviceState_T aState, IVirtualBoxErrorInfo *aErrInfo)
14600{
14601 LogFlowThisFunc(("\n"));
14602
14603 AutoCaller autoCaller(this);
14604 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14605
14606 ComPtr<IInternalSessionControl> directControl;
14607 {
14608 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14609 if (mData->mSession.mLockType == LockType_VM)
14610 directControl = mData->mSession.mDirectControl;
14611 }
14612
14613 /* ignore notifications sent after #OnSessionEnd() is called */
14614 if (!directControl)
14615 return S_OK;
14616
14617 return directControl->OnHostAudioDeviceChange(aDevice, aNew, aState, aErrInfo);
14618}
14619
14620/**
14621 * @note Locks this object for reading.
14622 */
14623HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14624{
14625 LogFlowThisFunc(("\n"));
14626
14627 AutoCaller autoCaller(this);
14628 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14629
14630 ComPtr<IInternalSessionControl> directControl;
14631 {
14632 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14633 if (mData->mSession.mLockType == LockType_VM)
14634 directControl = mData->mSession.mDirectControl;
14635 }
14636
14637 /* ignore notifications sent after #OnSessionEnd() is called */
14638 if (!directControl)
14639 return S_OK;
14640
14641 return directControl->OnSerialPortChange(serialPort);
14642}
14643
14644/**
14645 * @note Locks this object for reading.
14646 */
14647HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14648{
14649 LogFlowThisFunc(("\n"));
14650
14651 AutoCaller autoCaller(this);
14652 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14653
14654 ComPtr<IInternalSessionControl> directControl;
14655 {
14656 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14657 if (mData->mSession.mLockType == LockType_VM)
14658 directControl = mData->mSession.mDirectControl;
14659 }
14660
14661 /* ignore notifications sent after #OnSessionEnd() is called */
14662 if (!directControl)
14663 return S_OK;
14664
14665 return directControl->OnParallelPortChange(parallelPort);
14666}
14667
14668/**
14669 * @note Locks this object for reading.
14670 */
14671HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14672{
14673 LogFlowThisFunc(("\n"));
14674
14675 AutoCaller autoCaller(this);
14676 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14677
14678 ComPtr<IInternalSessionControl> directControl;
14679 {
14680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14681 if (mData->mSession.mLockType == LockType_VM)
14682 directControl = mData->mSession.mDirectControl;
14683 }
14684
14685 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14686
14687 /* ignore notifications sent after #OnSessionEnd() is called */
14688 if (!directControl)
14689 return S_OK;
14690
14691 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14692}
14693
14694/**
14695 * @note Locks this object for reading.
14696 */
14697HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14698{
14699 LogFlowThisFunc(("\n"));
14700
14701 AutoCaller autoCaller(this);
14702 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14703
14704 ComPtr<IInternalSessionControl> directControl;
14705 {
14706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14707 if (mData->mSession.mLockType == LockType_VM)
14708 directControl = mData->mSession.mDirectControl;
14709 }
14710
14711 mParent->i_onMediumChanged(aAttachment);
14712
14713 /* ignore notifications sent after #OnSessionEnd() is called */
14714 if (!directControl)
14715 return S_OK;
14716
14717 return directControl->OnMediumChange(aAttachment, aForce);
14718}
14719
14720HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14721{
14722 LogFlowThisFunc(("\n"));
14723
14724 AutoCaller autoCaller(this);
14725 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14726
14727 ComPtr<IInternalSessionControl> directControl;
14728 {
14729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14730 if (mData->mSession.mLockType == LockType_VM)
14731 directControl = mData->mSession.mDirectControl;
14732 }
14733
14734 /* ignore notifications sent after #OnSessionEnd() is called */
14735 if (!directControl)
14736 return S_OK;
14737
14738 return directControl->OnVMProcessPriorityChange(aPriority);
14739}
14740
14741/**
14742 * @note Locks this object for reading.
14743 */
14744HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14745{
14746 LogFlowThisFunc(("\n"));
14747
14748 AutoCaller autoCaller(this);
14749 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14750
14751 ComPtr<IInternalSessionControl> directControl;
14752 {
14753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14754 if (mData->mSession.mLockType == LockType_VM)
14755 directControl = mData->mSession.mDirectControl;
14756 }
14757
14758 /* ignore notifications sent after #OnSessionEnd() is called */
14759 if (!directControl)
14760 return S_OK;
14761
14762 return directControl->OnCPUChange(aCPU, aRemove);
14763}
14764
14765HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14766{
14767 LogFlowThisFunc(("\n"));
14768
14769 AutoCaller autoCaller(this);
14770 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14771
14772 ComPtr<IInternalSessionControl> directControl;
14773 {
14774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14775 if (mData->mSession.mLockType == LockType_VM)
14776 directControl = mData->mSession.mDirectControl;
14777 }
14778
14779 /* ignore notifications sent after #OnSessionEnd() is called */
14780 if (!directControl)
14781 return S_OK;
14782
14783 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14784}
14785
14786/**
14787 * @note Locks this object for reading.
14788 */
14789HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14790{
14791 LogFlowThisFunc(("\n"));
14792
14793 AutoCaller autoCaller(this);
14794 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14795
14796 ComPtr<IInternalSessionControl> directControl;
14797 {
14798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14799 if (mData->mSession.mLockType == LockType_VM)
14800 directControl = mData->mSession.mDirectControl;
14801 }
14802
14803 /* ignore notifications sent after #OnSessionEnd() is called */
14804 if (!directControl)
14805 return S_OK;
14806
14807 return directControl->OnVRDEServerChange(aRestart);
14808}
14809
14810/**
14811 * @note Locks this object for reading.
14812 */
14813HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14814{
14815 LogFlowThisFunc(("\n"));
14816
14817 AutoCaller autoCaller(this);
14818 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14819
14820 ComPtr<IInternalSessionControl> directControl;
14821 {
14822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14823 if (mData->mSession.mLockType == LockType_VM)
14824 directControl = mData->mSession.mDirectControl;
14825 }
14826
14827 /* ignore notifications sent after #OnSessionEnd() is called */
14828 if (!directControl)
14829 return S_OK;
14830
14831 return directControl->OnRecordingChange(aEnable);
14832}
14833
14834/**
14835 * @note Locks this object for reading.
14836 */
14837HRESULT SessionMachine::i_onUSBControllerChange()
14838{
14839 LogFlowThisFunc(("\n"));
14840
14841 AutoCaller autoCaller(this);
14842 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14843
14844 ComPtr<IInternalSessionControl> directControl;
14845 {
14846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14847 if (mData->mSession.mLockType == LockType_VM)
14848 directControl = mData->mSession.mDirectControl;
14849 }
14850
14851 /* ignore notifications sent after #OnSessionEnd() is called */
14852 if (!directControl)
14853 return S_OK;
14854
14855 return directControl->OnUSBControllerChange();
14856}
14857
14858/**
14859 * @note Locks this object for reading.
14860 */
14861HRESULT SessionMachine::i_onSharedFolderChange()
14862{
14863 LogFlowThisFunc(("\n"));
14864
14865 AutoCaller autoCaller(this);
14866 AssertComRCReturnRC(autoCaller.rc());
14867
14868 ComPtr<IInternalSessionControl> directControl;
14869 {
14870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14871 if (mData->mSession.mLockType == LockType_VM)
14872 directControl = mData->mSession.mDirectControl;
14873 }
14874
14875 /* ignore notifications sent after #OnSessionEnd() is called */
14876 if (!directControl)
14877 return S_OK;
14878
14879 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14880}
14881
14882/**
14883 * @note Locks this object for reading.
14884 */
14885HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14886{
14887 LogFlowThisFunc(("\n"));
14888
14889 AutoCaller autoCaller(this);
14890 AssertComRCReturnRC(autoCaller.rc());
14891
14892 ComPtr<IInternalSessionControl> directControl;
14893 {
14894 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14895 if (mData->mSession.mLockType == LockType_VM)
14896 directControl = mData->mSession.mDirectControl;
14897 }
14898
14899 /* ignore notifications sent after #OnSessionEnd() is called */
14900 if (!directControl)
14901 return S_OK;
14902
14903 return directControl->OnClipboardModeChange(aClipboardMode);
14904}
14905
14906/**
14907 * @note Locks this object for reading.
14908 */
14909HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14910{
14911 LogFlowThisFunc(("\n"));
14912
14913 AutoCaller autoCaller(this);
14914 AssertComRCReturnRC(autoCaller.rc());
14915
14916 ComPtr<IInternalSessionControl> directControl;
14917 {
14918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14919 if (mData->mSession.mLockType == LockType_VM)
14920 directControl = mData->mSession.mDirectControl;
14921 }
14922
14923 /* ignore notifications sent after #OnSessionEnd() is called */
14924 if (!directControl)
14925 return S_OK;
14926
14927 return directControl->OnClipboardFileTransferModeChange(aEnable);
14928}
14929
14930/**
14931 * @note Locks this object for reading.
14932 */
14933HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14934{
14935 LogFlowThisFunc(("\n"));
14936
14937 AutoCaller autoCaller(this);
14938 AssertComRCReturnRC(autoCaller.rc());
14939
14940 ComPtr<IInternalSessionControl> directControl;
14941 {
14942 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14943 if (mData->mSession.mLockType == LockType_VM)
14944 directControl = mData->mSession.mDirectControl;
14945 }
14946
14947 /* ignore notifications sent after #OnSessionEnd() is called */
14948 if (!directControl)
14949 return S_OK;
14950
14951 return directControl->OnDnDModeChange(aDnDMode);
14952}
14953
14954/**
14955 * @note Locks this object for reading.
14956 */
14957HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14958{
14959 LogFlowThisFunc(("\n"));
14960
14961 AutoCaller autoCaller(this);
14962 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14963
14964 ComPtr<IInternalSessionControl> directControl;
14965 {
14966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14967 if (mData->mSession.mLockType == LockType_VM)
14968 directControl = mData->mSession.mDirectControl;
14969 }
14970
14971 /* ignore notifications sent after #OnSessionEnd() is called */
14972 if (!directControl)
14973 return S_OK;
14974
14975 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14976}
14977
14978/**
14979 * @note Locks this object for reading.
14980 */
14981HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14982{
14983 LogFlowThisFunc(("\n"));
14984
14985 AutoCaller autoCaller(this);
14986 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14987
14988 ComPtr<IInternalSessionControl> directControl;
14989 {
14990 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14991 if (mData->mSession.mLockType == LockType_VM)
14992 directControl = mData->mSession.mDirectControl;
14993 }
14994
14995 /* ignore notifications sent after #OnSessionEnd() is called */
14996 if (!directControl)
14997 return S_OK;
14998
14999 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
15000}
15001
15002/**
15003 * Returns @c true if this machine's USB controller reports it has a matching
15004 * filter for the given USB device and @c false otherwise.
15005 *
15006 * @note locks this object for reading.
15007 */
15008bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
15009{
15010 AutoCaller autoCaller(this);
15011 /* silently return if not ready -- this method may be called after the
15012 * direct machine session has been called */
15013 if (!autoCaller.isOk())
15014 return false;
15015
15016#ifdef VBOX_WITH_USB
15017 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15018
15019 switch (mData->mMachineState)
15020 {
15021 case MachineState_Starting:
15022 case MachineState_Restoring:
15023 case MachineState_TeleportingIn:
15024 case MachineState_Paused:
15025 case MachineState_Running:
15026 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
15027 * elsewhere... */
15028 alock.release();
15029 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
15030 default: break;
15031 }
15032#else
15033 NOREF(aDevice);
15034 NOREF(aMaskedIfs);
15035#endif
15036 return false;
15037}
15038
15039/**
15040 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
15041 */
15042HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
15043 IVirtualBoxErrorInfo *aError,
15044 ULONG aMaskedIfs,
15045 const com::Utf8Str &aCaptureFilename)
15046{
15047 LogFlowThisFunc(("\n"));
15048
15049 AutoCaller autoCaller(this);
15050
15051 /* This notification may happen after the machine object has been
15052 * uninitialized (the session was closed), so don't assert. */
15053 if (FAILED(autoCaller.rc())) return autoCaller.rc();
15054
15055 ComPtr<IInternalSessionControl> directControl;
15056 {
15057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15058 if (mData->mSession.mLockType == LockType_VM)
15059 directControl = mData->mSession.mDirectControl;
15060 }
15061
15062 /* fail on notifications sent after #OnSessionEnd() is called, it is
15063 * expected by the caller */
15064 if (!directControl)
15065 return E_FAIL;
15066
15067 /* No locks should be held at this point. */
15068 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
15069 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
15070
15071 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
15072}
15073
15074/**
15075 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
15076 */
15077HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
15078 IVirtualBoxErrorInfo *aError)
15079{
15080 LogFlowThisFunc(("\n"));
15081
15082 AutoCaller autoCaller(this);
15083
15084 /* This notification may happen after the machine object has been
15085 * uninitialized (the session was closed), so don't assert. */
15086 if (FAILED(autoCaller.rc())) return autoCaller.rc();
15087
15088 ComPtr<IInternalSessionControl> directControl;
15089 {
15090 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15091 if (mData->mSession.mLockType == LockType_VM)
15092 directControl = mData->mSession.mDirectControl;
15093 }
15094
15095 /* fail on notifications sent after #OnSessionEnd() is called, it is
15096 * expected by the caller */
15097 if (!directControl)
15098 return E_FAIL;
15099
15100 /* No locks should be held at this point. */
15101 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
15102 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
15103
15104 return directControl->OnUSBDeviceDetach(aId, aError);
15105}
15106
15107// protected methods
15108/////////////////////////////////////////////////////////////////////////////
15109
15110/**
15111 * Deletes the given file if it is no longer in use by either the current machine state
15112 * (if the machine is "saved") or any of the machine's snapshots.
15113 *
15114 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
15115 * but is different for each SnapshotMachine. When calling this, the order of calling this
15116 * function on the one hand and changing that variable OR the snapshots tree on the other hand
15117 * is therefore critical. I know, it's all rather messy.
15118 *
15119 * @param strStateFile
15120 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
15121 * the test for whether the saved state file is in use.
15122 */
15123void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
15124 Snapshot *pSnapshotToIgnore)
15125{
15126 // it is safe to delete this saved state file if it is not currently in use by the machine ...
15127 if ( (strStateFile.isNotEmpty())
15128 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
15129 )
15130 // ... and it must also not be shared with other snapshots
15131 if ( !mData->mFirstSnapshot
15132 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
15133 // this checks the SnapshotMachine's state file paths
15134 )
15135 i_deleteFile(strStateFile, true /* fIgnoreFailures */);
15136}
15137
15138/**
15139 * Locks the attached media.
15140 *
15141 * All attached hard disks are locked for writing and DVD/floppy are locked for
15142 * reading. Parents of attached hard disks (if any) are locked for reading.
15143 *
15144 * This method also performs accessibility check of all media it locks: if some
15145 * media is inaccessible, the method will return a failure and a bunch of
15146 * extended error info objects per each inaccessible medium.
15147 *
15148 * Note that this method is atomic: if it returns a success, all media are
15149 * locked as described above; on failure no media is locked at all (all
15150 * succeeded individual locks will be undone).
15151 *
15152 * The caller is responsible for doing the necessary state sanity checks.
15153 *
15154 * The locks made by this method must be undone by calling #unlockMedia() when
15155 * no more needed.
15156 */
15157HRESULT SessionMachine::i_lockMedia()
15158{
15159 AutoCaller autoCaller(this);
15160 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15161
15162 AutoMultiWriteLock2 alock(this->lockHandle(),
15163 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
15164
15165 /* bail out if trying to lock things with already set up locking */
15166 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
15167
15168 MultiResult mrc(S_OK);
15169
15170 /* Collect locking information for all medium objects attached to the VM. */
15171 for (MediumAttachmentList::const_iterator
15172 it = mMediumAttachments->begin();
15173 it != mMediumAttachments->end();
15174 ++it)
15175 {
15176 MediumAttachment *pAtt = *it;
15177 DeviceType_T devType = pAtt->i_getType();
15178 Medium *pMedium = pAtt->i_getMedium();
15179
15180 MediumLockList *pMediumLockList(new MediumLockList());
15181 // There can be attachments without a medium (floppy/dvd), and thus
15182 // it's impossible to create a medium lock list. It still makes sense
15183 // to have the empty medium lock list in the map in case a medium is
15184 // attached later.
15185 if (pMedium != NULL)
15186 {
15187 MediumType_T mediumType = pMedium->i_getType();
15188 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
15189 || mediumType == MediumType_Shareable;
15190 bool fIsVitalImage = (devType == DeviceType_HardDisk);
15191
15192 alock.release();
15193 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
15194 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
15195 false /* fMediumLockWriteAll */,
15196 NULL,
15197 *pMediumLockList);
15198 alock.acquire();
15199 if (FAILED(mrc))
15200 {
15201 delete pMediumLockList;
15202 mData->mSession.mLockedMedia.Clear();
15203 break;
15204 }
15205 }
15206
15207 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
15208 if (FAILED(rc))
15209 {
15210 mData->mSession.mLockedMedia.Clear();
15211 mrc = setError(rc,
15212 tr("Collecting locking information for all attached media failed"));
15213 break;
15214 }
15215 }
15216
15217 if (SUCCEEDED(mrc))
15218 {
15219 /* Now lock all media. If this fails, nothing is locked. */
15220 alock.release();
15221 HRESULT rc = mData->mSession.mLockedMedia.Lock();
15222 alock.acquire();
15223 if (FAILED(rc))
15224 {
15225 mrc = setError(rc,
15226 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
15227 }
15228 }
15229
15230 return mrc;
15231}
15232
15233/**
15234 * Undoes the locks made by by #lockMedia().
15235 */
15236HRESULT SessionMachine::i_unlockMedia()
15237{
15238 AutoCaller autoCaller(this);
15239 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15240
15241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15242
15243 /* we may be holding important error info on the current thread;
15244 * preserve it */
15245 ErrorInfoKeeper eik;
15246
15247 HRESULT rc = mData->mSession.mLockedMedia.Clear();
15248 AssertComRC(rc);
15249 return rc;
15250}
15251
15252/**
15253 * Helper to change the machine state (reimplementation).
15254 *
15255 * @note Locks this object for writing.
15256 * @note This method must not call i_saveSettings or SaveSettings, otherwise
15257 * it can cause crashes in random places due to unexpectedly committing
15258 * the current settings. The caller is responsible for that. The call
15259 * to saveStateSettings is fine, because this method does not commit.
15260 */
15261HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
15262{
15263 LogFlowThisFuncEnter();
15264
15265 AutoCaller autoCaller(this);
15266 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15267
15268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15269
15270 MachineState_T oldMachineState = mData->mMachineState;
15271
15272 AssertMsgReturn(oldMachineState != aMachineState,
15273 ("oldMachineState=%s, aMachineState=%s\n",
15274 ::stringifyMachineState(oldMachineState), ::stringifyMachineState(aMachineState)),
15275 E_FAIL);
15276
15277 HRESULT rc = S_OK;
15278
15279 int stsFlags = 0;
15280 bool deleteSavedState = false;
15281
15282 /* detect some state transitions */
15283
15284 if ( ( ( oldMachineState == MachineState_Saved
15285 || oldMachineState == MachineState_AbortedSaved
15286 )
15287 && aMachineState == MachineState_Restoring
15288 )
15289 || ( ( oldMachineState == MachineState_PoweredOff
15290 || oldMachineState == MachineState_Teleported
15291 || oldMachineState == MachineState_Aborted
15292 )
15293 && ( aMachineState == MachineState_TeleportingIn
15294 || aMachineState == MachineState_Starting
15295 )
15296 )
15297 )
15298 {
15299 /* The EMT thread is about to start */
15300
15301 /* Nothing to do here for now... */
15302
15303 /// @todo NEWMEDIA don't let mDVDDrive and other children
15304 /// change anything when in the Starting/Restoring state
15305 }
15306 else if ( ( oldMachineState == MachineState_Running
15307 || oldMachineState == MachineState_Paused
15308 || oldMachineState == MachineState_Teleporting
15309 || oldMachineState == MachineState_OnlineSnapshotting
15310 || oldMachineState == MachineState_LiveSnapshotting
15311 || oldMachineState == MachineState_Stuck
15312 || oldMachineState == MachineState_Starting
15313 || oldMachineState == MachineState_Stopping
15314 || oldMachineState == MachineState_Saving
15315 || oldMachineState == MachineState_Restoring
15316 || oldMachineState == MachineState_TeleportingPausedVM
15317 || oldMachineState == MachineState_TeleportingIn
15318 )
15319 && ( aMachineState == MachineState_PoweredOff
15320 || aMachineState == MachineState_Saved
15321 || aMachineState == MachineState_Teleported
15322 || aMachineState == MachineState_Aborted
15323 || aMachineState == MachineState_AbortedSaved
15324 )
15325 )
15326 {
15327 /* The EMT thread has just stopped, unlock attached media. Note that as
15328 * opposed to locking that is done from Console, we do unlocking here
15329 * because the VM process may have aborted before having a chance to
15330 * properly unlock all media it locked. */
15331
15332 unlockMedia();
15333 }
15334
15335 if (oldMachineState == MachineState_Restoring)
15336 {
15337 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
15338 {
15339 /*
15340 * delete the saved state file once the machine has finished
15341 * restoring from it (note that Console sets the state from
15342 * Restoring to AbortedSaved if the VM couldn't restore successfully,
15343 * to give the user an ability to fix an error and retry --
15344 * we keep the saved state file in this case)
15345 */
15346 deleteSavedState = true;
15347 }
15348 }
15349 else if ( oldMachineState == MachineState_Saved
15350 && ( aMachineState == MachineState_PoweredOff
15351 || aMachineState == MachineState_Teleported
15352 )
15353 )
15354 {
15355 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
15356 deleteSavedState = true;
15357 mData->mCurrentStateModified = TRUE;
15358 stsFlags |= SaveSTS_CurStateModified;
15359 }
15360 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
15361 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
15362
15363 if ( aMachineState == MachineState_Starting
15364 || aMachineState == MachineState_Restoring
15365 || aMachineState == MachineState_TeleportingIn
15366 )
15367 {
15368 /* set the current state modified flag to indicate that the current
15369 * state is no more identical to the state in the
15370 * current snapshot */
15371 if (!mData->mCurrentSnapshot.isNull())
15372 {
15373 mData->mCurrentStateModified = TRUE;
15374 stsFlags |= SaveSTS_CurStateModified;
15375 }
15376 }
15377
15378 if (deleteSavedState)
15379 {
15380 if (mRemoveSavedState)
15381 {
15382 Assert(!mSSData->strStateFilePath.isEmpty());
15383
15384 // it is safe to delete the saved state file if ...
15385 if ( !mData->mFirstSnapshot // ... we have no snapshots or
15386 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
15387 // ... none of the snapshots share the saved state file
15388 )
15389 i_deleteFile(mSSData->strStateFilePath, true /* fIgnoreFailures */);
15390 }
15391
15392 mSSData->strStateFilePath.setNull();
15393 stsFlags |= SaveSTS_StateFilePath;
15394 }
15395
15396 /* redirect to the underlying peer machine */
15397 mPeer->i_setMachineState(aMachineState);
15398
15399 if ( oldMachineState != MachineState_RestoringSnapshot
15400 && ( aMachineState == MachineState_PoweredOff
15401 || aMachineState == MachineState_Teleported
15402 || aMachineState == MachineState_Aborted
15403 || aMachineState == MachineState_AbortedSaved
15404 || aMachineState == MachineState_Saved))
15405 {
15406 /* the machine has stopped execution
15407 * (or the saved state file was adopted) */
15408 stsFlags |= SaveSTS_StateTimeStamp;
15409 }
15410
15411 if ( ( oldMachineState == MachineState_PoweredOff
15412 || oldMachineState == MachineState_Aborted
15413 || oldMachineState == MachineState_Teleported
15414 )
15415 && aMachineState == MachineState_Saved)
15416 {
15417 /* the saved state file was adopted */
15418 Assert(!mSSData->strStateFilePath.isEmpty());
15419 stsFlags |= SaveSTS_StateFilePath;
15420 }
15421
15422#ifdef VBOX_WITH_GUEST_PROPS
15423 if ( aMachineState == MachineState_PoweredOff
15424 || aMachineState == MachineState_Aborted
15425 || aMachineState == MachineState_Teleported)
15426 {
15427 /* Make sure any transient guest properties get removed from the
15428 * property store on shutdown. */
15429 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
15430
15431 /* remove it from the settings representation */
15432 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
15433 for (settings::GuestPropertiesList::iterator
15434 it = llGuestProperties.begin();
15435 it != llGuestProperties.end();
15436 /*nothing*/)
15437 {
15438 const settings::GuestProperty &prop = *it;
15439 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
15440 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
15441 {
15442 it = llGuestProperties.erase(it);
15443 fNeedsSaving = true;
15444 }
15445 else
15446 {
15447 ++it;
15448 }
15449 }
15450
15451 /* Additionally remove it from the HWData representation. Required to
15452 * keep everything in sync, as this is what the API keeps using. */
15453 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
15454 for (HWData::GuestPropertyMap::iterator
15455 it = llHWGuestProperties.begin();
15456 it != llHWGuestProperties.end();
15457 /*nothing*/)
15458 {
15459 uint32_t fFlags = it->second.mFlags;
15460 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
15461 {
15462 /* iterator where we need to continue after the erase call
15463 * (C++03 is a fact still, and it doesn't return the iterator
15464 * which would allow continuing) */
15465 HWData::GuestPropertyMap::iterator it2 = it;
15466 ++it2;
15467 llHWGuestProperties.erase(it);
15468 it = it2;
15469 fNeedsSaving = true;
15470 }
15471 else
15472 {
15473 ++it;
15474 }
15475 }
15476
15477 if (fNeedsSaving)
15478 {
15479 mData->mCurrentStateModified = TRUE;
15480 stsFlags |= SaveSTS_CurStateModified;
15481 }
15482 }
15483#endif /* VBOX_WITH_GUEST_PROPS */
15484
15485 rc = i_saveStateSettings(stsFlags);
15486
15487 if ( ( oldMachineState != MachineState_PoweredOff
15488 && oldMachineState != MachineState_Aborted
15489 && oldMachineState != MachineState_Teleported
15490 )
15491 && ( aMachineState == MachineState_PoweredOff
15492 || aMachineState == MachineState_Aborted
15493 || aMachineState == MachineState_Teleported
15494 )
15495 )
15496 {
15497 /* we've been shut down for any reason */
15498 /* no special action so far */
15499 }
15500
15501 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, ::stringifyMachineState(mData->mMachineState) ));
15502 LogFlowThisFuncLeave();
15503 return rc;
15504}
15505
15506/**
15507 * Sends the current machine state value to the VM process.
15508 *
15509 * @note Locks this object for reading, then calls a client process.
15510 */
15511HRESULT SessionMachine::i_updateMachineStateOnClient()
15512{
15513 AutoCaller autoCaller(this);
15514 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15515
15516 ComPtr<IInternalSessionControl> directControl;
15517 {
15518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15519 AssertReturn(!!mData, E_FAIL);
15520 if (mData->mSession.mLockType == LockType_VM)
15521 directControl = mData->mSession.mDirectControl;
15522
15523 /* directControl may be already set to NULL here in #OnSessionEnd()
15524 * called too early by the direct session process while there is still
15525 * some operation (like deleting the snapshot) in progress. The client
15526 * process in this case is waiting inside Session::close() for the
15527 * "end session" process object to complete, while #uninit() called by
15528 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15529 * operation to complete. For now, we accept this inconsistent behavior
15530 * and simply do nothing here. */
15531
15532 if (mData->mSession.mState == SessionState_Unlocking)
15533 return S_OK;
15534 }
15535
15536 /* ignore notifications sent after #OnSessionEnd() is called */
15537 if (!directControl)
15538 return S_OK;
15539
15540 return directControl->UpdateMachineState(mData->mMachineState);
15541}
15542
15543
15544/*static*/
15545HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15546{
15547 va_list args;
15548 va_start(args, pcszMsg);
15549 HRESULT rc = setErrorInternalV(aResultCode,
15550 getStaticClassIID(),
15551 getStaticComponentName(),
15552 pcszMsg, args,
15553 false /* aWarning */,
15554 true /* aLogIt */);
15555 va_end(args);
15556 return rc;
15557}
15558
15559
15560HRESULT Machine::updateState(MachineState_T aState)
15561{
15562 NOREF(aState);
15563 ReturnComNotImplemented();
15564}
15565
15566HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15567{
15568 NOREF(aProgress);
15569 ReturnComNotImplemented();
15570}
15571
15572HRESULT Machine::endPowerUp(LONG aResult)
15573{
15574 NOREF(aResult);
15575 ReturnComNotImplemented();
15576}
15577
15578HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15579{
15580 NOREF(aProgress);
15581 ReturnComNotImplemented();
15582}
15583
15584HRESULT Machine::endPoweringDown(LONG aResult,
15585 const com::Utf8Str &aErrMsg)
15586{
15587 NOREF(aResult);
15588 NOREF(aErrMsg);
15589 ReturnComNotImplemented();
15590}
15591
15592HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15593 BOOL *aMatched,
15594 ULONG *aMaskedInterfaces)
15595{
15596 NOREF(aDevice);
15597 NOREF(aMatched);
15598 NOREF(aMaskedInterfaces);
15599 ReturnComNotImplemented();
15600
15601}
15602
15603HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15604{
15605 NOREF(aId); NOREF(aCaptureFilename);
15606 ReturnComNotImplemented();
15607}
15608
15609HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15610 BOOL aDone)
15611{
15612 NOREF(aId);
15613 NOREF(aDone);
15614 ReturnComNotImplemented();
15615}
15616
15617HRESULT Machine::autoCaptureUSBDevices()
15618{
15619 ReturnComNotImplemented();
15620}
15621
15622HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15623{
15624 NOREF(aDone);
15625 ReturnComNotImplemented();
15626}
15627
15628HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15629 ComPtr<IProgress> &aProgress)
15630{
15631 NOREF(aSession);
15632 NOREF(aProgress);
15633 ReturnComNotImplemented();
15634}
15635
15636HRESULT Machine::finishOnlineMergeMedium()
15637{
15638 ReturnComNotImplemented();
15639}
15640
15641HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15642 std::vector<com::Utf8Str> &aValues,
15643 std::vector<LONG64> &aTimestamps,
15644 std::vector<com::Utf8Str> &aFlags)
15645{
15646 NOREF(aNames);
15647 NOREF(aValues);
15648 NOREF(aTimestamps);
15649 NOREF(aFlags);
15650 ReturnComNotImplemented();
15651}
15652
15653HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15654 const com::Utf8Str &aValue,
15655 LONG64 aTimestamp,
15656 const com::Utf8Str &aFlags,
15657 BOOL fWasDeleted)
15658{
15659 NOREF(aName);
15660 NOREF(aValue);
15661 NOREF(aTimestamp);
15662 NOREF(aFlags);
15663 NOREF(fWasDeleted);
15664 ReturnComNotImplemented();
15665}
15666
15667HRESULT Machine::lockMedia()
15668{
15669 ReturnComNotImplemented();
15670}
15671
15672HRESULT Machine::unlockMedia()
15673{
15674 ReturnComNotImplemented();
15675}
15676
15677HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15678 ComPtr<IMediumAttachment> &aNewAttachment)
15679{
15680 NOREF(aAttachment);
15681 NOREF(aNewAttachment);
15682 ReturnComNotImplemented();
15683}
15684
15685HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15686 ULONG aCpuUser,
15687 ULONG aCpuKernel,
15688 ULONG aCpuIdle,
15689 ULONG aMemTotal,
15690 ULONG aMemFree,
15691 ULONG aMemBalloon,
15692 ULONG aMemShared,
15693 ULONG aMemCache,
15694 ULONG aPagedTotal,
15695 ULONG aMemAllocTotal,
15696 ULONG aMemFreeTotal,
15697 ULONG aMemBalloonTotal,
15698 ULONG aMemSharedTotal,
15699 ULONG aVmNetRx,
15700 ULONG aVmNetTx)
15701{
15702 NOREF(aValidStats);
15703 NOREF(aCpuUser);
15704 NOREF(aCpuKernel);
15705 NOREF(aCpuIdle);
15706 NOREF(aMemTotal);
15707 NOREF(aMemFree);
15708 NOREF(aMemBalloon);
15709 NOREF(aMemShared);
15710 NOREF(aMemCache);
15711 NOREF(aPagedTotal);
15712 NOREF(aMemAllocTotal);
15713 NOREF(aMemFreeTotal);
15714 NOREF(aMemBalloonTotal);
15715 NOREF(aMemSharedTotal);
15716 NOREF(aVmNetRx);
15717 NOREF(aVmNetTx);
15718 ReturnComNotImplemented();
15719}
15720
15721HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15722 com::Utf8Str &aResult)
15723{
15724 NOREF(aAuthParams);
15725 NOREF(aResult);
15726 ReturnComNotImplemented();
15727}
15728
15729com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15730{
15731 com::Utf8Str strControllerName = "Unknown";
15732 switch (aBusType)
15733 {
15734 case StorageBus_IDE:
15735 {
15736 strControllerName = "IDE";
15737 break;
15738 }
15739 case StorageBus_SATA:
15740 {
15741 strControllerName = "SATA";
15742 break;
15743 }
15744 case StorageBus_SCSI:
15745 {
15746 strControllerName = "SCSI";
15747 break;
15748 }
15749 case StorageBus_Floppy:
15750 {
15751 strControllerName = "Floppy";
15752 break;
15753 }
15754 case StorageBus_SAS:
15755 {
15756 strControllerName = "SAS";
15757 break;
15758 }
15759 case StorageBus_USB:
15760 {
15761 strControllerName = "USB";
15762 break;
15763 }
15764 default:
15765 break;
15766 }
15767 return strControllerName;
15768}
15769
15770HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15771{
15772 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15773
15774 AutoCaller autoCaller(this);
15775 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15776
15777 HRESULT rc = S_OK;
15778
15779 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15780 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15781 rc = getUSBDeviceFilters(usbDeviceFilters);
15782 if (FAILED(rc)) return rc;
15783
15784 NOREF(aFlags);
15785 com::Utf8Str osTypeId;
15786 ComObjPtr<GuestOSType> osType = NULL;
15787
15788 /* Get the guest os type as a string from the VB. */
15789 rc = getOSTypeId(osTypeId);
15790 if (FAILED(rc)) return rc;
15791
15792 /* Get the os type obj that coresponds, can be used to get
15793 * the defaults for this guest OS. */
15794 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15795 if (FAILED(rc)) return rc;
15796
15797 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15798
15799 /* Let the OS type select 64-bit ness. */
15800 mHWData->mLongMode = osType->i_is64Bit()
15801 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15802
15803 /* Let the OS type enable the X2APIC */
15804 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15805
15806 /* This one covers IOAPICEnabled. */
15807 mBIOSSettings->i_applyDefaults(osType);
15808
15809 /* Initialize default record settings. */
15810 mRecordingSettings->i_applyDefaults();
15811
15812 /* Initialize default BIOS settings here */
15813 /* Hardware virtualization must be ON by default */
15814 mHWData->mAPIC = true;
15815 mHWData->mHWVirtExEnabled = true;
15816
15817 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15818 if (FAILED(rc)) return rc;
15819
15820 rc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15821 if (FAILED(rc)) return rc;
15822
15823 /* Graphics stuff. */
15824 GraphicsControllerType_T graphicsController;
15825 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15826 if (FAILED(rc)) return rc;
15827
15828 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15829 if (FAILED(rc)) return rc;
15830
15831 ULONG vramSize;
15832 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15833 if (FAILED(rc)) return rc;
15834
15835 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15836 if (FAILED(rc)) return rc;
15837
15838 BOOL fAccelerate2DVideoEnabled;
15839 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15840 if (FAILED(rc)) return rc;
15841
15842 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15843 if (FAILED(rc)) return rc;
15844
15845 BOOL fAccelerate3DEnabled;
15846 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15847 if (FAILED(rc)) return rc;
15848
15849 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15850 if (FAILED(rc)) return rc;
15851
15852 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15853 if (FAILED(rc)) return rc;
15854
15855 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15856 if (FAILED(rc)) return rc;
15857
15858 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15859 if (FAILED(rc)) return rc;
15860
15861 BOOL mRTCUseUTC;
15862 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15863 if (FAILED(rc)) return rc;
15864
15865 setRTCUseUTC(mRTCUseUTC);
15866 if (FAILED(rc)) return rc;
15867
15868 /* the setter does more than just the assignment, so use it */
15869 ChipsetType_T enmChipsetType;
15870 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15871 if (FAILED(rc)) return rc;
15872
15873 rc = COMSETTER(ChipsetType)(enmChipsetType);
15874 if (FAILED(rc)) return rc;
15875
15876 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15877 if (FAILED(rc)) return rc;
15878
15879 /* Apply IOMMU defaults. */
15880 IommuType_T enmIommuType;
15881 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15882 if (FAILED(rc)) return rc;
15883
15884 rc = COMSETTER(IommuType)(enmIommuType);
15885 if (FAILED(rc)) return rc;
15886
15887 /* Apply network adapters defaults */
15888 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15889 mNetworkAdapters[slot]->i_applyDefaults(osType);
15890
15891 /* Apply serial port defaults */
15892 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15893 mSerialPorts[slot]->i_applyDefaults(osType);
15894
15895 /* Apply parallel port defaults - not OS dependent*/
15896 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15897 mParallelPorts[slot]->i_applyDefaults();
15898
15899 /* This one covers the TPM type. */
15900 mTrustedPlatformModule->i_applyDefaults(osType);
15901
15902 /* This one covers secure boot. */
15903 rc = mNvramStore->i_applyDefaults(osType);
15904 if (FAILED(rc)) return rc;
15905
15906 /* Audio stuff. */
15907 rc = mAudioSettings->i_applyDefaults(osType);
15908 if (FAILED(rc)) return rc;
15909
15910 /* Storage Controllers */
15911 StorageControllerType_T hdStorageControllerType;
15912 StorageBus_T hdStorageBusType;
15913 StorageControllerType_T dvdStorageControllerType;
15914 StorageBus_T dvdStorageBusType;
15915 BOOL recommendedFloppy;
15916 ComPtr<IStorageController> floppyController;
15917 ComPtr<IStorageController> hdController;
15918 ComPtr<IStorageController> dvdController;
15919 Utf8Str strFloppyName, strDVDName, strHDName;
15920
15921 /* GUI auto generates controller names using bus type. Do the same*/
15922 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15923
15924 /* Floppy recommended? add one. */
15925 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15926 if (FAILED(rc)) return rc;
15927 if (recommendedFloppy)
15928 {
15929 rc = addStorageController(strFloppyName,
15930 StorageBus_Floppy,
15931 floppyController);
15932 if (FAILED(rc)) return rc;
15933 }
15934
15935 /* Setup one DVD storage controller. */
15936 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15937 if (FAILED(rc)) return rc;
15938
15939 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15940 if (FAILED(rc)) return rc;
15941
15942 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15943
15944 rc = addStorageController(strDVDName,
15945 dvdStorageBusType,
15946 dvdController);
15947 if (FAILED(rc)) return rc;
15948
15949 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15950 if (FAILED(rc)) return rc;
15951
15952 /* Setup one HDD storage controller. */
15953 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15954 if (FAILED(rc)) return rc;
15955
15956 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15957 if (FAILED(rc)) return rc;
15958
15959 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15960
15961 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15962 {
15963 rc = addStorageController(strHDName,
15964 hdStorageBusType,
15965 hdController);
15966 if (FAILED(rc)) return rc;
15967
15968 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15969 if (FAILED(rc)) return rc;
15970 }
15971 else
15972 {
15973 /* The HD controller is the same as DVD: */
15974 hdController = dvdController;
15975 }
15976
15977 /* Limit the AHCI port count if it's used because windows has trouble with
15978 * too many ports and other guest (OS X in particular) may take extra long
15979 * boot: */
15980
15981 // pParent = static_cast<Medium*>(aP)
15982 IStorageController *temp = hdController;
15983 ComObjPtr<StorageController> storageController;
15984 storageController = static_cast<StorageController *>(temp);
15985
15986 // tempHDController = aHDController;
15987 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15988 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15989 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15990 storageController->COMSETTER(PortCount)(1);
15991
15992 /* USB stuff */
15993
15994 bool ohciEnabled = false;
15995
15996 ComPtr<IUSBController> usbController;
15997 BOOL recommendedUSB3;
15998 BOOL recommendedUSB;
15999 BOOL usbProxyAvailable;
16000
16001 getUSBProxyAvailable(&usbProxyAvailable);
16002 if (FAILED(rc)) return rc;
16003
16004 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
16005 if (FAILED(rc)) return rc;
16006 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
16007 if (FAILED(rc)) return rc;
16008
16009 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
16010 {
16011#ifdef VBOX_WITH_EXTPACK
16012 /* USB 3.0 is only available if the proper ExtPack is installed. */
16013 ExtPackManager *aManager = mParent->i_getExtPackManager();
16014 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
16015 {
16016 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
16017 if (FAILED(rc)) return rc;
16018
16019 /* xHci includes OHCI */
16020 ohciEnabled = true;
16021 }
16022#endif
16023 }
16024 if ( !ohciEnabled
16025 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
16026 {
16027 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16028 if (FAILED(rc)) return rc;
16029 ohciEnabled = true;
16030
16031#ifdef VBOX_WITH_EXTPACK
16032 /* USB 2.0 is only available if the proper ExtPack is installed.
16033 * Note. Configuring EHCI here and providing messages about
16034 * the missing extpack isn't exactly clean, but it is a
16035 * necessary evil to patch over legacy compatability issues
16036 * introduced by the new distribution model. */
16037 ExtPackManager *manager = mParent->i_getExtPackManager();
16038 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
16039 {
16040 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
16041 if (FAILED(rc)) return rc;
16042 }
16043#endif
16044 }
16045
16046 /* Set recommended human interface device types: */
16047 BOOL recommendedUSBHID;
16048 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
16049 if (FAILED(rc)) return rc;
16050
16051 if (recommendedUSBHID)
16052 {
16053 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
16054 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
16055 if (!ohciEnabled && !usbDeviceFilters.isNull())
16056 {
16057 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16058 if (FAILED(rc)) return rc;
16059 }
16060 }
16061
16062 BOOL recommendedUSBTablet;
16063 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
16064 if (FAILED(rc)) return rc;
16065
16066 if (recommendedUSBTablet)
16067 {
16068 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
16069 if (!ohciEnabled && !usbDeviceFilters.isNull())
16070 {
16071 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
16072 if (FAILED(rc)) return rc;
16073 }
16074 }
16075
16076 /* Enable the VMMDev testing feature for bootsector VMs: */
16077 if (osTypeId == "VBoxBS_64")
16078 {
16079 rc = setExtraData("VBoxInternal/Devices/VMMDev/0/Config/TestingEnabled", "1");
16080 if (FAILED(rc))
16081 return rc;
16082 }
16083
16084 return S_OK;
16085}
16086
16087#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
16088/**
16089 * Task record for change encryption settins.
16090 */
16091class Machine::ChangeEncryptionTask
16092 : public Machine::Task
16093{
16094public:
16095 ChangeEncryptionTask(Machine *m,
16096 Progress *p,
16097 const Utf8Str &t,
16098 const com::Utf8Str &aCurrentPassword,
16099 const com::Utf8Str &aCipher,
16100 const com::Utf8Str &aNewPassword,
16101 const com::Utf8Str &aNewPasswordId,
16102 const BOOL aForce,
16103 const MediaList &llMedia)
16104 : Task(m, p, t),
16105 mstrNewPassword(aNewPassword),
16106 mstrCurrentPassword(aCurrentPassword),
16107 mstrCipher(aCipher),
16108 mstrNewPasswordId(aNewPasswordId),
16109 mForce(aForce),
16110 mllMedia(llMedia)
16111 {}
16112
16113 ~ChangeEncryptionTask()
16114 {
16115 if (mstrNewPassword.length())
16116 RTMemWipeThoroughly(mstrNewPassword.mutableRaw(), mstrNewPassword.length(), 10 /* cPasses */);
16117 if (mstrCurrentPassword.length())
16118 RTMemWipeThoroughly(mstrCurrentPassword.mutableRaw(), mstrCurrentPassword.length(), 10 /* cPasses */);
16119 if (m_pCryptoIf)
16120 {
16121 m_pMachine->i_getVirtualBox()->i_releaseCryptoIf(m_pCryptoIf);
16122 m_pCryptoIf = NULL;
16123 }
16124 }
16125
16126 Utf8Str mstrNewPassword;
16127 Utf8Str mstrCurrentPassword;
16128 Utf8Str mstrCipher;
16129 Utf8Str mstrNewPasswordId;
16130 BOOL mForce;
16131 MediaList mllMedia;
16132 PCVBOXCRYPTOIF m_pCryptoIf;
16133private:
16134 void handler()
16135 {
16136 try
16137 {
16138 m_pMachine->i_changeEncryptionHandler(*this);
16139 }
16140 catch (...)
16141 {
16142 LogRel(("Some exception in the function Machine::i_changeEncryptionHandler()\n"));
16143 }
16144 }
16145
16146 friend void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task);
16147};
16148
16149/**
16150 * Scans specified directory and fills list by files found
16151 *
16152 * @returns VBox status code.
16153 * @param lstFiles
16154 * @param strDir
16155 * @param filePattern
16156 */
16157int Machine::i_findFiles(std::list<com::Utf8Str> &lstFiles, const com::Utf8Str &strDir,
16158 const com::Utf8Str &strPattern)
16159{
16160 /* To get all entries including subdirectories. */
16161 char *pszFilePattern = RTPathJoinA(strDir.c_str(), "*");
16162 if (!pszFilePattern)
16163 return VERR_NO_STR_MEMORY;
16164
16165 PRTDIRENTRYEX pDirEntry = NULL;
16166 RTDIR hDir;
16167 size_t cbDirEntry = sizeof(RTDIRENTRYEX);
16168 int rc = RTDirOpenFiltered(&hDir, pszFilePattern, RTDIRFILTER_WINNT, 0 /*fFlags*/);
16169 if (RT_SUCCESS(rc))
16170 {
16171 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
16172 if (pDirEntry)
16173 {
16174 while ( (rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK))
16175 != VERR_NO_MORE_FILES)
16176 {
16177 char *pszFilePath = NULL;
16178
16179 if (rc == VERR_BUFFER_OVERFLOW)
16180 {
16181 /* allocate new buffer. */
16182 RTMemFree(pDirEntry);
16183 pDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbDirEntry);
16184 if (!pDirEntry)
16185 {
16186 rc = VERR_NO_MEMORY;
16187 break;
16188 }
16189 /* Retry. */
16190 rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
16191 if (RT_FAILURE(rc))
16192 break;
16193 }
16194 else if (RT_FAILURE(rc))
16195 break;
16196
16197 /* Exclude . and .. */
16198 if ( (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '\0')
16199 || (pDirEntry->szName[0] == '.' && pDirEntry->szName[1] == '.' && pDirEntry->szName[2] == '\0'))
16200 continue;
16201 if (RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode))
16202 {
16203 char *pszSubDirPath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
16204 if (!pszSubDirPath)
16205 {
16206 rc = VERR_NO_STR_MEMORY;
16207 break;
16208 }
16209 rc = i_findFiles(lstFiles, pszSubDirPath, strPattern);
16210 RTMemFree(pszSubDirPath);
16211 if (RT_FAILURE(rc))
16212 break;
16213 continue;
16214 }
16215
16216 /* We got the new entry. */
16217 if (!RTFS_IS_FILE(pDirEntry->Info.Attr.fMode))
16218 continue;
16219
16220 if (!RTStrSimplePatternMatch(strPattern.c_str(), pDirEntry->szName))
16221 continue;
16222
16223 /* Prepend the path to the libraries. */
16224 pszFilePath = RTPathJoinA(strDir.c_str(), pDirEntry->szName);
16225 if (!pszFilePath)
16226 {
16227 rc = VERR_NO_STR_MEMORY;
16228 break;
16229 }
16230
16231 lstFiles.push_back(pszFilePath);
16232 RTStrFree(pszFilePath);
16233 }
16234
16235 RTMemFree(pDirEntry);
16236 }
16237 else
16238 rc = VERR_NO_MEMORY;
16239
16240 RTDirClose(hDir);
16241 }
16242 else
16243 {
16244 /* On Windows the above immediately signals that there are no
16245 * files matching, while on other platforms enumerating the
16246 * files below fails. Either way: stop searching. */
16247 }
16248
16249 if ( rc == VERR_NO_MORE_FILES
16250 || rc == VERR_FILE_NOT_FOUND
16251 || rc == VERR_PATH_NOT_FOUND)
16252 rc = VINF_SUCCESS;
16253 RTStrFree(pszFilePattern);
16254 return rc;
16255}
16256
16257/**
16258 * Helper to set up an I/O stream to read or write a possibly encrypted file.
16259 *
16260 * @returns VBox status code.
16261 * @param pszFilename The file to open.
16262 * @param pCryptoIf Pointer to the cryptographic interface if the file should be encrypted or contains encrypted data.
16263 * @param pszKeyStore The keystore if the file should be encrypted or contains encrypted data.
16264 * @param pszPassword The password if the file should be encrypted or contains encrypted data.
16265 * @param fOpen The open flags for the file.
16266 * @param phVfsIos Where to store the handle to the I/O stream on success.
16267 */
16268int Machine::i_createIoStreamForFile(const char *pszFilename, PCVBOXCRYPTOIF pCryptoIf,
16269 const char *pszKeyStore, const char *pszPassword,
16270 uint64_t fOpen, PRTVFSIOSTREAM phVfsIos)
16271{
16272 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
16273 int vrc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
16274 if (RT_SUCCESS(vrc))
16275 {
16276 if (pCryptoIf)
16277 {
16278 RTVFSFILE hVfsFileCrypto = NIL_RTVFSFILE;
16279 vrc = pCryptoIf->pfnCryptoFileFromVfsFile(hVfsFile, pszKeyStore, pszPassword, &hVfsFileCrypto);
16280 if (RT_SUCCESS(vrc))
16281 {
16282 RTVfsFileRelease(hVfsFile);
16283 hVfsFile = hVfsFileCrypto;
16284 }
16285 }
16286
16287 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
16288 RTVfsFileRelease(hVfsFile);
16289 }
16290
16291 return vrc;
16292}
16293
16294/**
16295 * Helper function processing all actions for one component (saved state files,
16296 * NVRAM files, etc). Used by Machine::i_changeEncryptionHandler only.
16297 *
16298 * @param task
16299 * @param strDirectory
16300 * @param strFilePattern
16301 * @param strMagic
16302 * @param strKeyStore
16303 * @param strKeyId
16304 * @return
16305 */
16306HRESULT Machine::i_changeEncryptionForComponent(ChangeEncryptionTask &task, const com::Utf8Str strDirectory,
16307 const com::Utf8Str strFilePattern, com::Utf8Str &strKeyStore,
16308 com::Utf8Str &strKeyId, int iCipherMode)
16309{
16310 bool fDecrypt = task.mstrCurrentPassword.isNotEmpty()
16311 && task.mstrCipher.isEmpty()
16312 && task.mstrNewPassword.isEmpty()
16313 && task.mstrNewPasswordId.isEmpty();
16314 bool fEncrypt = task.mstrCurrentPassword.isEmpty()
16315 && task.mstrCipher.isNotEmpty()
16316 && task.mstrNewPassword.isNotEmpty()
16317 && task.mstrNewPasswordId.isNotEmpty();
16318
16319 /* check if the cipher is changed which causes the reencryption*/
16320
16321 const char *pszTaskCipher = NULL;
16322 if (task.mstrCipher.isNotEmpty())
16323 pszTaskCipher = getCipherString(task.mstrCipher.c_str(), iCipherMode);
16324
16325 if (!task.mForce && !fDecrypt && !fEncrypt)
16326 {
16327 char *pszCipher = NULL;
16328 int vrc = task.m_pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(strKeyStore.c_str(),
16329 NULL /*pszPassword*/,
16330 NULL /*ppbKey*/,
16331 NULL /*pcbKey*/,
16332 &pszCipher);
16333 if (RT_SUCCESS(vrc))
16334 {
16335 task.mForce = strcmp(pszTaskCipher, pszCipher) != 0;
16336 RTMemFree(pszCipher);
16337 }
16338 else
16339 return setErrorBoth(E_FAIL, vrc, tr("Obtain cipher for '%s' files failed (%Rrc)"),
16340 strFilePattern.c_str(), vrc);
16341 }
16342
16343 /* Only the password needs to be changed */
16344 if (!task.mForce && !fDecrypt && !fEncrypt)
16345 {
16346 Assert(task.m_pCryptoIf);
16347
16348 VBOXCRYPTOCTX hCryptoCtx;
16349 int vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(strKeyStore.c_str(), task.mstrCurrentPassword.c_str(), &hCryptoCtx);
16350 if (RT_FAILURE(vrc))
16351 return setErrorBoth(E_FAIL, vrc, tr("Loading old key store for '%s' files failed, (%Rrc)"),
16352 strFilePattern.c_str(), vrc);
16353 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
16354 if (RT_FAILURE(vrc))
16355 return setErrorBoth(E_FAIL, vrc, tr("Changing the password for '%s' files failed, (%Rrc)"),
16356 strFilePattern.c_str(), vrc);
16357
16358 char *pszKeyStore = NULL;
16359 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16360 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16361 if (RT_FAILURE(vrc))
16362 return setErrorBoth(E_FAIL, vrc, tr("Saving the key store for '%s' files failed, (%Rrc)"),
16363 strFilePattern.c_str(), vrc);
16364 strKeyStore = pszKeyStore;
16365 RTMemFree(pszKeyStore);
16366 strKeyId = task.mstrNewPasswordId;
16367 return S_OK;
16368 }
16369
16370 /* Reencryption required */
16371 HRESULT rc = S_OK;
16372 int vrc = VINF_SUCCESS;
16373
16374 std::list<com::Utf8Str> lstFiles;
16375 if (SUCCEEDED(rc))
16376 {
16377 vrc = i_findFiles(lstFiles, strDirectory, strFilePattern);
16378 if (RT_FAILURE(vrc))
16379 rc = setErrorBoth(E_FAIL, vrc, tr("Getting file list for '%s' files failed, (%Rrc)"),
16380 strFilePattern.c_str(), vrc);
16381 }
16382 com::Utf8Str strNewKeyStore;
16383 if (SUCCEEDED(rc))
16384 {
16385 if (!fDecrypt)
16386 {
16387 VBOXCRYPTOCTX hCryptoCtx;
16388 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(pszTaskCipher, task.mstrNewPassword.c_str(), &hCryptoCtx);
16389 if (RT_FAILURE(vrc))
16390 return setErrorBoth(E_FAIL, vrc, tr("Create new key store for '%s' files failed, (%Rrc)"),
16391 strFilePattern.c_str(), vrc);
16392
16393 char *pszKeyStore = NULL;
16394 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16395 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16396 if (RT_FAILURE(vrc))
16397 return setErrorBoth(E_FAIL, vrc, tr("Saving the new key store for '%s' files failed, (%Rrc)"),
16398 strFilePattern.c_str(), vrc);
16399 strNewKeyStore = pszKeyStore;
16400 RTMemFree(pszKeyStore);
16401 }
16402
16403 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
16404 it != lstFiles.end();
16405 ++it)
16406 {
16407 RTVFSIOSTREAM hVfsIosOld = NIL_RTVFSIOSTREAM;
16408 RTVFSIOSTREAM hVfsIosNew = NIL_RTVFSIOSTREAM;
16409
16410 uint64_t fOpenForRead = RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE;
16411 uint64_t fOpenForWrite = RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE;
16412
16413 vrc = i_createIoStreamForFile((*it).c_str(),
16414 fEncrypt ? NULL : task.m_pCryptoIf,
16415 fEncrypt ? NULL : strKeyStore.c_str(),
16416 fEncrypt ? NULL : task.mstrCurrentPassword.c_str(),
16417 fOpenForRead, &hVfsIosOld);
16418 if (RT_SUCCESS(vrc))
16419 {
16420 vrc = i_createIoStreamForFile((*it + ".tmp").c_str(),
16421 fDecrypt ? NULL : task.m_pCryptoIf,
16422 fDecrypt ? NULL : strNewKeyStore.c_str(),
16423 fDecrypt ? NULL : task.mstrNewPassword.c_str(),
16424 fOpenForWrite, &hVfsIosNew);
16425 if (RT_FAILURE(vrc))
16426 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
16427 (*it + ".tmp").c_str(), vrc);
16428 }
16429 else
16430 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening file '%s' failed, (%Rrc)"),
16431 (*it).c_str(), vrc);
16432
16433 if (RT_SUCCESS(vrc))
16434 {
16435 vrc = RTVfsUtilPumpIoStreams(hVfsIosOld, hVfsIosNew, BUF_DATA_SIZE);
16436 if (RT_FAILURE(vrc))
16437 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Changing encryption of the file '%s' failed with %Rrc"),
16438 (*it).c_str(), vrc);
16439 }
16440
16441 if (hVfsIosOld != NIL_RTVFSIOSTREAM)
16442 RTVfsIoStrmRelease(hVfsIosOld);
16443 if (hVfsIosNew != NIL_RTVFSIOSTREAM)
16444 RTVfsIoStrmRelease(hVfsIosNew);
16445 }
16446 }
16447
16448 if (SUCCEEDED(rc))
16449 {
16450 for (std::list<com::Utf8Str>::iterator it = lstFiles.begin();
16451 it != lstFiles.end();
16452 ++it)
16453 {
16454 vrc = RTFileRename((*it + ".tmp").c_str(), (*it).c_str(), RTPATHRENAME_FLAGS_REPLACE);
16455 if (RT_FAILURE(vrc))
16456 {
16457 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming the file '%s' failed, (%Rrc)"),
16458 (*it + ".tmp").c_str(), vrc);
16459 break;
16460 }
16461 }
16462 }
16463
16464 if (SUCCEEDED(rc))
16465 {
16466 strKeyStore = strNewKeyStore;
16467 strKeyId = task.mstrNewPasswordId;
16468 }
16469
16470 return rc;
16471}
16472
16473/**
16474 * Task thread implementation for Machine::changeEncryption(), called from
16475 * Machine::taskHandler().
16476 *
16477 * @note Locks this object for writing.
16478 *
16479 * @param task
16480 * @return
16481 */
16482void Machine::i_changeEncryptionHandler(ChangeEncryptionTask &task)
16483{
16484 LogFlowThisFuncEnter();
16485
16486 AutoCaller autoCaller(this);
16487 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
16488 if (FAILED(autoCaller.rc()))
16489 {
16490 /* we might have been uninitialized because the session was accidentally
16491 * closed by the client, so don't assert */
16492 HRESULT rc = setError(E_FAIL,
16493 tr("The session has been accidentally closed"));
16494 task.m_pProgress->i_notifyComplete(rc);
16495 LogFlowThisFuncLeave();
16496 return;
16497 }
16498
16499 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16500
16501 HRESULT rc = S_OK;
16502 com::Utf8Str strOldKeyId = mData->mstrKeyId;
16503 com::Utf8Str strOldKeyStore = mData->mstrKeyStore;
16504 try
16505 {
16506 rc = this->i_getVirtualBox()->i_retainCryptoIf(&task.m_pCryptoIf);
16507 if (FAILED(rc))
16508 throw rc;
16509
16510 if (task.mstrCurrentPassword.isEmpty())
16511 {
16512 if (mData->mstrKeyStore.isNotEmpty())
16513 throw setError(VBOX_E_PASSWORD_INCORRECT,
16514 tr("The password given for the encrypted VM is incorrect"));
16515 }
16516 else
16517 {
16518 if (mData->mstrKeyStore.isEmpty())
16519 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16520 tr("The VM is not configured for encryption"));
16521 rc = checkEncryptionPassword(task.mstrCurrentPassword);
16522 if (rc == VBOX_E_PASSWORD_INCORRECT)
16523 throw setError(VBOX_E_PASSWORD_INCORRECT,
16524 tr("The password to decrypt the VM is incorrect"));
16525 }
16526
16527 if (task.mstrCipher.isNotEmpty())
16528 {
16529 if ( task.mstrNewPassword.isEmpty()
16530 && task.mstrNewPasswordId.isEmpty()
16531 && task.mstrCurrentPassword.isNotEmpty())
16532 {
16533 /* An empty password and password ID will default to the current password. */
16534 task.mstrNewPassword = task.mstrCurrentPassword;
16535 }
16536 else if (task.mstrNewPassword.isEmpty())
16537 throw setError(VBOX_E_OBJECT_NOT_FOUND,
16538 tr("A password must be given for the VM encryption"));
16539 else if (task.mstrNewPasswordId.isEmpty())
16540 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16541 tr("A valid identifier for the password must be given"));
16542 }
16543 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
16544 throw setError(VBOX_E_INVALID_OBJECT_STATE,
16545 tr("The password and password identifier must be empty if the output should be unencrypted"));
16546
16547 /*
16548 * Save config.
16549 * Must be first operation to prevent making encrypted copies
16550 * for old version of the config file.
16551 */
16552 int fSave = Machine::SaveS_Force;
16553 if (task.mstrNewPassword.isNotEmpty())
16554 {
16555 VBOXCRYPTOCTX hCryptoCtx;
16556
16557 int vrc = VINF_SUCCESS;
16558 if (task.mForce || task.mstrCurrentPassword.isEmpty() || task.mstrCipher.isNotEmpty())
16559 {
16560 vrc = task.m_pCryptoIf->pfnCryptoCtxCreate(getCipherString(task.mstrCipher.c_str(), CipherModeGcm),
16561 task.mstrNewPassword.c_str(), &hCryptoCtx);
16562 if (RT_FAILURE(vrc))
16563 throw setErrorBoth(E_FAIL, vrc, tr("New key store creation failed, (%Rrc)"), vrc);
16564 }
16565 else
16566 {
16567 vrc = task.m_pCryptoIf->pfnCryptoCtxLoad(mData->mstrKeyStore.c_str(),
16568 task.mstrCurrentPassword.c_str(),
16569 &hCryptoCtx);
16570 if (RT_FAILURE(vrc))
16571 throw setErrorBoth(E_FAIL, vrc, tr("Loading old key store failed, (%Rrc)"), vrc);
16572 vrc = task.m_pCryptoIf->pfnCryptoCtxPasswordChange(hCryptoCtx, task.mstrNewPassword.c_str());
16573 if (RT_FAILURE(vrc))
16574 throw setErrorBoth(E_FAIL, vrc, tr("Changing the password failed, (%Rrc)"), vrc);
16575 }
16576
16577 char *pszKeyStore;
16578 vrc = task.m_pCryptoIf->pfnCryptoCtxSave(hCryptoCtx, &pszKeyStore);
16579 task.m_pCryptoIf->pfnCryptoCtxDestroy(hCryptoCtx);
16580 if (RT_FAILURE(vrc))
16581 throw setErrorBoth(E_FAIL, vrc, tr("Saving the key store failed, (%Rrc)"), vrc);
16582 mData->mstrKeyStore = pszKeyStore;
16583 RTStrFree(pszKeyStore);
16584 mData->mstrKeyId = task.mstrNewPasswordId;
16585 size_t cbPassword = task.mstrNewPassword.length() + 1;
16586 uint8_t *pbPassword = (uint8_t *)task.mstrNewPassword.c_str();
16587 mData->mpKeyStore->deleteSecretKey(task.mstrNewPasswordId);
16588 mData->mpKeyStore->addSecretKey(task.mstrNewPasswordId, pbPassword, cbPassword);
16589 mNvramStore->i_addPassword(task.mstrNewPasswordId, task.mstrNewPassword);
16590
16591 /*
16592 * Remove backuped config after saving because it can contain
16593 * unencrypted version of the config
16594 */
16595 fSave |= Machine::SaveS_RemoveBackup;
16596 }
16597 else
16598 {
16599 mData->mstrKeyId.setNull();
16600 mData->mstrKeyStore.setNull();
16601 }
16602
16603 Bstr bstrCurrentPassword(task.mstrCurrentPassword);
16604 Bstr bstrCipher(getCipherString(task.mstrCipher.c_str(), CipherModeXts));
16605 Bstr bstrNewPassword(task.mstrNewPassword);
16606 Bstr bstrNewPasswordId(task.mstrNewPasswordId);
16607 /* encrypt mediums */
16608 alock.release();
16609 for (MediaList::iterator it = task.mllMedia.begin();
16610 it != task.mllMedia.end();
16611 ++it)
16612 {
16613 ComPtr<IProgress> pProgress1;
16614 HRESULT hrc = (*it)->ChangeEncryption(bstrCurrentPassword.raw(), bstrCipher.raw(),
16615 bstrNewPassword.raw(), bstrNewPasswordId.raw(),
16616 pProgress1.asOutParam());
16617 if (FAILED(hrc)) throw hrc;
16618 hrc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress1, 0 /* indefinite wait */);
16619 if (FAILED(hrc)) throw hrc;
16620 }
16621 alock.acquire();
16622
16623 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the SAV files")).raw(), 1);
16624
16625 Utf8Str strFullSnapshotFolder;
16626 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
16627
16628 /* .sav files (main and snapshots) */
16629 rc = i_changeEncryptionForComponent(task, strFullSnapshotFolder, "*.sav",
16630 mSSData->strStateKeyStore, mSSData->strStateKeyId, CipherModeGcm);
16631 if (FAILED(rc))
16632 /* the helper function already sets error object */
16633 throw rc;
16634
16635 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the NVRAM files")).raw(), 1);
16636
16637 /* .nvram files */
16638 com::Utf8Str strNVRAMKeyId;
16639 com::Utf8Str strNVRAMKeyStore;
16640 rc = mNvramStore->i_getEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16641 if (FAILED(rc))
16642 throw setError(rc, tr("Getting NVRAM encryption settings failed (%Rhrc)"), rc);
16643
16644 Utf8Str strMachineFolder;
16645 i_calculateFullPath(".", strMachineFolder);
16646
16647 rc = i_changeEncryptionForComponent(task, strMachineFolder, "*.nvram",
16648 strNVRAMKeyStore, strNVRAMKeyId, CipherModeGcm);
16649 if (FAILED(rc))
16650 /* the helper function already sets error object */
16651 throw rc;
16652
16653 rc = mNvramStore->i_updateEncryptionSettings(strNVRAMKeyId, strNVRAMKeyStore);
16654 if (FAILED(rc))
16655 throw setError(rc, tr("Setting NVRAM encryption settings failed (%Rhrc)"), rc);
16656
16657 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of log files")).raw(), 1);
16658
16659 /* .log files */
16660 com::Utf8Str strLogFolder;
16661 i_getLogFolder(strLogFolder);
16662 rc = i_changeEncryptionForComponent(task, strLogFolder, "VBox.log*",
16663 mData->mstrLogKeyStore, mData->mstrLogKeyId, CipherModeCtr);
16664 if (FAILED(rc))
16665 /* the helper function already sets error object */
16666 throw rc;
16667
16668 task.m_pProgress->SetNextOperation(Bstr(tr("Change encryption of the config file")).raw(), 1);
16669
16670 i_saveSettings(NULL, alock, fSave);
16671 }
16672 catch (HRESULT aRC)
16673 {
16674 rc = aRC;
16675 mData->mstrKeyId = strOldKeyId;
16676 mData->mstrKeyStore = strOldKeyStore;
16677 }
16678
16679 task.m_pProgress->i_notifyComplete(rc);
16680
16681 LogFlowThisFuncLeave();
16682}
16683#endif /*!VBOX_WITH_FULL_VM_ENCRYPTION*/
16684
16685HRESULT Machine::changeEncryption(const com::Utf8Str &aCurrentPassword,
16686 const com::Utf8Str &aCipher,
16687 const com::Utf8Str &aNewPassword,
16688 const com::Utf8Str &aNewPasswordId,
16689 BOOL aForce,
16690 ComPtr<IProgress> &aProgress)
16691{
16692 LogFlowFuncEnter();
16693
16694#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16695 RT_NOREF(aCurrentPassword, aCipher, aNewPassword, aNewPasswordId, aForce, aProgress);
16696 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16697#else
16698 /* make the VM accessible */
16699 if (!mData->mAccessible)
16700 {
16701 if ( aCurrentPassword.isEmpty()
16702 || mData->mstrKeyId.isEmpty())
16703 return setError(E_ACCESSDENIED, tr("Machine is inaccessible"));
16704
16705 HRESULT rc = addEncryptionPassword(mData->mstrKeyId, aCurrentPassword);
16706 if (FAILED(rc))
16707 return rc;
16708 }
16709
16710 AutoLimitedCaller autoCaller(this);
16711 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16712
16713 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16714
16715 /* define mediums to be change encryption */
16716
16717 MediaList llMedia;
16718 for (MediumAttachmentList::iterator
16719 it = mMediumAttachments->begin();
16720 it != mMediumAttachments->end();
16721 ++it)
16722 {
16723 ComObjPtr<MediumAttachment> &pAttach = *it;
16724 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
16725
16726 if (!pMedium.isNull())
16727 {
16728 AutoCaller mac(pMedium);
16729 if (FAILED(mac.rc())) return mac.rc();
16730 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
16731 DeviceType_T devType = pMedium->i_getDeviceType();
16732 if (devType == DeviceType_HardDisk)
16733 {
16734 /*
16735 * We need to move to last child because the Medium::changeEncryption
16736 * encrypts all chain of specified medium with its parents.
16737 * Also we perform cheking of back reference and children for
16738 * all media in the chain to raise error before we start any action.
16739 * So, we first move into root parent and then we will move to last child
16740 * keeping latter in the list for encryption.
16741 */
16742
16743 /* move to root parent */
16744 ComObjPtr<Medium> pTmpMedium = pMedium;
16745 while (pTmpMedium.isNotNull())
16746 {
16747 AutoCaller mediumAC(pTmpMedium);
16748 if (FAILED(mediumAC.rc())) return mac.rc();
16749 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16750
16751 /* Cannot encrypt media which are attached to more than one virtual machine. */
16752 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16753 if (cBackRefs > 1)
16754 return setError(VBOX_E_INVALID_OBJECT_STATE,
16755 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16756 pTmpMedium->i_getName().c_str(), cBackRefs);
16757
16758 size_t cChildren = pTmpMedium->i_getChildren().size();
16759 if (cChildren > 1)
16760 return setError(VBOX_E_INVALID_OBJECT_STATE,
16761 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16762 pTmpMedium->i_getName().c_str(), cChildren);
16763
16764 pTmpMedium = pTmpMedium->i_getParent();
16765 }
16766 /* move to last child */
16767 pTmpMedium = pMedium;
16768 while (pTmpMedium.isNotNull() && pTmpMedium->i_getChildren().size() != 0)
16769 {
16770 AutoCaller mediumAC(pTmpMedium);
16771 if (FAILED(mediumAC.rc())) return mac.rc();
16772 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
16773
16774 /* Cannot encrypt media which are attached to more than one virtual machine. */
16775 size_t cBackRefs = pTmpMedium->i_getMachineBackRefCount();
16776 if (cBackRefs > 1)
16777 return setError(VBOX_E_INVALID_OBJECT_STATE,
16778 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines", "", cBackRefs),
16779 pTmpMedium->i_getName().c_str(), cBackRefs);
16780
16781 size_t cChildren = pTmpMedium->i_getChildren().size();
16782 if (cChildren > 1)
16783 return setError(VBOX_E_INVALID_OBJECT_STATE,
16784 tr("Cannot encrypt medium '%s' because it has %d children", "", cChildren),
16785 pTmpMedium->i_getName().c_str(), cChildren);
16786
16787 pTmpMedium = pTmpMedium->i_getChildren().front();
16788 }
16789 llMedia.push_back(pTmpMedium);
16790 }
16791 }
16792 }
16793
16794 ComObjPtr<Progress> pProgress;
16795 pProgress.createObject();
16796 HRESULT rc = pProgress->init(i_getVirtualBox(),
16797 static_cast<IMachine*>(this) /* aInitiator */,
16798 tr("Change encryption"),
16799 TRUE /* fCancellable */,
16800 (ULONG)(4 + + llMedia.size()), // cOperations
16801 tr("Change encryption of the mediuma"));
16802 if (FAILED(rc))
16803 return rc;
16804
16805 /* create and start the task on a separate thread (note that it will not
16806 * start working until we release alock) */
16807 ChangeEncryptionTask *pTask = new ChangeEncryptionTask(this, pProgress, "VM encryption",
16808 aCurrentPassword, aCipher, aNewPassword,
16809 aNewPasswordId, aForce, llMedia);
16810 rc = pTask->createThread();
16811 pTask = NULL;
16812 if (FAILED(rc))
16813 return rc;
16814
16815 pProgress.queryInterfaceTo(aProgress.asOutParam());
16816
16817 LogFlowFuncLeave();
16818
16819 return S_OK;
16820#endif
16821}
16822
16823HRESULT Machine::getEncryptionSettings(com::Utf8Str &aCipher,
16824 com::Utf8Str &aPasswordId)
16825{
16826#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16827 RT_NOREF(aCipher, aPasswordId);
16828 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16829#else
16830 AutoLimitedCaller autoCaller(this);
16831 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16832
16833 PCVBOXCRYPTOIF pCryptoIf = NULL;
16834 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16835 if (FAILED(hrc)) return hrc; /* Error is set */
16836
16837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16838
16839 if (mData->mstrKeyStore.isNotEmpty())
16840 {
16841 char *pszCipher = NULL;
16842 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), NULL /*pszPassword*/,
16843 NULL /*ppbKey*/, NULL /*pcbKey*/, &pszCipher);
16844 if (RT_SUCCESS(vrc))
16845 {
16846 aCipher = getCipherStringWithoutMode(pszCipher);
16847 RTStrFree(pszCipher);
16848 aPasswordId = mData->mstrKeyId;
16849 }
16850 else
16851 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
16852 tr("Failed to query the encryption settings with %Rrc"),
16853 vrc);
16854 }
16855 else
16856 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16857
16858 mParent->i_releaseCryptoIf(pCryptoIf);
16859
16860 return hrc;
16861#endif
16862}
16863
16864HRESULT Machine::checkEncryptionPassword(const com::Utf8Str &aPassword)
16865{
16866#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16867 RT_NOREF(aPassword);
16868 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16869#else
16870 AutoLimitedCaller autoCaller(this);
16871 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
16872
16873 PCVBOXCRYPTOIF pCryptoIf = NULL;
16874 HRESULT hrc = mParent->i_retainCryptoIf(&pCryptoIf);
16875 if (FAILED(hrc)) return hrc; /* Error is set */
16876
16877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
16878
16879 if (mData->mstrKeyStore.isNotEmpty())
16880 {
16881 char *pszCipher = NULL;
16882 uint8_t *pbDek = NULL;
16883 size_t cbDek = 0;
16884 int vrc = pCryptoIf->pfnCryptoKeyStoreGetDekFromEncoded(mData->mstrKeyStore.c_str(), aPassword.c_str(),
16885 &pbDek, &cbDek, &pszCipher);
16886 if (RT_SUCCESS(vrc))
16887 {
16888 RTStrFree(pszCipher);
16889 RTMemSaferFree(pbDek, cbDek);
16890 }
16891 else
16892 hrc = setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc,
16893 tr("The password supplied for the encrypted machine is incorrect"));
16894 }
16895 else
16896 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("This VM is not encrypted"));
16897
16898 mParent->i_releaseCryptoIf(pCryptoIf);
16899
16900 return hrc;
16901#endif
16902}
16903
16904HRESULT Machine::addEncryptionPassword(const com::Utf8Str &aId,
16905 const com::Utf8Str &aPassword)
16906{
16907#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16908 RT_NOREF(aId, 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 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16915
16916 size_t cbPassword = aPassword.length() + 1;
16917 uint8_t *pbPassword = (uint8_t *)aPassword.c_str();
16918
16919 mData->mpKeyStore->addSecretKey(aId, pbPassword, cbPassword);
16920
16921 if ( mData->mAccessible
16922 && mData->mSession.mState == SessionState_Locked
16923 && mData->mSession.mLockType == LockType_VM
16924 && mData->mSession.mDirectControl != NULL)
16925 {
16926 /* get the console from the direct session */
16927 ComPtr<IConsole> console;
16928 HRESULT rc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
16929 ComAssertComRC(rc);
16930 /* send passsword to console */
16931 console->AddEncryptionPassword(Bstr(aId).raw(),
16932 Bstr(aPassword).raw(),
16933 TRUE);
16934 }
16935
16936 if (mData->mstrKeyId == aId)
16937 {
16938 HRESULT hrc = checkEncryptionPassword(aPassword);
16939 if (FAILED(hrc))
16940 return hrc;
16941
16942 if (SUCCEEDED(hrc))
16943 {
16944 /*
16945 * Encryption is used and password is correct,
16946 * Reinit the machine if required.
16947 */
16948 BOOL fAccessible;
16949 alock.release();
16950 getAccessible(&fAccessible);
16951 alock.acquire();
16952 }
16953 }
16954
16955 /*
16956 * Add the password into the NvramStore only after
16957 * the machine becomes accessible and the NvramStore
16958 * contains key id and key store.
16959 */
16960 if (mNvramStore.isNotNull())
16961 mNvramStore->i_addPassword(aId, aPassword);
16962
16963 return S_OK;
16964#endif
16965}
16966
16967HRESULT Machine::addEncryptionPasswords(const std::vector<com::Utf8Str> &aIds,
16968 const std::vector<com::Utf8Str> &aPasswords)
16969{
16970#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16971 RT_NOREF(aIds, aPasswords);
16972 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16973#else
16974 if (aIds.size() != aPasswords.size())
16975 return setError(E_INVALIDARG, tr("Id and passwords arrays must have the same size"));
16976
16977 HRESULT hrc = S_OK;
16978 for (size_t i = 0; i < aIds.size() && SUCCEEDED(hrc); ++i)
16979 hrc = addEncryptionPassword(aIds[i], aPasswords[i]);
16980
16981 return hrc;
16982#endif
16983}
16984
16985HRESULT Machine::removeEncryptionPassword(AutoCaller &autoCaller, const com::Utf8Str &aId)
16986{
16987#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
16988 RT_NOREF(autoCaller, aId);
16989 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
16990#else
16991 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
16992
16993 if ( mData->mAccessible
16994 && mData->mSession.mState == SessionState_Locked
16995 && mData->mSession.mLockType == LockType_VM
16996 && mData->mSession.mDirectControl != NULL)
16997 {
16998 /* get the console from the direct session */
16999 ComPtr<IConsole> console;
17000 HRESULT rc = mData->mSession.mDirectControl->COMGETTER(RemoteConsole)(console.asOutParam());
17001 ComAssertComRC(rc);
17002 /* send passsword to console */
17003 console->RemoveEncryptionPassword(Bstr(aId).raw());
17004 }
17005
17006 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty() && mData->mstrKeyId == aId)
17007 {
17008 if (Global::IsOnlineOrTransient(mData->mMachineState))
17009 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
17010 alock.release();
17011 autoCaller.release();
17012 /* return because all passwords are purged when machine becomes inaccessible; */
17013 return i_setInaccessible();
17014 }
17015
17016 if (mNvramStore.isNotNull())
17017 mNvramStore->i_removePassword(aId);
17018 mData->mpKeyStore->deleteSecretKey(aId);
17019 return S_OK;
17020#endif
17021}
17022
17023HRESULT Machine::clearAllEncryptionPasswords(AutoCaller &autoCaller)
17024{
17025#ifndef VBOX_WITH_FULL_VM_ENCRYPTION
17026 RT_NOREF(autoCaller);
17027 return setError(VBOX_E_NOT_SUPPORTED, tr("Full VM encryption is not available with this build"));
17028#else
17029 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
17030
17031 if (mData->mAccessible && mData->mstrKeyStore.isNotEmpty())
17032 {
17033 if (Global::IsOnlineOrTransient(mData->mMachineState))
17034 return setError(VBOX_E_INVALID_VM_STATE, tr("The machine is in online or transient state"));
17035 alock.release();
17036 autoCaller.release();
17037 /* return because all passwords are purged when machine becomes inaccessible; */
17038 return i_setInaccessible();
17039 }
17040
17041 mNvramStore->i_removeAllPasswords();
17042 mData->mpKeyStore->deleteAllSecretKeys(false, true);
17043 return S_OK;
17044#endif
17045}
17046
17047#ifdef VBOX_WITH_FULL_VM_ENCRYPTION
17048HRESULT Machine::i_setInaccessible()
17049{
17050 if (!mData->mAccessible)
17051 return S_OK;
17052
17053 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
17054 VirtualBox *pParent = mParent;
17055 com::Utf8Str strConfigFile = mData->m_strConfigFile;
17056 Guid id(i_getId());
17057
17058 alock.release();
17059
17060 uninit();
17061 HRESULT rc = initFromSettings(pParent, strConfigFile, &id, com::Utf8Str());
17062
17063 alock.acquire();
17064 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
17065 return rc;
17066}
17067#endif
17068
17069/* This isn't handled entirely by the wrapper generator yet. */
17070#ifdef VBOX_WITH_XPCOM
17071NS_DECL_CLASSINFO(SessionMachine)
17072NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
17073
17074NS_DECL_CLASSINFO(SnapshotMachine)
17075NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
17076#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